Maison > Article > développement back-end > Invites imbriquées dans Go à l'aide de promptui
Je travaillais récemment sur un outil CLI écrit en Go, en utilisant l'outil Cobra, et j'ai eu un cas d'utilisation où j'avais besoin d'une invite imbriquée pour l'une des commandes. J'utilisais promptui pour les invites et je n'ai pas trouvé de moyen simple de le faire. Ce court article montrera comment créer une invite imbriquée à l'aide de promptui. Le code complété peut être trouvé ici.
Nous devons d’abord créer un projet Go vide. Nous l'appellerons une invite imbriquée :
$ mkdir nested-prompt && cd nested-prompt $ go mod init github.com/Thwani47/nested-prompt
Nous installerons ensuite les packages cobra, cobra-cli et promptui :
$ go get -u github.com/spf13/cobra@latest $ go install github.com/spf13/cobra-cli@latest $ go get -u github.com/manifoldco/promptui
Nous pouvons initialiser une nouvelle application CLI en utilisant le cobra-cli et ajouter une commande à notre CLI
$ cobra-cli init # initializes a new CLI application $ cobra-cli add config # adds a new command to the CLI named 'config'
Nous pouvons nettoyer le fichier cmd/config.go et supprimer tous les commentaires. Ça devrait être comme ça :
// cmd/config.go package cmd import ( "fmt" "github.com/spf13/cobra" ) var configCmd = &cobra.Command{ Use: "config", Short: "Configure settings for the application", Long: `Configure settings for the application`, Run: func(cmd *cobra.Command, args []string) { fmt.Println("config called") }, } func init() { rootCmd.AddCommand(configCmd) }
Nous devons d’abord créer un type personnalisé pour notre invite. Nous faisons cela en définissant une structure promptItem comme suit
type PromptType int const ( TextPrompt PromptType = 0 PasswordPrompt PromptType = 1 SelectPrompt PromptType = 2 ) type promptItem struct { ID string Label string Value string SelectOptions []string promptType PromptType }
L'énumération PromptType nous permet de collecter différents types d'entrées à partir de nos invites, nous pouvons demander à l'utilisateur du texte ou des valeurs sensibles telles que des mots de passe ou des clés API, ou inviter l'utilisateur à sélectionner dans une liste de valeurs définies
Nous définissons ensuite une fonction promptInput qui demandera une saisie à l'utilisateur. La fonction renvoie la valeur de chaîne saisie par l'utilisateur ou une erreur si l'invite échoue.
func promptInput(item promptItem) (string, error) { prompt := promptui.Prompt{ Label: item.Label, HideEntered: true, } if item.promptType == PasswordPrompt { prompt.Mask = '*' } res, err := prompt.Run() if err != nil { fmt.Printf("Prompt failed %v\n", err) return "", err } return res, nil }
Nous définissons ensuite une fonction promptSelect qui permettra à l'utilisateur de sélectionner parmi une liste d'options. La fonction renvoie la valeur de chaîne sélectionnée par l'utilisateur ou une erreur si l'invite échoue.
func promptSelect(item selectItem) (string, error) { prompt := promptui.Select{ Label: item.Label, Items: item.SelectValues, HideSelected: true, } _, result, err := prompt.Run() if err != nil { fmt.Printf("Prompt failed %v\n", err) return "", err } return result, nil }
Pour simuler une invite imbriquée, nous allons créer une fonction promptNested qui nous permettra de demander à l'utilisateur une valeur et l'invite restera active jusqu'à ce que l'utilisateur sélectionne "Terminé". La fonction renvoie une valeur booléenne qui indique que l'invite a réussi.
Les commentaires dans la fonction expliquent de quoi chaque bloc majeur de code est responsable
func promptNested(promptLabel string, startingIndex int, items []*promptItem) bool { // Add a "Done" option to the prompt if it does not exist doneID := "Done" if len(items) > 0 && items[0].ID != doneID { items = append([]*promptItem{{ID: doneID, Label: "Done"}}, items...) } templates := &promptui.SelectTemplates{ Label: "{{ . }}?", Active: "\U0001F336 {{ .Label | cyan }}", Inactive: "{{ .Label | cyan }}", Selected: "\U0001F336 {{ .Label | red | cyan }}", } prompt := promptui.Select{ Label: promptLabel, Items: items, Templates: templates, Size: 3, HideSelected: true, CursorPos: startingIndex, // Set the cursor to the last selected item } idx, _, err := prompt.Run() if err != nil { fmt.Printf("Error occurred when running prompt: %v\n", err) return false } selectedItem := items[idx] // if the user selects "Done", return true and exit from the function if selectedItem.ID == doneID { return true } var promptResponse string // if the prompt type is Text or Password, prompt the user for input if selectedItem.promptType == TextPrompt || selectedItem.promptType == PasswordPrompt { promptResponse, err = promptInput(*selectedItem) if err != nil { fmt.Printf("Error occurred when running prompt: %v\n", err) return false } items[idx].Value = promptResponse } // if the prompt type is Select, prompt the user to select from a list of options if selectedItem.promptType == SelectPrompt { promptResponse, err = promptSelect(*selectedItem) if err != nil { fmt.Printf("Error occurred when running prompt: %v\n", err) return false } items[idx].Value = promptResponse } if err != nil { fmt.Printf("Error occurred when running prompt: %v\n", err) return false } // recursively call the promptNested function to allow the user to select another option return promptNested(idx, items) }
Maintenant, nous avons toutes les méthodes dont nous avons besoin et nous devons les tester. Dans la fonction Run de la commande configCmd, nous allons créer une liste de promptItem et appeler la fonction promptNested pour demander à l'utilisateur une saisie. La fonction Exécuter devrait ressembler à ceci :
// create a list of prompt items items := []*promptItem{ { ID: "APIKey", Label: "API Key", promptType: PasswordPrompt, }, { ID: "Theme", Label: "Theme", promptType: SelectPrompt, SelectOptions: []string{"Dark", "Light"}, }, { ID: "Language", Label: "Preferred Language", promptType: SelectPrompt, SelectOptions: []string{"English", "Spanish", "French", "German", "Chinese", "Japanese"}, }, } // set the starting index to 0 to start at the first item in the list promptNested("Configuration Items", 0, items) for _, v := range items { fmt.Printf("Saving configuration (%s) with value (%s)...\n", v.ID, v.Value) }
Construisez et testez l'application comme suit
$ go build . $ ./nested-prompt config
Le résultat est le suivant
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!