This repository has been archived by the owner on Apr 19, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 358
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #30 from coryb/confirm
add Confirm prompt
- Loading branch information
Showing
3 changed files
with
235 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |