confirm.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. package survey
  2. import (
  3. "fmt"
  4. "os"
  5. "regexp"
  6. "github.com/kataras/survey/core"
  7. "github.com/kataras/survey/terminal"
  8. )
  9. // Confirm is a regular text input that accept yes/no answers. Response type is a bool.
  10. type Confirm struct {
  11. core.Renderer
  12. Message string
  13. Default bool
  14. Help string
  15. }
  16. // data available to the templates when processing
  17. type ConfirmTemplateData struct {
  18. Confirm
  19. Answer string
  20. ShowHelp bool
  21. }
  22. // Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
  23. var ConfirmQuestionTemplate = `
  24. {{- if .ShowHelp }}{{- color "cyan"}}{{ HelpIcon }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
  25. {{- color "green+hb"}}{{ QuestionIcon }} {{color "reset"}}
  26. {{- color "default+hb"}}{{ .Message }} {{color "reset"}}
  27. {{- if .Answer}}
  28. {{- color "cyan"}}{{.Answer}}{{color "reset"}}{{"\n"}}
  29. {{- else }}
  30. {{- if and .Help (not .ShowHelp)}}{{color "cyan"}}[{{ HelpInputRune }} for help]{{color "reset"}} {{end}}
  31. {{- color "white"}}{{if .Default}}(Y/n) {{else}}(y/N) {{end}}{{color "reset"}}
  32. {{- end}}`
  33. // the regex for answers
  34. var (
  35. yesRx = regexp.MustCompile("^(?i:y(?:es)?)$")
  36. noRx = regexp.MustCompile("^(?i:n(?:o)?)$")
  37. )
  38. func yesNo(t bool) string {
  39. if t {
  40. return "Yes"
  41. }
  42. return "No"
  43. }
  44. func (c *Confirm) getBool(showHelp bool) (bool, error) {
  45. rr := terminal.NewRuneReader(os.Stdin)
  46. rr.SetTermMode()
  47. defer rr.RestoreTermMode()
  48. // start waiting for input
  49. for {
  50. line, err := rr.ReadLine(0)
  51. if err != nil {
  52. return false, err
  53. }
  54. // move back up a line to compensate for the \n echoed from terminal
  55. terminal.CursorPreviousLine(1)
  56. val := string(line)
  57. // get the answer that matches the
  58. var answer bool
  59. switch {
  60. case yesRx.Match([]byte(val)):
  61. answer = true
  62. case noRx.Match([]byte(val)):
  63. answer = false
  64. case val == "":
  65. answer = c.Default
  66. case val == string(core.HelpInputRune) && c.Help != "":
  67. err := c.Render(
  68. ConfirmQuestionTemplate,
  69. ConfirmTemplateData{Confirm: *c, ShowHelp: true},
  70. )
  71. if err != nil {
  72. // use the default value and bubble up
  73. return c.Default, err
  74. }
  75. showHelp = true
  76. continue
  77. default:
  78. // we didnt get a valid answer, so print error and prompt again
  79. if err := c.Error(fmt.Errorf("%q is not a valid answer, please try again.", val)); err != nil {
  80. return c.Default, err
  81. }
  82. err := c.Render(
  83. ConfirmQuestionTemplate,
  84. ConfirmTemplateData{Confirm: *c, ShowHelp: showHelp},
  85. )
  86. if err != nil {
  87. // use the default value and bubble up
  88. return c.Default, err
  89. }
  90. continue
  91. }
  92. return answer, nil
  93. }
  94. // should not get here
  95. return c.Default, nil
  96. }
  97. /*
  98. Prompt prompts the user with a simple text field and expects a reply followed
  99. by a carriage return.
  100. likesPie := false
  101. prompt := &survey.Confirm{ Message: "What is your name?" }
  102. survey.AskOne(prompt, &likesPie, nil)
  103. */
  104. func (c *Confirm) Prompt() (interface{}, error) {
  105. // render the question template
  106. err := c.Render(
  107. ConfirmQuestionTemplate,
  108. ConfirmTemplateData{Confirm: *c},
  109. )
  110. if err != nil {
  111. return "", err
  112. }
  113. // get input and return
  114. return c.getBool(false)
  115. }
  116. // Cleanup overwrite the line with the finalized formatted version
  117. func (c *Confirm) Cleanup(val interface{}) error {
  118. // if the value was previously true
  119. ans := yesNo(val.(bool))
  120. // render the template
  121. return c.Render(
  122. ConfirmQuestionTemplate,
  123. ConfirmTemplateData{Confirm: *c, Answer: ans},
  124. )
  125. }