File: confirm.go

package info (click to toggle)
golang-github-alecaivazis-survey 2.3.7-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 652 kB
  • sloc: makefile: 12
file content (154 lines) | stat: -rw-r--r-- 3,704 bytes parent folder | download
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package survey

import (
	"fmt"
	"regexp"
)

// Confirm is a regular text input that accept yes/no answers. Response type is a bool.
type Confirm struct {
	Renderer
	Message string
	Default bool
	Help    string
}

// data available to the templates when processing
type ConfirmTemplateData struct {
	Confirm
	Answer   string
	ShowHelp bool
	Config   *PromptConfig
}

// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
var ConfirmQuestionTemplate = `
{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}}
{{- color "default+hb"}}{{ .Message }} {{color "reset"}}
{{- if .Answer}}
  {{- color "cyan"}}{{.Answer}}{{color "reset"}}{{"\n"}}
{{- else }}
  {{- if and .Help (not .ShowHelp)}}{{color "cyan"}}[{{ .Config.HelpInput }} for help]{{color "reset"}} {{end}}
  {{- color "white"}}{{if .Default}}(Y/n) {{else}}(y/N) {{end}}{{color "reset"}}
{{- end}}`

// the regex for answers
var (
	yesRx = regexp.MustCompile("^(?i:y(?:es)?)$")
	noRx  = regexp.MustCompile("^(?i:n(?:o)?)$")
)

func yesNo(t bool) string {
	if t {
		return "Yes"
	}
	return "No"
}

func (c *Confirm) getBool(showHelp bool, config *PromptConfig) (bool, error) {
	cursor := c.NewCursor()
	rr := c.NewRuneReader()
	_ = rr.SetTermMode()
	defer func() {
		_ = rr.RestoreTermMode()
	}()

	// start waiting for input
	for {
		line, err := rr.ReadLine(0)
		if err != nil {
			return false, err
		}
		// move back up a line to compensate for the \n echoed from terminal
		cursor.PreviousLine(1)
		val := string(line)

		// get the answer that matches the
		var answer bool
		switch {
		case yesRx.Match([]byte(val)):
			answer = true
		case noRx.Match([]byte(val)):
			answer = false
		case val == "":
			answer = c.Default
		case val == config.HelpInput && c.Help != "":
			err := c.Render(
				ConfirmQuestionTemplate,
				ConfirmTemplateData{
					Confirm:  *c,
					ShowHelp: true,
					Config:   config,
				},
			)
			if err != nil {
				// use the default value and bubble up
				return c.Default, err
			}
			showHelp = true
			continue
		default:
			// we didnt get a valid answer, so print error and prompt again
			//lint:ignore ST1005 it should be fine for this error message to have punctuation
			if err := c.Error(config, fmt.Errorf("%q is not a valid answer, please try again.", val)); err != nil {
				return c.Default, err
			}
			err := c.Render(
				ConfirmQuestionTemplate,
				ConfirmTemplateData{
					Confirm:  *c,
					ShowHelp: showHelp,
					Config:   config,
				},
			)
			if err != nil {
				// use the default value and bubble up
				return c.Default, err
			}
			continue
		}
		return answer, nil
	}
}

/*
Prompt prompts the user with a simple text field and expects a reply followed
by a carriage return.

	likesPie := false
	prompt := &survey.Confirm{ Message: "What is your name?" }
	survey.AskOne(prompt, &likesPie)
*/
func (c *Confirm) Prompt(config *PromptConfig) (interface{}, error) {
	// render the question template
	err := c.Render(
		ConfirmQuestionTemplate,
		ConfirmTemplateData{
			Confirm: *c,
			Config:  config,
		},
	)
	if err != nil {
		return "", err
	}

	// get input and return
	return c.getBool(false, config)
}

// Cleanup overwrite the line with the finalized formatted version
func (c *Confirm) Cleanup(config *PromptConfig, val interface{}) error {
	// if the value was previously true
	ans := yesNo(val.(bool))

	// render the template
	return c.Render(
		ConfirmQuestionTemplate,
		ConfirmTemplateData{
			Confirm: *c,
			Answer:  ans,
			Config:  config,
		},
	)
}