私は最近、Cobra ツールを使用して Go で書かれた CLI ツールに取り組んでいたのですが、コマンドの 1 つに対してネストされたプロンプトが必要になるユースケースがありました。プロンプトにprompuiを使用していましたが、これを行う簡単な方法が見つかりませんでした。この短い投稿では、prompui を使用してネストされたプロンプトを作成する方法を説明します。完成したコードはここにあります。
最初に空の Go プロジェクトを作成する必要があります。これをネストされたプロンプトと呼びます:
$ mkdir nested-prompt && cd nested-prompt $ go mod init github.com/Thwani47/nested-prompt
次に、cobra、cobra-cli、prompui パッケージをインストールします。
$ go get -u github.com/spf13/cobra@latest $ go install github.com/spf13/cobra-cli@latest $ go get -u github.com/manifoldco/promptui
cobra-cli を使用して新しい CLI アプリケーションを初期化し、CLI にコマンドを追加できます
$ cobra-cli init # initializes a new CLI application $ cobra-cli add config # adds a new command to the CLI named 'config'
cmd/config.go ファイルをクリーンアップして、すべてのコメントを削除できます。次のようになります:
// 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) }
まず、プロンプトのカスタム タイプを作成する必要があります。これを行うには、次のように、promptItem 構造体を定義します
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 }
PromptType 列挙型を使用すると、プロンプトからさまざまなタイプの入力を収集できます。ユーザーにテキスト、またはパスワードや API キーなどの機密値の入力を求めるプロンプトを表示したり、定義された値のリストから選択するようユーザーに求めたりすることができます
次に、ユーザーからの入力を求めるプロンプトを表示する、promptInput 関数を定義します。この関数は、ユーザーが入力した文字列値を返すか、プロンプトが失敗した場合はエラーを返します。
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 }
次に、ユーザーがオプションのリストから選択できるようにするプロンプト選択関数を定義します。この関数は、ユーザーが選択した文字列値を返すか、プロンプトが失敗した場合はエラーを返します。
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 }
ネストされたプロンプトをシミュレートするには、promptNested 関数を作成します。この関数を使用すると、ユーザーに値の入力を求めることができ、ユーザーが「完了」を選択するまでプロンプトはアクティブなままになります。この関数は、プロンプトが成功したことを示すブール値を返します。
関数内のコメントは、コードの各主要ブロックが何を担当するかを説明しています
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) }
これで、必要なメソッドがすべて揃ったので、それらをテストする必要があります。 configCmd コマンドの Run 関数内で、promptItem のリストを作成し、promptNested 関数を呼び出してユーザーに入力を求めます。 Run 関数は次のようになります:
// 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) }
次のようにアプリケーションを構築してテストします
$ go build . $ ./nested-prompt config
結果は以下の通りです
以上がPrompui を使用した Go のネストされたプロンプトの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。