Skip to content
This repository has been archived by the owner on Apr 19, 2024. It is now read-only.

Commit

Permalink
Merge pull request #30 from coryb/confirm
Browse files Browse the repository at this point in the history
add Confirm prompt
  • Loading branch information
AlecAivazis authored Apr 4, 2017
2 parents 2100c03 + f45dc19 commit 3d4e271
Show file tree
Hide file tree
Showing 3 changed files with 235 additions and 0 deletions.
135 changes: 135 additions & 0 deletions confirm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package survey

import (
"bufio"
"fmt"
"os"
"regexp"

tm "github.com/buger/goterm"
)

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

// data available to the templates when processing
type ConfirmTemplateData struct {
Confirm
Answer string
}

// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
var ConfirmQuestionTemplate = `
{{- color "green+hb"}}? {{color "reset"}}
{{- color "default+hb"}}{{ .Message }} {{color "reset"}}
{{- if .Answer}}
{{- color "cyan"}}{{.Answer}}{{color "reset"}}
{{- else }}
{{- color "white"}}{{if .Default}}(Y/n) {{else}}(y/N) {{end}}{{color "reset"}}
{{- end}}`

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

// Prompt prompts the user with a simple text field and expects a reply followed
// by a carriage return.
func (confirm *Confirm) Prompt() (string, error) {
if confirm.Answer == nil {
answer := false
confirm.Answer = &answer
}
out, err := runTemplate(
ConfirmQuestionTemplate,
ConfirmTemplateData{Confirm: *confirm},
)
if err != nil {
return "", err
}

// print the question we were given to kick off the prompt
fmt.Print(out)

// a scanner to look at the input from stdin
scanner := bufio.NewScanner(os.Stdin)
// wait for a response
yesRx := regexp.MustCompile("^(?i:y(?:es)?)$")
noRx := regexp.MustCompile("^(?i:n(?:o)?)$")
answer := confirm.Default
for scanner.Scan() {
// get the availible text in the scanner
res := scanner.Text()
// if there is no answer
if res == "" {
// use the default
break
}
// is answer yes?
if yesRx.Match([]byte(res)) {
answer = true
break
}

// is answer "no"
if noRx.Match([]byte(res)) {
answer = false
break
}

// we didnt get a valid answer, so print error and prompt again
out, err := runTemplate(ErrorTemplate, fmt.Errorf("%q is not a valid answer, try again", res))
if err != nil {
return "", err
}
// send the message to the user
fmt.Print(out)
return confirm.Prompt()
}

// return the value
*confirm.Answer = answer
return yesNo(answer), nil
}

// Cleanup overwrite the line with the finalized formatted version
func (confirm *Confirm) Cleanup(val string) error {
// get the current cursor location
loc, err := CursorLocation()
// if something went wrong
if err != nil {
// bubble
return err
}

var initLoc int
// if we are printing at the end of the console
if loc.col == tm.Height() {
initLoc = loc.col - 2
} else {
initLoc = loc.col - 1
}

// move to the beginning of the current line
tm.MoveCursor(initLoc, 1)

out, err := runTemplate(
ConfirmQuestionTemplate,
ConfirmTemplateData{Confirm: *confirm, Answer: val},
)
if err != nil {
return err
}

tm.Print(out, AnsiClearLine)
tm.Flush()

// nothing went wrong
return nil
}
74 changes: 74 additions & 0 deletions confirm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package survey

import "testing"

func init() {
// disable color output for all prompts to simplify testing
DisableColor = true
}

func TestConfirmFormatQuestion(t *testing.T) {

prompt := &Confirm{
Message: "Is pizza your favorite food?",
Default: true,
}

actual, err := runTemplate(
ConfirmQuestionTemplate,
ConfirmTemplateData{Confirm: *prompt},
)
if err != nil {
t.Errorf("Failed to run template to format input question: %s", err)
}

expected := `? Is pizza your favorite food? (Y/n) `

if actual != expected {
t.Errorf("Formatted input question was not formatted correctly. Found:\n%s\nExpected:\n%s", actual, expected)
}
}

func TestConfirmFormatQuestionDefaultFalse(t *testing.T) {

prompt := &Confirm{
Message: "Is pizza your favorite food?",
Default: false,
}

actual, err := runTemplate(
ConfirmQuestionTemplate,
ConfirmTemplateData{Confirm: *prompt},
)
if err != nil {
t.Errorf("Failed to run template to format input answer: %s", err)
}

expected := `? Is pizza your favorite food? (y/N) `

if actual != expected {
t.Errorf("Formatted input answer was not formatted correctly. Found:\n%s\nExpected:\n%s", actual, expected)
}
}

func TestConfirmFormatAnswer(t *testing.T) {

// default false
prompt := &Confirm{
Message: "Is pizza your favorite food?",
}

actual, err := runTemplate(
ConfirmQuestionTemplate,
ConfirmTemplateData{Confirm: *prompt, Answer: "Yes"},
)
if err != nil {
t.Errorf("Failed to run template to format input answer: %s", err)
}

expected := `? Is pizza your favorite food? Yes`

if actual != expected {
t.Errorf("Formatted input answer was not formatted correctly. Found:\n%s\nExpected:\n%s", actual, expected)
}
}
26 changes: 26 additions & 0 deletions examples/confirm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package main

import (
"fmt"

"github.com/alecaivazis/survey"
)

func main() {
var happy bool
prompt := &survey.Confirm{
Message: "Are you happy?",
Default: true,
Answer: &happy,
}

answer, err := survey.AskOne(prompt)

if err != nil {
fmt.Println(err.Error())
return
}

fmt.Printf("response string: %s\n", answer)
fmt.Printf("response happy: %t\n", happy)
}

0 comments on commit 3d4e271

Please sign in to comment.