ホームページ  >  記事  >  バックエンド開発  >  Prompui を使用した Go のネストされたプロンプト

Prompui を使用した Go のネストされたプロンプト

王林
王林オリジナル
2024-07-17 20:22:421149ブラウズ

私は最近、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

結果は以下の通りです
Nested Prompts in Go using promptui

以上がPrompui を使用した Go のネストされたプロンプトの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。