Maison  >  Article  >  développement back-end  >  Invites imbriquées dans Go à l'aide de promptui

Invites imbriquées dans Go à l'aide de promptui

王林
王林original
2024-07-17 20:22:421149parcourir

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
Nested Prompts in Go using promptui

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!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn