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