prompt.go

  1package input
  2
  3import (
  4	"bufio"
  5	"fmt"
  6	"os"
  7	"strconv"
  8	"strings"
  9	"syscall"
 10
 11	"golang.org/x/crypto/ssh/terminal"
 12
 13	"github.com/MichaelMure/git-bug/util/interrupt"
 14)
 15
 16// PromptValidator is a validator for a user entry
 17type PromptValidator func(name string, value string) (complaint string, err error)
 18
 19// Required is a validator preventing a "" value
 20func Required(name string, value string) (string, error) {
 21	if value == "" {
 22		return fmt.Sprintf("%s is empty", name), nil
 23	}
 24	return "", nil
 25}
 26
 27func Prompt(prompt, name string, validators ...PromptValidator) (string, error) {
 28	return PromptDefault(prompt, name, "", validators...)
 29}
 30
 31func PromptDefault(prompt, name, preValue string, validators ...PromptValidator) (string, error) {
 32	for {
 33		if preValue != "" {
 34			_, _ = fmt.Fprintf(os.Stderr, "%s [%s]: ", prompt, preValue)
 35		} else {
 36			_, _ = fmt.Fprintf(os.Stderr, "%s: ", prompt)
 37		}
 38
 39		line, err := bufio.NewReader(os.Stdin).ReadString('\n')
 40		if err != nil {
 41			return "", err
 42		}
 43
 44		line = strings.TrimSpace(line)
 45
 46		if preValue != "" && line == "" {
 47			line = preValue
 48		}
 49
 50		for _, validator := range validators {
 51			complaint, err := validator(name, line)
 52			if err != nil {
 53				return "", err
 54			}
 55			if complaint != "" {
 56				_, _ = fmt.Fprintln(os.Stderr, complaint)
 57				continue
 58			}
 59		}
 60
 61		return line, nil
 62	}
 63}
 64
 65func PromptPassword(prompt, name string, validators ...PromptValidator) (string, error) {
 66	termState, err := terminal.GetState(syscall.Stdin)
 67	if err != nil {
 68		return "", err
 69	}
 70
 71	cancel := interrupt.RegisterCleaner(func() error {
 72		return terminal.Restore(syscall.Stdin, termState)
 73	})
 74	defer cancel()
 75
 76	for {
 77		_, _ = fmt.Fprintf(os.Stderr, "%s: ", prompt)
 78
 79		bytePassword, err := terminal.ReadPassword(syscall.Stdin)
 80		// new line for coherent formatting, ReadPassword clip the normal new line
 81		// entered by the user
 82		fmt.Println()
 83
 84		if err != nil {
 85			return "", err
 86		}
 87
 88		pass := string(bytePassword)
 89
 90		for _, validator := range validators {
 91			complaint, err := validator(name, pass)
 92			if err != nil {
 93				return "", err
 94			}
 95			if complaint != "" {
 96				_, _ = fmt.Fprintln(os.Stderr, complaint)
 97				continue
 98			}
 99		}
100
101		return pass, nil
102	}
103}
104
105func PromptChoice(prompt string, choices []string) (int, error) {
106	for {
107		for i, choice := range choices {
108			_, _ = fmt.Fprintf(os.Stderr, "[%d]: %s\n", i+1, choice)
109		}
110		_, _ = fmt.Fprintf(os.Stderr, "%s: ", prompt)
111
112		line, err := bufio.NewReader(os.Stdin).ReadString('\n')
113		fmt.Println()
114		if err != nil {
115			return 0, err
116		}
117
118		line = strings.TrimSpace(line)
119
120		index, err := strconv.Atoi(line)
121		if err != nil || index < 1 || index > len(choices) {
122			fmt.Println("invalid input")
123			continue
124		}
125
126		return index, nil
127	}
128}