forked from Songmu/prompter
/
prompter.go
126 lines (114 loc) · 2.67 KB
/
prompter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Package prompter is utility for easy prompting
package prompter
import (
"bufio"
"fmt"
"os"
"regexp"
"strings"
"github.com/mattn/go-isatty"
"golang.org/x/crypto/ssh/terminal"
)
// VERSION version of prompter
const VERSION = "0.1.0"
// Prompter is object for prompting
type Prompter struct {
Message string
// choices of answer
Choices []string
IgnoreCase bool
Default string
// specify answer pattern by regexp. When both Choices and Regexp are specified, Regexp takes a priority.
Regexp *regexp.Regexp
// for passwords and so on.
NoEcho bool
UseDefault bool
reg *regexp.Regexp
}
// Prompt displays a prompt and returns answer
func (p *Prompter) Prompt() string {
fmt.Print(p.msg())
if p.UseDefault || skip() {
return p.Default
}
input := ""
if p.NoEcho {
b, err := terminal.ReadPassword(int(os.Stdin.Fd()))
if err == nil {
input = string(b)
}
fmt.Print("\n")
} else {
scanner := bufio.NewScanner(os.Stdin)
ok := scanner.Scan()
if ok {
input = strings.TrimRight(scanner.Text(), "\r\n")
}
}
if input == "" {
input = p.Default
}
if !p.inputIsValid(input) {
fmt.Println(p.errorMsg())
return p.Prompt()
}
return input
}
func skip() bool {
if os.Getenv("GO_PROMPTER_USE_DEFAULT") != "" {
return true
}
return !isatty.IsTerminal(os.Stdin.Fd()) || !isatty.IsTerminal(os.Stdout.Fd())
}
func (p *Prompter) msg() string {
msg := p.Message
if p.Choices != nil && len(p.Choices) > 0 {
msg += fmt.Sprintf(" (%s)", strings.Join(p.Choices, "/"))
}
if p.Default != "" {
msg += " [" + p.Default + "]"
}
return msg + ": "
}
func (p *Prompter) errorMsg() string {
if p.Regexp != nil {
return fmt.Sprintf("# Answer should match /%s/", p.Regexp)
}
if p.Choices != nil && len(p.Choices) > 0 {
if len(p.Choices) == 1 {
return fmt.Sprintf("# Enter `%s`", p.Choices[0])
}
choices := make([]string, len(p.Choices)-1)
for i, v := range p.Choices[:len(p.Choices)-1] {
choices[i] = "`" + v + "`"
}
return fmt.Sprintf("# Enter %s or `%s`", strings.Join(choices, ", "), p.Choices[len(p.Choices)-1])
}
return ""
}
func (p *Prompter) inputIsValid(input string) bool {
return p.regexp().MatchString(input)
}
var allReg = regexp.MustCompile(`.*`)
func (p *Prompter) regexp() *regexp.Regexp {
if p.Regexp != nil {
return p.Regexp
}
if p.reg != nil {
return p.reg
}
if p.Choices == nil || len(p.Choices) == 0 {
p.reg = allReg
return p.reg
}
choices := make([]string, len(p.Choices))
for i, v := range p.Choices {
choices[i] = regexp.QuoteMeta(v)
}
ignoreReg := ""
if p.IgnoreCase {
ignoreReg = "(?i)"
}
p.reg = regexp.MustCompile(fmt.Sprintf(`%s\A(?:%s)\z`, ignoreReg, strings.Join(choices, "|")))
return p.reg
}