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, }, ) }