pax_global_header 0000666 0000000 0000000 00000000064 14344402622 0014513 g ustar 00root root 0000000 0000000 52 comment=fa37277e6394c29db7bcc94062cb30cd7785a126
survey-2.3.7/ 0000775 0000000 0000000 00000000000 14344402622 0013061 5 ustar 00root root 0000000 0000000 survey-2.3.7/.github/ 0000775 0000000 0000000 00000000000 14344402622 0014421 5 ustar 00root root 0000000 0000000 survey-2.3.7/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 14344402622 0016604 5 ustar 00root root 0000000 0000000 survey-2.3.7/.github/ISSUE_TEMPLATE/bug.md 0000664 0000000 0000000 00000000411 14344402622 0017677 0 ustar 00root root 0000000 0000000 ---
name: Bug report
about: Create a report to help us improve
title: ''
labels: 'Bug'
assignees: ''
---
**What operating system and terminal are you using?**
**An example that showcases the bug.**
**What did you expect to see?**
**What did you see instead?**
survey-2.3.7/.github/ISSUE_TEMPLATE/question.md 0000664 0000000 0000000 00000000201 14344402622 0020766 0 ustar 00root root 0000000 0000000 ---
name: Ask for help
about: Suggest an idea for this project or ask for help
title: ''
labels: 'Question'
assignees: ''
---
survey-2.3.7/.github/matchers.json 0000664 0000000 0000000 00000000630 14344402622 0017121 0 ustar 00root root 0000000 0000000 {
"problemMatcher": [
{
"owner": "go",
"pattern": [
{
"regexp": "^\\s*(.+\\.go):(\\d+):(?:(\\d+):)? (?:(warning): )?(.*)",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
]
}
]
}
survey-2.3.7/.github/workflows/ 0000775 0000000 0000000 00000000000 14344402622 0016456 5 ustar 00root root 0000000 0000000 survey-2.3.7/.github/workflows/test.yml 0000664 0000000 0000000 00000001210 14344402622 0020152 0 ustar 00root root 0000000 0000000 name: Test
on: [push, pull_request]
jobs:
test:
strategy:
fail-fast: false
matrix:
platform: [ubuntu-latest, macos-latest, windows-latest]
go: ["1.19", "1.18", "1.17", "1.16"]
runs-on: ${{ matrix.platform }}
steps:
- name: Check out code
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: "${{ matrix.go }}"
cache: true
- name: Add problem matcher
run: echo "::add-matcher::$PWD/.github/matchers.json"
- name: Run tests
run: go test -timeout 30s -v $(go list -v ./... | grep -vE '(tests|examples)$')
survey-2.3.7/CONTRIBUTING.md 0000664 0000000 0000000 00000005657 14344402622 0015327 0 ustar 00root root 0000000 0000000 # Contributing to Survey
🎉🎉 First off, thanks for the interest in contributing to `survey`! 🎉🎉
The following is a set of guidelines to follow when contributing to this package. These are not hard rules, please use common sense and feel free to propose changes to this document in a pull request.
## Code of Conduct
This project and its contibutors are expected to uphold the [Go Community Code of Conduct](https://golang.org/conduct). By participating, you are expected to follow these guidelines.
## Getting help
* [Open an issue](https://github.com/AlecAivazis/survey/issues/new/choose)
* Reach out to `@AlecAivazis` or `@mislav` in the Gophers slack (please use only when urgent)
## Submitting a contribution
When submitting a contribution,
- Try to make a series of smaller changes instead of one large change
- Provide a description of each change that you are proposing
- Reference the issue addressed by your pull request (if there is one)
- Document all new exported Go APIs
- Update the project's README when applicable
- Include unit tests if possible
- Contributions with visual ramifications or interaction changes should be accompanied with an integration test—see below for details.
## Writing and running tests
When submitting features, please add as many units tests as necessary to test both positive and negative cases.
Integration tests for survey uses [go-expect](https://github.com/Netflix/go-expect) to expect a match on stdout and respond on stdin. Since `os.Stdout` in a `go test` process is not a TTY, you need a way to interpret terminal / ANSI escape sequences for things like `CursorLocation`. The stdin/stdout handled by `go-expect` is also multiplexed to a [virtual terminal](https://github.com/hinshun/vt10x).
For example, you can extend the tests for Input by specifying the following test case:
```go
{
"Test Input prompt interaction", // Name of the test.
&Input{ // An implementation of the survey.Prompt interface.
Message: "What is your name?",
},
func(c *expect.Console) { // An expect procedure. You can expect strings / regexps and
c.ExpectString("What is your name?") // write back strings / bytes to its psuedoterminal for survey.
c.SendLine("Johnny Appleseed")
c.ExpectEOF() // Nothing is read from the tty without an expect, and once an
// expectation is met, no further bytes are read. End your
// procedure with `c.ExpectEOF()` to read until survey finishes.
},
"Johnny Appleseed", // The expected result.
}
```
If you want to write your own `go-expect` test from scratch, you'll need to instantiate a virtual terminal,
multiplex it into an `*expect.Console`, and hook up its tty with survey's optional stdio. Please see `go-expect`
[documentation](https://godoc.org/github.com/Netflix/go-expect) for more detail.
survey-2.3.7/LICENSE 0000664 0000000 0000000 00000002055 14344402622 0014070 0 ustar 00root root 0000000 0000000 MIT License
Copyright (c) 2018 Alec Aivazis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
survey-2.3.7/README.md 0000664 0000000 0000000 00000041373 14344402622 0014350 0 ustar 00root root 0000000 0000000 # Survey
[](https://pkg.go.dev/github.com/AlecAivazis/survey/v2)
A library for building interactive and accessible prompts on terminals supporting ANSI escape sequences.
```go
package main
import (
"fmt"
"github.com/AlecAivazis/survey/v2"
)
// the questions to ask
var qs = []*survey.Question{
{
Name: "name",
Prompt: &survey.Input{Message: "What is your name?"},
Validate: survey.Required,
Transform: survey.Title,
},
{
Name: "color",
Prompt: &survey.Select{
Message: "Choose a color:",
Options: []string{"red", "blue", "green"},
Default: "red",
},
},
{
Name: "age",
Prompt: &survey.Input{Message: "How old are you?"},
},
}
func main() {
// the answers will be written to this struct
answers := struct {
Name string // survey will match the question and field names
FavoriteColor string `survey:"color"` // or you can tag fields to match a specific name
Age int // if the types don't match, survey will convert it
}{}
// perform the questions
err := survey.Ask(qs, &answers)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Printf("%s chose %s.", answers.Name, answers.FavoriteColor)
}
```
## Examples
Examples can be found in the `examples/` directory. Run them
to see basic behavior:
```bash
go run examples/simple.go
go run examples/validation.go
```
## Running the Prompts
There are two primary ways to execute prompts and start collecting information from your users: `Ask` and
`AskOne`. The primary difference is whether you are interested in collecting a single piece of information
or if you have a list of questions to ask whose answers should be collected in a single struct.
For most basic usecases, `Ask` should be enough. However, for surveys with complicated branching logic,
we recommend that you break out your questions into multiple calls to both of these functions to fit your needs.
### Configuring the Prompts
Most prompts take fine-grained configuration through fields on the structs you instantiate. It is also
possible to change survey's default behaviors by passing `AskOpts` to either `Ask` or `AskOne`. Examples
in this document will do both interchangeably:
```golang
prompt := &Select{
Message: "Choose a color:",
Options: []string{"red", "blue", "green"},
// can pass a validator directly
Validate: survey.Required,
}
// or define a default for the single call to `AskOne`
// the answer will get written to the color variable
survey.AskOne(prompt, &color, survey.WithValidator(survey.Required))
// or define a default for every entry in a list of questions
// the answer will get copied into the matching field of the struct as shown above
survey.Ask(questions, &answers, survey.WithValidator(survey.Required))
```
## Prompts
### Input
```golang
name := ""
prompt := &survey.Input{
Message: "ping",
}
survey.AskOne(prompt, &name)
```
#### Suggestion Options
```golang
file := ""
prompt := &survey.Input{
Message: "inform a file to save:",
Suggest: func (toComplete string) []string {
files, _ := filepath.Glob(toComplete + "*")
return files
},
}
}
survey.AskOne(prompt, &file)
```
### Multiline
```golang
text := ""
prompt := &survey.Multiline{
Message: "ping",
}
survey.AskOne(prompt, &text)
```
### Password
```golang
password := ""
prompt := &survey.Password{
Message: "Please type your password",
}
survey.AskOne(prompt, &password)
```
### Confirm
```golang
name := false
prompt := &survey.Confirm{
Message: "Do you like pie?",
}
survey.AskOne(prompt, &name)
```
### Select
```golang
color := ""
prompt := &survey.Select{
Message: "Choose a color:",
Options: []string{"red", "blue", "green"},
}
survey.AskOne(prompt, &color)
```
Fields and values that come from a `Select` prompt can be one of two different things. If you pass an `int`
the field will have the value of the selected index. If you instead pass a string, the string value selected
will be written to the field.
The user can also press `esc` to toggle the ability cycle through the options with the j and k keys to do down and up respectively.
By default, the select prompt is limited to showing 7 options at a time
and will paginate lists of options longer than that. This can be changed a number of ways:
```golang
// as a field on a single select
prompt := &survey.MultiSelect{..., PageSize: 10}
// or as an option to Ask or AskOne
survey.AskOne(prompt, &days, survey.WithPageSize(10))
```
#### Select options description
The optional description text can be used to add extra information to each option listed in the select prompt:
```golang
color := ""
prompt := &survey.Select{
Message: "Choose a color:",
Options: []string{"red", "blue", "green"},
Description: func(value string, index int) string {
if value == "red" {
return "My favorite color"
}
return ""
},
}
survey.AskOne(prompt, &color)
// Assuming that the user chose "red - My favorite color":
fmt.Println(color) //=> "red"
```
### MultiSelect

```golang
days := []string{}
prompt := &survey.MultiSelect{
Message: "What days do you prefer:",
Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
}
survey.AskOne(prompt, &days)
```
Fields and values that come from a `MultiSelect` prompt can be one of two different things. If you pass an `int`
the field will have a slice of the selected indices. If you instead pass a string, a slice of the string values
selected will be written to the field.
The user can also press `esc` to toggle the ability cycle through the options with the j and k keys to do down and up respectively.
By default, the MultiSelect prompt is limited to showing 7 options at a time
and will paginate lists of options longer than that. This can be changed a number of ways:
```golang
// as a field on a single select
prompt := &survey.MultiSelect{..., PageSize: 10}
// or as an option to Ask or AskOne
survey.AskOne(prompt, &days, survey.WithPageSize(10))
```
### Editor
Launches the user's preferred editor (defined by the \$VISUAL or \$EDITOR environment variables) on a
temporary file. Once the user exits their editor, the contents of the temporary file are read in as
the result. If neither of those are present, notepad (on Windows) or vim (Linux or Mac) is used.
You can also specify a [pattern](https://golang.org/pkg/io/ioutil/#TempFile) for the name of the temporary file. This
can be useful for ensuring syntax highlighting matches your usecase.
```golang
prompt := &survey.Editor{
Message: "Shell code snippet",
FileName: "*.sh",
}
survey.AskOne(prompt, &content)
```
## Filtering Options
By default, the user can filter for options in Select and MultiSelects by typing while the prompt
is active. This will filter out all options that don't contain the typed string anywhere in their name, ignoring case.
A custom filter function can also be provided to change this behavior:
```golang
func myFilter(filterValue string, optValue string, optIndex int) bool {
// only include the option if it includes the filter and has length greater than 5
return strings.Contains(optValue, filterValue) && len(optValue) >= 5
}
// configure it for a specific prompt
&Select{
Message: "Choose a color:",
Options: []string{"red", "blue", "green"},
Filter: myFilter,
}
// or define a default for all of the questions
survey.AskOne(prompt, &color, survey.WithFilter(myFilter))
```
## Keeping the filter active
By default the filter will disappear if the user selects one of the filtered elements. Once the user selects one element the filter setting is gone.
However the user can prevent this from happening and keep the filter active for multiple selections in a e.g. MultiSelect:
```golang
// configure it for a specific prompt
&Select{
Message: "Choose a color:",
Options: []string{"light-green", "green", "dark-green", "red"},
KeepFilter: true,
}
// or define a default for all of the questions
survey.AskOne(prompt, &color, survey.WithKeepFilter(true))
```
## Validation
Validating individual responses for a particular question can be done by defining a
`Validate` field on the `survey.Question` to be validated. This function takes an
`interface{}` type and returns an error to show to the user, prompting them for another
response. Like usual, validators can be provided directly to the prompt or with `survey.WithValidator`:
```golang
q := &survey.Question{
Prompt: &survey.Input{Message: "Hello world validation"},
Validate: func (val interface{}) error {
// since we are validating an Input, the assertion will always succeed
if str, ok := val.(string) ; !ok || len(str) > 10 {
return errors.New("This response cannot be longer than 10 characters.")
}
return nil
},
}
color := ""
prompt := &survey.Input{ Message: "Whats your name?" }
// you can pass multiple validators here and survey will make sure each one passes
survey.AskOne(prompt, &color, survey.WithValidator(survey.Required))
```
### Built-in Validators
`survey` comes prepackaged with a few validators to fit common situations. Currently these
validators include:
| name | valid types | description | notes |
| ------------ | -------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| Required | any | Rejects zero values of the response type | Boolean values pass straight through since the zero value (false) is a valid response |
| MinLength(n) | string | Enforces that a response is at least the given length | |
| MaxLength(n) | string | Enforces that a response is no longer than the given length | |
| MaxItems(n) | []OptionAnswer | Enforces that a response has no more selections of the indicated | |
| MinItems(n) | []OptionAnswer | Enforces that a response has no less selections of the indicated | |
## Help Text
All of the prompts have a `Help` field which can be defined to provide more information to your users:
```golang
&survey.Input{
Message: "What is your phone number:",
Help: "Phone number should include the area code",
}
```
## Removing the "Select All" and "Select None" options
By default, users can select all of the multi-select options using the right arrow key. To prevent users from being able to do this (and remove the ` to all` message from the prompt), use the option `WithRemoveSelectAll`:
```golang
import (
"github.com/AlecAivazis/survey/v2"
)
number := ""
prompt := &survey.Input{
Message: "This question has the select all option removed",
}
survey.AskOne(prompt, &number, survey.WithRemoveSelectAll())
```
Also by default, users can use the left arrow key to unselect all of the options. To prevent users from being able to do this (and remove the ` to none` message from the prompt), use the option `WithRemoveSelectNone`:
```golang
import (
"github.com/AlecAivazis/survey/v2"
)
number := ""
prompt := &survey.Input{
Message: "This question has the select all option removed",
}
survey.AskOne(prompt, &number, survey.WithRemoveSelectNone())
```
### Changing the input rune
In some situations, `?` is a perfectly valid response. To handle this, you can change the rune that survey
looks for with `WithHelpInput`:
```golang
import (
"github.com/AlecAivazis/survey/v2"
)
number := ""
prompt := &survey.Input{
Message: "If you have this need, please give me a reasonable message.",
Help: "I couldn't come up with one.",
}
survey.AskOne(prompt, &number, survey.WithHelpInput('^'))
```
## Changing the Icons
Changing the icons and their color/format can be done by passing the `WithIcons` option. The format
follows the patterns outlined [here](https://github.com/mgutz/ansi#style-format). For example:
```golang
import (
"github.com/AlecAivazis/survey/v2"
)
number := ""
prompt := &survey.Input{
Message: "If you have this need, please give me a reasonable message.",
Help: "I couldn't come up with one.",
}
survey.AskOne(prompt, &number, survey.WithIcons(func(icons *survey.IconSet) {
// you can set any icons
icons.Question.Text = "⁇"
// for more information on formatting the icons, see here: https://github.com/mgutz/ansi#style-format
icons.Question.Format = "yellow+hb"
}))
```
The icons and their default text and format are summarized below:
| name | text | format | description |
| -------------- | ---- | ---------- | ------------------------------------------------------------- |
| Error | X | red | Before an error |
| Help | i | cyan | Before help text |
| Question | ? | green+hb | Before the message of a prompt |
| SelectFocus | > | green | Marks the current focus in `Select` and `MultiSelect` prompts |
| UnmarkedOption | [ ] | default+hb | Marks an unselected option in a `MultiSelect` prompt |
| MarkedOption | [x] | cyan+b | Marks a chosen selection in a `MultiSelect` prompt |
## Custom Types
survey will assign prompt answers to your custom types if they implement this interface:
```golang
type Settable interface {
WriteAnswer(field string, value interface{}) error
}
```
Here is an example how to use them:
```golang
type MyValue struct {
value string
}
func (my *MyValue) WriteAnswer(name string, value interface{}) error {
my.value = value.(string)
}
myval := MyValue{}
survey.AskOne(
&survey.Input{
Message: "Enter something:",
},
&myval
)
```
## Testing
You can test your program's interactive prompts using [go-expect](https://github.com/Netflix/go-expect). The library
can be used to expect a match on stdout and respond on stdin. Since `os.Stdout` in a `go test` process is not a TTY,
if you are manipulating the cursor or using `survey`, you will need a way to interpret terminal / ANSI escape sequences
for things like `CursorLocation`. `vt10x.NewVT10XConsole` will create a `go-expect` console that also multiplexes
stdio to an in-memory [virtual terminal](https://github.com/hinshun/vt10x).
For some examples, you can see any of the tests in this repo.
## FAQ
### What kinds of IO are supported by `survey`?
survey aims to support most terminal emulators; it expects support for ANSI escape sequences.
This means that reading from piped stdin or writing to piped stdout is **not supported**,
and likely to break your application in these situations. See [#337](https://github.com/AlecAivazis/survey/pull/337#issue-581351617)
### Why isn't Ctrl-C working?
Ordinarily, when you type Ctrl-C, the terminal recognizes this as the QUIT button and delivers a SIGINT signal to the process, which terminates it.
However, Survey temporarily configures the terminal to deliver control codes as ordinary input bytes.
When Survey reads a ^C byte (ASCII \x03, "end of text"), it interrupts the current survey and returns a
`github.com/AlecAivazis/survey/v2/terminal.InterruptErr` from `Ask` or `AskOne`.
If you want to stop the process, handle the returned error in your code:
```go
err := survey.AskOne(prompt, &myVar)
if err != nil {
if err == terminal.InterruptErr {
log.Fatal("interrupted")
}
...
}
```
survey-2.3.7/confirm.go 0000664 0000000 0000000 00000007170 14344402622 0015052 0 ustar 00root root 0000000 0000000 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,
},
)
}
survey-2.3.7/confirm_test.go 0000664 0000000 0000000 00000007324 14344402622 0016112 0 ustar 00root root 0000000 0000000 package survey
import (
"bytes"
"fmt"
"io"
"os"
"testing"
"github.com/AlecAivazis/survey/v2/core"
"github.com/AlecAivazis/survey/v2/terminal"
"github.com/stretchr/testify/assert"
)
func init() {
// disable color output for all prompts to simplify testing
core.DisableColor = true
}
func TestConfirmRender(t *testing.T) {
tests := []struct {
title string
prompt Confirm
data ConfirmTemplateData
expected string
}{
{
"Test Confirm question output with default true",
Confirm{Message: "Is pizza your favorite food?", Default: true},
ConfirmTemplateData{},
fmt.Sprintf("%s Is pizza your favorite food? (Y/n) ", defaultIcons().Question.Text),
},
{
"Test Confirm question output with default false",
Confirm{Message: "Is pizza your favorite food?", Default: false},
ConfirmTemplateData{},
fmt.Sprintf("%s Is pizza your favorite food? (y/N) ", defaultIcons().Question.Text),
},
{
"Test Confirm answer output",
Confirm{Message: "Is pizza your favorite food?"},
ConfirmTemplateData{Answer: "Yes"},
fmt.Sprintf("%s Is pizza your favorite food? Yes\n", defaultIcons().Question.Text),
},
{
"Test Confirm with help but help message is hidden",
Confirm{Message: "Is pizza your favorite food?", Help: "This is helpful"},
ConfirmTemplateData{},
fmt.Sprintf("%s Is pizza your favorite food? [%s for help] (y/N) ", defaultIcons().Question.Text, string(defaultPromptConfig().HelpInput)),
},
{
"Test Confirm help output with help message shown",
Confirm{Message: "Is pizza your favorite food?", Help: "This is helpful"},
ConfirmTemplateData{ShowHelp: true},
fmt.Sprintf("%s This is helpful\n%s Is pizza your favorite food? (y/N) ", defaultIcons().Help.Text, defaultIcons().Question.Text),
},
}
for _, test := range tests {
t.Run(test.title, func(t *testing.T) {
r, w, err := os.Pipe()
assert.NoError(t, err)
test.prompt.WithStdio(terminal.Stdio{Out: w})
test.data.Confirm = test.prompt
// set the runtime config
test.data.Config = defaultPromptConfig()
err = test.prompt.Render(
ConfirmQuestionTemplate,
test.data,
)
assert.NoError(t, err)
assert.NoError(t, w.Close())
var buf bytes.Buffer
_, err = io.Copy(&buf, r)
assert.NoError(t, err)
assert.Contains(t, buf.String(), test.expected)
})
}
}
func TestConfirmPrompt(t *testing.T) {
tests := []PromptTest{
{
"Test Confirm prompt interaction",
&Confirm{
Message: "Is pizza your favorite food?",
},
func(c expectConsole) {
c.ExpectString("Is pizza your favorite food? (y/N)")
c.SendLine("n")
c.ExpectEOF()
},
false,
},
{
"Test Confirm prompt interaction with default",
&Confirm{
Message: "Is pizza your favorite food?",
Default: true,
},
func(c expectConsole) {
c.ExpectString("Is pizza your favorite food? (Y/n)")
c.SendLine("")
c.ExpectEOF()
},
true,
},
{
"Test Confirm prompt interaction overriding default",
&Confirm{
Message: "Is pizza your favorite food?",
Default: true,
},
func(c expectConsole) {
c.ExpectString("Is pizza your favorite food? (Y/n)")
c.SendLine("n")
c.ExpectEOF()
},
false,
},
{
"Test Confirm prompt interaction and prompt for help",
&Confirm{
Message: "Is pizza your favorite food?",
Help: "It probably is",
},
func(c expectConsole) {
c.ExpectString(
fmt.Sprintf(
"Is pizza your favorite food? [%s for help] (y/N)",
string(defaultPromptConfig().HelpInput),
),
)
c.SendLine("?")
c.ExpectString("It probably is")
c.SendLine("Y")
c.ExpectEOF()
},
true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
RunPromptTest(t, test)
})
}
}
survey-2.3.7/core/ 0000775 0000000 0000000 00000000000 14344402622 0014011 5 ustar 00root root 0000000 0000000 survey-2.3.7/core/template.go 0000664 0000000 0000000 00000005606 14344402622 0016162 0 ustar 00root root 0000000 0000000 package core
import (
"bytes"
"os"
"sync"
"text/template"
"github.com/mgutz/ansi"
)
// DisableColor can be used to make testing reliable
var DisableColor = false
var TemplateFuncsWithColor = map[string]interface{}{
// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
"color": ansi.ColorCode,
}
var TemplateFuncsNoColor = map[string]interface{}{
// Templates without Color formatting. For layout/ testing.
"color": func(color string) string {
return ""
},
}
// envColorDisabled returns if output colors are forbid by environment variables
func envColorDisabled() bool {
return os.Getenv("NO_COLOR") != "" || os.Getenv("CLICOLOR") == "0"
}
// envColorForced returns if output colors are forced from environment variables
func envColorForced() bool {
val, ok := os.LookupEnv("CLICOLOR_FORCE")
return ok && val != "0"
}
// RunTemplate returns two formatted strings given a template and
// the data it requires. The first string returned is generated for
// user-facing output and may or may not contain ANSI escape codes
// for colored output. The second string does not contain escape codes
// and can be used by the renderer for layout purposes.
func RunTemplate(tmpl string, data interface{}) (string, string, error) {
tPair, err := GetTemplatePair(tmpl)
if err != nil {
return "", "", err
}
userBuf := bytes.NewBufferString("")
err = tPair[0].Execute(userBuf, data)
if err != nil {
return "", "", err
}
layoutBuf := bytes.NewBufferString("")
err = tPair[1].Execute(layoutBuf, data)
if err != nil {
return userBuf.String(), "", err
}
return userBuf.String(), layoutBuf.String(), err
}
var (
memoizedGetTemplate = map[string][2]*template.Template{}
memoMutex = &sync.RWMutex{}
)
// GetTemplatePair returns a pair of compiled templates where the
// first template is generated for user-facing output and the
// second is generated for use by the renderer. The second
// template does not contain any color escape codes, whereas
// the first template may or may not depending on DisableColor.
func GetTemplatePair(tmpl string) ([2]*template.Template, error) {
memoMutex.RLock()
if t, ok := memoizedGetTemplate[tmpl]; ok {
memoMutex.RUnlock()
return t, nil
}
memoMutex.RUnlock()
templatePair := [2]*template.Template{nil, nil}
templateNoColor, err := template.New("prompt").Funcs(TemplateFuncsNoColor).Parse(tmpl)
if err != nil {
return [2]*template.Template{}, err
}
templatePair[1] = templateNoColor
envColorHide := envColorDisabled() && !envColorForced()
if DisableColor || envColorHide {
templatePair[0] = templatePair[1]
} else {
templateWithColor, err := template.New("prompt").Funcs(TemplateFuncsWithColor).Parse(tmpl)
templatePair[0] = templateWithColor
if err != nil {
return [2]*template.Template{}, err
}
}
memoMutex.Lock()
memoizedGetTemplate[tmpl] = templatePair
memoMutex.Unlock()
return templatePair, nil
}
survey-2.3.7/core/write.go 0000664 0000000 0000000 00000023717 14344402622 0015504 0 ustar 00root root 0000000 0000000 package core
import (
"errors"
"fmt"
"reflect"
"strconv"
"strings"
"time"
)
// the tag used to denote the name of the question
const tagName = "survey"
// Settable allow for configuration when assigning answers
type Settable interface {
WriteAnswer(field string, value interface{}) error
}
// OptionAnswer is the return type of Selects/MultiSelects that lets the appropriate information
// get copied to the user's struct
type OptionAnswer struct {
Value string
Index int
}
type reflectField struct {
value reflect.Value
fieldType reflect.StructField
}
func OptionAnswerList(incoming []string) []OptionAnswer {
list := []OptionAnswer{}
for i, opt := range incoming {
list = append(list, OptionAnswer{Value: opt, Index: i})
}
return list
}
func WriteAnswer(t interface{}, name string, v interface{}) (err error) {
// if the field is a custom type
if s, ok := t.(Settable); ok {
// use the interface method
return s.WriteAnswer(name, v)
}
// the target to write to
target := reflect.ValueOf(t)
// the value to write from
value := reflect.ValueOf(v)
// make sure we are writing to a pointer
if target.Kind() != reflect.Ptr {
return errors.New("you must pass a pointer as the target of a Write operation")
}
// the object "inside" of the target pointer
elem := target.Elem()
// handle the special types
switch elem.Kind() {
// if we are writing to a struct
case reflect.Struct:
// if we are writing to an option answer than we want to treat
// it like a single thing and not a place to deposit answers
if elem.Type().Name() == "OptionAnswer" {
// copy the value over to the normal struct
return copy(elem, value)
}
// get the name of the field that matches the string we were given
field, _, err := findField(elem, name)
// if something went wrong
if err != nil {
// bubble up
return err
}
// handle references to the Settable interface aswell
if s, ok := field.Interface().(Settable); ok {
// use the interface method
return s.WriteAnswer(name, v)
}
if field.CanAddr() {
if s, ok := field.Addr().Interface().(Settable); ok {
// use the interface method
return s.WriteAnswer(name, v)
}
}
// copy the value over to the normal struct
return copy(field, value)
case reflect.Map:
mapType := reflect.TypeOf(t).Elem()
if mapType.Key().Kind() != reflect.String {
return errors.New("answer maps key must be of type string")
}
// copy only string value/index value to map if,
// map is not of type interface and is 'OptionAnswer'
if value.Type().Name() == "OptionAnswer" {
if kval := mapType.Elem().Kind(); kval == reflect.String {
mt := *t.(*map[string]string)
mt[name] = value.FieldByName("Value").String()
return nil
} else if kval == reflect.Int {
mt := *t.(*map[string]int)
mt[name] = int(value.FieldByName("Index").Int())
return nil
}
}
if mapType.Elem().Kind() != reflect.Interface {
return errors.New("answer maps must be of type map[string]interface")
}
mt := *t.(*map[string]interface{})
mt[name] = value.Interface()
return nil
}
// otherwise just copy the value to the target
return copy(elem, value)
}
type errFieldNotMatch struct {
questionName string
}
func (err errFieldNotMatch) Error() string {
return fmt.Sprintf("could not find field matching %v", err.questionName)
}
func (err errFieldNotMatch) Is(target error) bool { // implements the dynamic errors.Is interface.
if target != nil {
if name, ok := IsFieldNotMatch(target); ok {
// if have a filled questionName then perform "deeper" comparison.
return name == "" || err.questionName == "" || name == err.questionName
}
}
return false
}
// IsFieldNotMatch reports whether an "err" is caused by a non matching field.
// It returns the Question.Name that couldn't be matched with a destination field.
//
// Usage:
//
// if err := survey.Ask(qs, &v); err != nil {
// if name, ok := core.IsFieldNotMatch(err); ok {
// // name is the question name that did not match a field
// }
// }
func IsFieldNotMatch(err error) (string, bool) {
if err != nil {
if v, ok := err.(errFieldNotMatch); ok {
return v.questionName, true
}
}
return "", false
}
// BUG(AlecAivazis): the current implementation might cause weird conflicts if there are
// two fields with same name that only differ by casing.
func findField(s reflect.Value, name string) (reflect.Value, reflect.StructField, error) {
fields := flattenFields(s)
// first look for matching tags so we can overwrite matching field names
for _, f := range fields {
// the value of the survey tag
tag := f.fieldType.Tag.Get(tagName)
// if the tag matches the name we are looking for
if tag != "" && tag == name {
// then we found our index
return f.value, f.fieldType, nil
}
}
// then look for matching names
for _, f := range fields {
// if the name of the field matches what we're looking for
if strings.EqualFold(f.fieldType.Name, name) {
return f.value, f.fieldType, nil
}
}
// we didn't find the field
return reflect.Value{}, reflect.StructField{}, errFieldNotMatch{name}
}
func flattenFields(s reflect.Value) []reflectField {
sType := s.Type()
numField := sType.NumField()
fields := make([]reflectField, 0, numField)
for i := 0; i < numField; i++ {
fieldType := sType.Field(i)
field := s.Field(i)
if field.Kind() == reflect.Struct && fieldType.Anonymous {
// field is a promoted structure
fields = append(fields, flattenFields(field)...)
continue
}
fields = append(fields, reflectField{field, fieldType})
}
return fields
}
// isList returns true if the element is something we can Len()
func isList(v reflect.Value) bool {
switch v.Type().Kind() {
case reflect.Array, reflect.Slice:
return true
default:
return false
}
}
// Write takes a value and copies it to the target
func copy(t reflect.Value, v reflect.Value) (err error) {
// if something ends up panicing we need to catch it in a deferred func
defer func() {
if r := recover(); r != nil {
// if we paniced with an error
if _, ok := r.(error); ok {
// cast the result to an error object
err = r.(error)
} else if _, ok := r.(string); ok {
// otherwise we could have paniced with a string so wrap it in an error
err = errors.New(r.(string))
}
}
}()
// if we are copying from a string result to something else
if v.Kind() == reflect.String && v.Type() != t.Type() {
var castVal interface{}
var casterr error
vString := v.Interface().(string)
switch t.Kind() {
case reflect.Bool:
castVal, casterr = strconv.ParseBool(vString)
case reflect.Int:
castVal, casterr = strconv.Atoi(vString)
case reflect.Int8:
var val64 int64
val64, casterr = strconv.ParseInt(vString, 10, 8)
if casterr == nil {
castVal = int8(val64)
}
case reflect.Int16:
var val64 int64
val64, casterr = strconv.ParseInt(vString, 10, 16)
if casterr == nil {
castVal = int16(val64)
}
case reflect.Int32:
var val64 int64
val64, casterr = strconv.ParseInt(vString, 10, 32)
if casterr == nil {
castVal = int32(val64)
}
case reflect.Int64:
if t.Type() == reflect.TypeOf(time.Duration(0)) {
castVal, casterr = time.ParseDuration(vString)
} else {
castVal, casterr = strconv.ParseInt(vString, 10, 64)
}
case reflect.Uint:
var val64 uint64
val64, casterr = strconv.ParseUint(vString, 10, 8)
if casterr == nil {
castVal = uint(val64)
}
case reflect.Uint8:
var val64 uint64
val64, casterr = strconv.ParseUint(vString, 10, 8)
if casterr == nil {
castVal = uint8(val64)
}
case reflect.Uint16:
var val64 uint64
val64, casterr = strconv.ParseUint(vString, 10, 16)
if casterr == nil {
castVal = uint16(val64)
}
case reflect.Uint32:
var val64 uint64
val64, casterr = strconv.ParseUint(vString, 10, 32)
if casterr == nil {
castVal = uint32(val64)
}
case reflect.Uint64:
castVal, casterr = strconv.ParseUint(vString, 10, 64)
case reflect.Float32:
var val64 float64
val64, casterr = strconv.ParseFloat(vString, 32)
if casterr == nil {
castVal = float32(val64)
}
case reflect.Float64:
castVal, casterr = strconv.ParseFloat(vString, 64)
default:
//lint:ignore ST1005 allow this error message to be capitalized
return fmt.Errorf("Unable to convert from string to type %s", t.Kind())
}
if casterr != nil {
return casterr
}
t.Set(reflect.ValueOf(castVal))
return
}
// if we are copying from an OptionAnswer to something
if v.Type().Name() == "OptionAnswer" {
// copying an option answer to a string
if t.Kind() == reflect.String {
// copies the Value field of the struct
t.Set(reflect.ValueOf(v.FieldByName("Value").Interface()))
return
}
// copying an option answer to an int
if t.Kind() == reflect.Int {
// copies the Index field of the struct
t.Set(reflect.ValueOf(v.FieldByName("Index").Interface()))
return
}
// copying an OptionAnswer to an OptionAnswer
if t.Type().Name() == "OptionAnswer" {
t.Set(v)
return
}
// we're copying an option answer to an incorrect type
//lint:ignore ST1005 allow this error message to be capitalized
return fmt.Errorf("Unable to convert from OptionAnswer to type %s", t.Kind())
}
// if we are copying from one slice or array to another
if isList(v) && isList(t) {
// loop over every item in the desired value
for i := 0; i < v.Len(); i++ {
// write to the target given its kind
switch t.Kind() {
// if its a slice
case reflect.Slice:
// an object of the correct type
obj := reflect.Indirect(reflect.New(t.Type().Elem()))
// write the appropriate value to the obj and catch any errors
if err := copy(obj, v.Index(i)); err != nil {
return err
}
// just append the value to the end
t.Set(reflect.Append(t, obj))
// otherwise it could be an array
case reflect.Array:
// set the index to the appropriate value
if err := copy(t.Slice(i, i+1).Index(0), v.Index(i)); err != nil {
return err
}
}
}
} else {
// set the value to the target
t.Set(v)
}
// we're done
return
}
survey-2.3.7/core/write_test.go 0000664 0000000 0000000 00000050330 14344402622 0016532 0 ustar 00root root 0000000 0000000 package core
import (
"fmt"
"reflect"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestWrite_returnsErrorIfTargetNotPtr(t *testing.T) {
// try to copy a value to a non-pointer
err := WriteAnswer(true, "hello", true)
// make sure there was an error
if err == nil {
t.Error("Did not encounter error when writing to non-pointer.")
}
}
func TestWrite_canWriteToBool(t *testing.T) {
// a pointer to hold the boolean value
ptr := true
// try to copy a false value to the pointer
err := WriteAnswer(&ptr, "", false)
if err != nil {
t.Fatalf("WriteAnswer() = %v", err)
}
// if the value is true
if ptr {
// the test failed
t.Error("Could not write a false bool to a pointer")
}
}
func TestWrite_canWriteString(t *testing.T) {
// a pointer to hold the boolean value
ptr := ""
// try to copy a false value to the pointer
err := WriteAnswer(&ptr, "", "hello")
if err != nil {
t.Fatalf("WriteAnswer() = %v", err)
}
// if the value is not what we wrote
if ptr != "hello" {
t.Error("Could not write a string value to a pointer")
}
}
func TestWrite_canWriteSlice(t *testing.T) {
// a pointer to hold the value
ptr := []string{}
// copy in a value
err := WriteAnswer(&ptr, "", []string{"hello", "world"})
if err != nil {
t.Fatalf("WriteAnswer() = %v", err)
}
// make sure there are two entries
assert.Equal(t, []string{"hello", "world"}, ptr)
}
func TestWrite_canWriteMapString(t *testing.T) {
// a map to hold the values
ptr := map[string]string{}
// copy in a value
err := WriteAnswer(&ptr, "test", OptionAnswer{Value: "hello", Index: 5})
if err != nil {
t.Fatalf("WriteAnswer() = %v", err)
}
// make sure only string value is written
assert.Equal(t, map[string]string{"test": "hello"}, ptr)
}
func TestWrite_canWriteMapInt(t *testing.T) {
// a map to hold the values
ptr := map[string]int{}
// copy in a value
err := WriteAnswer(&ptr, "test", OptionAnswer{Value: "hello", Index: 5})
if err != nil {
t.Fatalf("WriteAnswer() = %v", err)
}
// make sure only string value is written
assert.Equal(t, map[string]int{"test": 5}, ptr)
}
func TestWrite_recoversInvalidReflection(t *testing.T) {
// a variable to mutate
ptr := false
// write a boolean value to the string
err := WriteAnswer(&ptr, "", "hello")
// if there was no error
if err == nil {
// the test failed
t.Error("Did not encounter error when forced invalid write.")
}
}
func TestWriteAnswer_handlesNonStructValues(t *testing.T) {
// the value to write to
ptr := ""
// write a value to the pointer
err := WriteAnswer(&ptr, "", "world")
if err != nil {
t.Fatalf("WriteAnswer() = %v", err)
}
// if we didn't change the value appropriate
if ptr != "world" {
// the test failed
t.Error("Did not write value to primitive pointer")
}
}
func TestWriteAnswer_canMutateStruct(t *testing.T) {
// the struct to hold the answer
ptr := struct{ Name string }{}
// write a value to an existing field
err := WriteAnswer(&ptr, "name", "world")
if err != nil {
// the test failed
t.Errorf("Encountered error while writing answer: %v", err.Error())
// we're done here
return
}
// make sure we changed the field
if ptr.Name != "world" {
// the test failed
t.Error("Did not mutate struct field when writing answer.")
}
}
func TestWriteAnswer_optionAnswer(t *testing.T) {
t.Run("writes index for ints", func(t *testing.T) {
val := 0
// write a value to an existing field
err := WriteAnswer(&val, "", OptionAnswer{
Index: 10,
Value: "string value",
})
if err != nil {
t.Errorf("Encountered error while writing answer: %v", err.Error())
return
}
if val != 10 {
t.Errorf("Wrong value written. Wanted 10, got %v", val)
return
}
})
t.Run("writes OptionAnswer for OptionAnswer", func(t *testing.T) {
val := OptionAnswer{}
// write a value to an existing field
err := WriteAnswer(&val, "", OptionAnswer{
Index: 10,
Value: "string value",
})
if err != nil {
t.Errorf("Encountered error while writing answer: %v", err.Error())
return
}
if val.Index != 10 || val.Value != "string value" {
t.Errorf("Wrong internal values: %v", val)
return
}
})
t.Run("writes value for strings", func(t *testing.T) {
val := ""
// write a value to an existing field
err := WriteAnswer(&val, "", OptionAnswer{
Index: 10,
Value: "string value",
})
if err != nil {
t.Errorf("Encountered error while writing answer: %v", err.Error())
return
}
if val != "string value" {
t.Errorf("Wrong value written. Wanted \"100\", got %v", val)
return
}
})
t.Run("writes slice of indices for slice of ints", func(t *testing.T) {
val := []int{}
// write a value to an existing field
err := WriteAnswer(&val, "", []OptionAnswer{
{
Index: 10,
Value: "string value",
},
})
if err != nil {
t.Errorf("Encountered error while writing answer: %v", err.Error())
return
}
if len(val) != 1 || val[0] != 10 {
t.Errorf("Wrong value written. Wanted [10], got %v", val)
return
}
})
t.Run("writes slice of values for slice of strings", func(t *testing.T) {
val := []string{}
// write a value to an existing field
err := WriteAnswer(&val, "", []OptionAnswer{
{
Index: 10,
Value: "string value",
},
})
if err != nil {
t.Errorf("Encountered error while writing answer: %v", err.Error())
return
}
if len(val) != 1 || val[0] != "string value" {
t.Errorf("Wrong value written. Wanted [string value], got %v", val)
return
}
})
}
func TestWriteAnswer_canMutateMap(t *testing.T) {
// the map to hold the answer
ptr := make(map[string]interface{})
// write a value to an existing field
err := WriteAnswer(&ptr, "name", "world")
if err != nil {
// the test failed
t.Errorf("Encountered error while writing answer: %v", err.Error())
// we're done here
return
}
// make sure we changed the field
if ptr["name"] != "world" {
// the test failed
t.Error("Did not mutate map when writing answer.")
}
}
func TestWrite_returnsErrorIfInvalidMapType(t *testing.T) {
// try to copy a value to a non map[string]interface{}
ptr := make(map[int]string)
err := WriteAnswer(&ptr, "name", "world")
// make sure there was an error
if err == nil {
t.Error("Did not encounter error when writing to invalid map.")
}
}
func TestWrite_writesStringSliceToIntSlice(t *testing.T) {
// make a slice of int to write to
target := []int{}
// write the answer
err := WriteAnswer(&target, "name", []string{"1", "2", "3"})
// make sure there was no error
assert.Nil(t, err, "WriteSlice to Int Slice")
// and we got what we wanted
assert.Equal(t, []int{1, 2, 3}, target)
}
func TestWrite_writesStringArrayToIntArray(t *testing.T) {
// make an array of int to write to
target := [3]int{}
// write the answer
err := WriteAnswer(&target, "name", [3]string{"1", "2", "3"})
// make sure there was no error
assert.Nil(t, err, "WriteArray to Int Array")
// and we got what we wanted
assert.Equal(t, [3]int{1, 2, 3}, target)
}
func TestWriteAnswer_returnsErrWhenFieldNotFound(t *testing.T) {
// the struct to hold the answer
ptr := struct{ Name string }{}
// write a value to an existing field
err := WriteAnswer(&ptr, "", "world")
if err == nil {
// the test failed
t.Error("Did not encountered error while writing answer to non-existing field.")
}
}
func TestFindField_canFindExportedField(t *testing.T) {
// create a reflective wrapper over the struct to look through
val := reflect.ValueOf(struct{ Name string }{Name: "Jack"})
// find the field matching "name"
field, fieldType, err := findField(val, "name")
// if something went wrong
if err != nil {
// the test failed
t.Error(err.Error())
return
}
// make sure we got the right value
if field.Interface() != "Jack" {
// the test failed
t.Errorf("Did not find the correct field value. Expected 'Jack' found %v.", field.Interface())
}
// make sure we got the right field type
if fieldType.Name != "Name" {
// the test failed
t.Errorf("Did not find the correct field name. Expected 'Name' found %v.", fieldType.Name)
}
}
func TestFindField_canFindTaggedField(t *testing.T) {
// the struct to look through
val := reflect.ValueOf(struct {
Username string `survey:"name"`
}{
Username: "Jack",
})
// find the field matching "name"
field, fieldType, err := findField(val, "name")
// if something went wrong
if err != nil {
// the test failed
t.Error(err.Error())
return
}
// make sure we got the right value
if field.Interface() != "Jack" {
// the test failed
t.Errorf("Did not find the correct field value. Expected 'Jack' found %v.", field.Interface())
}
// make sure we got the right fieldType
if fieldType.Name != "Username" {
// the test failed
t.Errorf("Did not find the correct field name. Expected 'Username' found %v.", fieldType.Name)
}
}
func TestFindField_canHandleCapitalAnswerNames(t *testing.T) {
// create a reflective wrapper over the struct to look through
val := reflect.ValueOf(struct{ Name string }{Name: "Jack"})
// find the field matching "name"
field, fieldType, err := findField(val, "Name")
// if something went wrong
if err != nil {
// the test failed
t.Error(err.Error())
return
}
// make sure we got the right value
if field.Interface() != "Jack" {
// the test failed
t.Errorf("Did not find the correct field value. Expected 'Jack' found %v.", field.Interface())
}
// make sure we got the right fieldType
if fieldType.Name != "Name" {
// the test failed
t.Errorf("Did not find the correct field name. Expected 'Name' found %v.", fieldType.Name)
}
}
func TestFindField_tagOverwriteFieldName(t *testing.T) {
// the struct to look through
val := reflect.ValueOf(struct {
Name string
Username string `survey:"name"`
}{
Name: "Ralf",
Username: "Jack",
})
// find the field matching "name"
field, fieldType, err := findField(val, "name")
// if something went wrong
if err != nil {
// the test failed
t.Error(err.Error())
return
}
// make sure we got the right value
if field.Interface() != "Jack" {
// the test failed
t.Errorf("Did not find the correct field value. Expected 'Jack' found %v.", field.Interface())
}
// make sure we got the right fieldType
if fieldType.Name != "Username" {
// the test failed
t.Errorf("Did not find the correct field name. Expected 'Username' found %v.", fieldType.Name)
}
}
func TestFindField_supportsPromotedFields(t *testing.T) {
// create a reflective wrapper over the struct to look through
type Common struct {
Name string
}
type Strct struct {
Common // Name field added by composition
Username string
}
val := reflect.ValueOf(Strct{Common: Common{Name: "Jack"}})
// find the field matching "name"
field, fieldType, err := findField(val, "Name")
// if something went wrong
if err != nil {
// the test failed
t.Error(err.Error())
return
}
// make sure we got the right value
if field.Interface() != "Jack" {
// the test failed
t.Errorf("Did not find the correct field value. Expected 'Jack' found %v.", field.Interface())
}
// make sure we got the right fieldType
if fieldType.Name != "Name" {
// the test failed
t.Errorf("Did not find the correct field name. Expected 'Name' found %v.", fieldType.Name)
}
}
func TestFindField_promotedFieldsWithTag(t *testing.T) {
// create a reflective wrapper over the struct to look through
type Common struct {
Username string `survey:"name"`
}
type Strct struct {
Common // Name field added by composition
Name string
}
val := reflect.ValueOf(Strct{
Common: Common{Username: "Jack"},
Name: "Ralf",
})
// find the field matching "name"
field, fieldType, err := findField(val, "name")
// if something went wrong
if err != nil {
// the test failed
t.Error(err.Error())
return
}
// make sure we got the right value
if field.Interface() != "Jack" {
// the test failed
t.Errorf("Did not find the correct field value. Expected 'Jack' found %v.", field.Interface())
}
// make sure we got the right fieldType
if fieldType.Name != "Username" {
// the test failed
t.Errorf("Did not find the correct field name. Expected 'Username' found %v.", fieldType.Name)
}
}
func TestFindField_promotedFieldsDontHavePriorityOverTags(t *testing.T) {
// create a reflective wrapper over the struct to look through
type Common struct {
Name string
}
type Strct struct {
Common // Name field added by composition
Username string `survey:"name"`
}
val := reflect.ValueOf(Strct{
Common: Common{Name: "Ralf"},
Username: "Jack",
})
// find the field matching "name"
field, fieldType, err := findField(val, "name")
// if something went wrong
if err != nil {
// the test failed
t.Error(err.Error())
return
}
// make sure we got the right value
if field.Interface() != "Jack" {
// the test failed
t.Errorf("Did not find the correct field value. Expected 'Jack' found %v.", field.Interface())
}
// make sure we got the right fieldType
if fieldType.Name != "Username" {
// the test failed
t.Errorf("Did not find the correct field name. Expected 'Username' found %v.", fieldType.Name)
}
}
type testFieldSettable struct {
Values map[string]string
}
type testStringSettable struct {
Value string `survey:"string"`
}
type testTaggedStruct struct {
TaggedValue testStringSettable `survey:"tagged"`
}
type testPtrTaggedStruct struct {
TaggedValue *testStringSettable `survey:"tagged"`
}
func (t *testFieldSettable) WriteAnswer(name string, value interface{}) error {
if t.Values == nil {
t.Values = map[string]string{}
}
if v, ok := value.(string); ok {
t.Values[name] = v
return nil
}
return fmt.Errorf("Incompatible type %T", value)
}
func (t *testStringSettable) WriteAnswer(_ string, value interface{}) error {
t.Value = value.(string)
return nil
}
func TestWriteWithFieldSettable(t *testing.T) {
testSet1 := testFieldSettable{}
err := WriteAnswer(&testSet1, "values", "stringVal")
assert.Nil(t, err)
assert.Equal(t, map[string]string{"values": "stringVal"}, testSet1.Values)
testSet2 := testFieldSettable{}
err = WriteAnswer(&testSet2, "values", 123)
assert.Error(t, fmt.Errorf("Incompatible type int64"), err)
assert.Equal(t, map[string]string{}, testSet2.Values)
testString1 := testStringSettable{}
err = WriteAnswer(&testString1, "", "value1")
assert.Nil(t, err)
assert.Equal(t, testStringSettable{"value1"}, testString1)
testSetStruct := testTaggedStruct{}
err = WriteAnswer(&testSetStruct, "tagged", "stringVal1")
assert.Nil(t, err)
assert.Equal(t, testTaggedStruct{TaggedValue: testStringSettable{"stringVal1"}}, testSetStruct)
testPtrSetStruct := testPtrTaggedStruct{&testStringSettable{}}
err = WriteAnswer(&testPtrSetStruct, "tagged", "stringVal1")
assert.Nil(t, err)
assert.Equal(t, testPtrTaggedStruct{TaggedValue: &testStringSettable{"stringVal1"}}, testPtrSetStruct)
}
// CONVERSION TESTS
func TestWrite_canStringToBool(t *testing.T) {
// a pointer to hold the boolean value
ptr := true
// try to copy a false value to the pointer
err := WriteAnswer(&ptr, "", "false")
if err != nil {
t.Fatalf("WriteAnswer() = %v", err)
}
// if the value is true
if ptr {
// the test failed
t.Error("Could not convert string to pointer type")
}
}
func TestWrite_canStringToInt(t *testing.T) {
// a pointer to hold the value
var ptr int = 1
// try to copy a value to the pointer
err := WriteAnswer(&ptr, "", "2")
if err != nil {
t.Fatalf("WriteAnswer() = %v", err)
}
// if the value is true
if ptr != 2 {
// the test failed
t.Error("Could not convert string to pointer type")
}
}
func TestWrite_canStringToInt8(t *testing.T) {
// a pointer to hold the value
var ptr int8 = 1
// try to copy a value to the pointer
err := WriteAnswer(&ptr, "", "2")
if err != nil {
t.Fatalf("WriteAnswer() = %v", err)
}
// if the value is true
if ptr != 2 {
// the test failed
t.Error("Could not convert string to pointer type")
}
}
func TestWrite_canStringToInt16(t *testing.T) {
// a pointer to hold the value
var ptr int16 = 1
// try to copy a value to the pointer
err := WriteAnswer(&ptr, "", "2")
if err != nil {
t.Fatalf("WriteAnswer() = %v", err)
}
// if the value is true
if ptr != 2 {
// the test failed
t.Error("Could not convert string to pointer type")
}
}
func TestWrite_canStringToInt32(t *testing.T) {
// a pointer to hold the value
var ptr int32 = 1
// try to copy a value to the pointer
err := WriteAnswer(&ptr, "", "2")
if err != nil {
t.Fatalf("WriteAnswer() = %v", err)
}
// if the value is true
if ptr != 2 {
// the test failed
t.Error("Could not convert string to pointer type")
}
}
func TestWrite_canStringToInt64(t *testing.T) {
// a pointer to hold the value
var ptr int64 = 1
// try to copy a value to the pointer
err := WriteAnswer(&ptr, "", "2")
if err != nil {
t.Fatalf("WriteAnswer() = %v", err)
}
// if the value is true
if ptr != 2 {
// the test failed
t.Error("Could not convert string to pointer type")
}
}
func TestWrite_canStringToUint(t *testing.T) {
// a pointer to hold the value
var ptr uint = 1
// try to copy a value to the pointer
err := WriteAnswer(&ptr, "", "2")
if err != nil {
t.Fatalf("WriteAnswer() = %v", err)
}
// if the value is true
if ptr != 2 {
// the test failed
t.Error("Could not convert string to pointer type")
}
}
func TestWrite_canStringToUint8(t *testing.T) {
// a pointer to hold the value
var ptr uint8 = 1
// try to copy a value to the pointer
err := WriteAnswer(&ptr, "", "2")
if err != nil {
t.Fatalf("WriteAnswer() = %v", err)
}
// if the value is true
if ptr != 2 {
// the test failed
t.Error("Could not convert string to pointer type")
}
}
func TestWrite_canStringToUint16(t *testing.T) {
// a pointer to hold the value
var ptr uint16 = 1
// try to copy a value to the pointer
err := WriteAnswer(&ptr, "", "2")
if err != nil {
t.Fatalf("WriteAnswer() = %v", err)
}
// if the value is true
if ptr != 2 {
// the test failed
t.Error("Could not convert string to pointer type")
}
}
func TestWrite_canStringToUint32(t *testing.T) {
// a pointer to hold the value
var ptr uint32 = 1
// try to copy a value to the pointer
err := WriteAnswer(&ptr, "", "2")
if err != nil {
t.Fatalf("WriteAnswer() = %v", err)
}
// if the value is true
if ptr != 2 {
// the test failed
t.Error("Could not convert string to pointer type")
}
}
func TestWrite_canStringToUint64(t *testing.T) {
// a pointer to hold the value
var ptr uint64 = 1
// try to copy a value to the pointer
err := WriteAnswer(&ptr, "", "2")
if err != nil {
t.Fatalf("WriteAnswer() = %v", err)
}
// if the value is true
if ptr != 2 {
// the test failed
t.Error("Could not convert string to pointer type")
}
}
func TestWrite_canStringToFloat32(t *testing.T) {
// a pointer to hold the value
var ptr float32 = 1.0
// try to copy a value to the pointer
err := WriteAnswer(&ptr, "", "2.5")
if err != nil {
t.Fatalf("WriteAnswer() = %v", err)
}
// if the value is true
if ptr != 2.5 {
// the test failed
t.Error("Could not convert string to pointer type")
}
}
func TestWrite_canStringToFloat64(t *testing.T) {
// a pointer to hold the value
var ptr float64 = 1.0
// try to copy a value to the pointer
err := WriteAnswer(&ptr, "", "2.5")
if err != nil {
t.Fatalf("WriteAnswer() = %v", err)
}
// if the value is true
if ptr != 2.5 {
// the test failed
t.Error("Could not convert string to pointer type")
}
}
func TestWrite_canConvertStructFieldTypes(t *testing.T) {
// the struct to hold the answer
ptr := struct {
Name string
Age uint
Male bool
Height float64
Timeout time.Duration
}{}
// write the values as strings
check(t, WriteAnswer(&ptr, "name", "Bob"))
check(t, WriteAnswer(&ptr, "age", "22"))
check(t, WriteAnswer(&ptr, "male", "true"))
check(t, WriteAnswer(&ptr, "height", "6.2"))
check(t, WriteAnswer(&ptr, "timeout", "30s"))
// make sure we changed the fields
if ptr.Name != "Bob" {
t.Error("Did not mutate Name when writing answer.")
}
if ptr.Age != 22 {
t.Error("Did not mutate Age when writing answer.")
}
if !ptr.Male {
t.Error("Did not mutate Male when writing answer.")
}
if ptr.Height != 6.2 {
t.Error("Did not mutate Height when writing answer.")
}
if ptr.Timeout != time.Duration(30*time.Second) {
t.Error("Did not mutate Timeout when writing answer.")
}
}
func check(t *testing.T, err error) {
if err != nil {
t.Fatalf("Encountered error while writing answer: %v", err.Error())
}
}
survey-2.3.7/editor.go 0000664 0000000 0000000 00000012366 14344402622 0014706 0 ustar 00root root 0000000 0000000 package survey
import (
"bytes"
"io/ioutil"
"os"
"os/exec"
"runtime"
"github.com/AlecAivazis/survey/v2/terminal"
shellquote "github.com/kballard/go-shellquote"
)
/*
Editor launches an instance of the users preferred editor on a temporary file.
The editor to use is determined by reading the $VISUAL or $EDITOR environment
variables. If neither of those are present, notepad (on Windows) or vim
(others) is used.
The launch of the editor is triggered by the enter key. Since the response may
be long, it will not be echoed as Input does, instead, it print .
Response type is a string.
message := ""
prompt := &survey.Editor{ Message: "What is your commit message?" }
survey.AskOne(prompt, &message)
*/
type Editor struct {
Renderer
Message string
Default string
Help string
Editor string
HideDefault bool
AppendDefault bool
FileName string
}
// data available to the templates when processing
type EditorTemplateData struct {
Editor
Answer string
ShowAnswer bool
ShowHelp bool
Config *PromptConfig
}
// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
var EditorQuestionTemplate = `
{{- 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 .ShowAnswer}}
{{- color "cyan"}}{{.Answer}}{{color "reset"}}{{"\n"}}
{{- else }}
{{- if and .Help (not .ShowHelp)}}{{color "cyan"}}[{{ .Config.HelpInput }} for help]{{color "reset"}} {{end}}
{{- if and .Default (not .HideDefault)}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}}
{{- color "cyan"}}[Enter to launch editor] {{color "reset"}}
{{- end}}`
var (
bom = []byte{0xef, 0xbb, 0xbf}
editor = "vim"
)
func init() {
if runtime.GOOS == "windows" {
editor = "notepad"
}
if v := os.Getenv("VISUAL"); v != "" {
editor = v
} else if e := os.Getenv("EDITOR"); e != "" {
editor = e
}
}
func (e *Editor) PromptAgain(config *PromptConfig, invalid interface{}, err error) (interface{}, error) {
initialValue := invalid.(string)
return e.prompt(initialValue, config)
}
func (e *Editor) Prompt(config *PromptConfig) (interface{}, error) {
initialValue := ""
if e.Default != "" && e.AppendDefault {
initialValue = e.Default
}
return e.prompt(initialValue, config)
}
func (e *Editor) prompt(initialValue string, config *PromptConfig) (interface{}, error) {
// render the template
err := e.Render(
EditorQuestionTemplate,
EditorTemplateData{
Editor: *e,
Config: config,
},
)
if err != nil {
return "", err
}
// start reading runes from the standard in
rr := e.NewRuneReader()
_ = rr.SetTermMode()
defer func() {
_ = rr.RestoreTermMode()
}()
cursor := e.NewCursor()
cursor.Hide()
defer cursor.Show()
for {
r, _, err := rr.ReadRune()
if err != nil {
return "", err
}
if r == '\r' || r == '\n' {
break
}
if r == terminal.KeyInterrupt {
return "", terminal.InterruptErr
}
if r == terminal.KeyEndTransmission {
break
}
if string(r) == config.HelpInput && e.Help != "" {
err = e.Render(
EditorQuestionTemplate,
EditorTemplateData{
Editor: *e,
ShowHelp: true,
Config: config,
},
)
if err != nil {
return "", err
}
}
continue
}
// prepare the temp file
pattern := e.FileName
if pattern == "" {
pattern = "survey*.txt"
}
f, err := ioutil.TempFile("", pattern)
if err != nil {
return "", err
}
defer func() {
_ = os.Remove(f.Name())
}()
// write utf8 BOM header
// The reason why we do this is because notepad.exe on Windows determines the
// encoding of an "empty" text file by the locale, for example, GBK in China,
// while golang string only handles utf8 well. However, a text file with utf8
// BOM header is not considered "empty" on Windows, and the encoding will then
// be determined utf8 by notepad.exe, instead of GBK or other encodings.
if _, err := f.Write(bom); err != nil {
return "", err
}
// write initial value
if _, err := f.WriteString(initialValue); err != nil {
return "", err
}
// close the fd to prevent the editor unable to save file
if err := f.Close(); err != nil {
return "", err
}
// check is input editor exist
if e.Editor != "" {
editor = e.Editor
}
stdio := e.Stdio()
args, err := shellquote.Split(editor)
if err != nil {
return "", err
}
args = append(args, f.Name())
// open the editor
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdin = stdio.In
cmd.Stdout = stdio.Out
cmd.Stderr = stdio.Err
cursor.Show()
if err := cmd.Run(); err != nil {
return "", err
}
// raw is a BOM-unstripped UTF8 byte slice
raw, err := ioutil.ReadFile(f.Name())
if err != nil {
return "", err
}
// strip BOM header
text := string(bytes.TrimPrefix(raw, bom))
// check length, return default value on empty
if len(text) == 0 && !e.AppendDefault {
return e.Default, nil
}
return text, nil
}
func (e *Editor) Cleanup(config *PromptConfig, val interface{}) error {
return e.Render(
EditorQuestionTemplate,
EditorTemplateData{
Editor: *e,
Answer: "",
ShowAnswer: true,
Config: config,
},
)
}
survey-2.3.7/editor_test.go 0000664 0000000 0000000 00000015372 14344402622 0015745 0 ustar 00root root 0000000 0000000 package survey
import (
"bytes"
"fmt"
"io"
"os"
"os/exec"
"testing"
"time"
"github.com/AlecAivazis/survey/v2/core"
"github.com/AlecAivazis/survey/v2/terminal"
"github.com/stretchr/testify/assert"
)
func init() {
// disable color output for all prompts to simplify testing
core.DisableColor = true
}
func TestEditorRender(t *testing.T) {
tests := []struct {
title string
prompt Editor
data EditorTemplateData
expected string
}{
{
"Test Editor question output without default",
Editor{Message: "What is your favorite month:"},
EditorTemplateData{},
fmt.Sprintf("%s What is your favorite month: [Enter to launch editor] ", defaultIcons().Question.Text),
},
{
"Test Editor question output with default",
Editor{Message: "What is your favorite month:", Default: "April"},
EditorTemplateData{},
fmt.Sprintf("%s What is your favorite month: (April) [Enter to launch editor] ", defaultIcons().Question.Text),
},
{
"Test Editor question output with HideDefault",
Editor{Message: "What is your favorite month:", Default: "April", HideDefault: true},
EditorTemplateData{},
fmt.Sprintf("%s What is your favorite month: [Enter to launch editor] ", defaultIcons().Question.Text),
},
{
"Test Editor answer output",
Editor{Message: "What is your favorite month:"},
EditorTemplateData{Answer: "October", ShowAnswer: true},
fmt.Sprintf("%s What is your favorite month: October\n", defaultIcons().Question.Text),
},
{
"Test Editor question output without default but with help hidden",
Editor{Message: "What is your favorite month:", Help: "This is helpful"},
EditorTemplateData{},
fmt.Sprintf("%s What is your favorite month: [%s for help] [Enter to launch editor] ", defaultIcons().Question.Text, string(defaultPromptConfig().HelpInput)),
},
{
"Test Editor question output with default and with help hidden",
Editor{Message: "What is your favorite month:", Default: "April", Help: "This is helpful"},
EditorTemplateData{},
fmt.Sprintf("%s What is your favorite month: [%s for help] (April) [Enter to launch editor] ", defaultIcons().Question.Text, string(defaultPromptConfig().HelpInput)),
},
{
"Test Editor question output without default but with help shown",
Editor{Message: "What is your favorite month:", Help: "This is helpful"},
EditorTemplateData{ShowHelp: true},
fmt.Sprintf("%s This is helpful\n%s What is your favorite month: [Enter to launch editor] ", defaultIcons().Help.Text, defaultIcons().Question.Text),
},
{
"Test Editor question output with default and with help shown",
Editor{Message: "What is your favorite month:", Default: "April", Help: "This is helpful"},
EditorTemplateData{ShowHelp: true},
fmt.Sprintf("%s This is helpful\n%s What is your favorite month: (April) [Enter to launch editor] ", defaultIcons().Help.Text, defaultIcons().Question.Text),
},
}
for _, test := range tests {
t.Run(test.title, func(t *testing.T) {
r, w, err := os.Pipe()
assert.NoError(t, err)
test.prompt.WithStdio(terminal.Stdio{Out: w})
test.data.Editor = test.prompt
// set the icon set
test.data.Config = defaultPromptConfig()
err = test.prompt.Render(
EditorQuestionTemplate,
test.data,
)
assert.NoError(t, err)
assert.NoError(t, w.Close())
var buf bytes.Buffer
_, err = io.Copy(&buf, r)
assert.NoError(t, err)
assert.Contains(t, buf.String(), test.expected)
})
}
}
func TestEditorPrompt(t *testing.T) {
if _, err := exec.LookPath("vi"); err != nil {
t.Skip("warning: vi not found in PATH")
}
tests := []PromptTest{
{
"Test Editor prompt interaction",
&Editor{
Editor: "vi",
Message: "Edit git commit message",
},
func(c expectConsole) {
c.ExpectString("Edit git commit message [Enter to launch editor]")
c.SendLine("")
time.Sleep(time.Millisecond)
c.Send("ccAdd editor prompt tests\x1b")
c.SendLine(":wq!")
c.ExpectEOF()
},
"Add editor prompt tests\n",
},
{
"Test Editor prompt interaction with default",
&Editor{
Editor: "vi",
Message: "Edit git commit message",
Default: "No comment",
},
func(c expectConsole) {
c.ExpectString("Edit git commit message (No comment) [Enter to launch editor]")
c.SendLine("")
time.Sleep(time.Millisecond)
c.SendLine(":q!")
c.ExpectEOF()
},
"No comment",
},
{
"Test Editor prompt interaction overriding default",
&Editor{
Editor: "vi",
Message: "Edit git commit message",
Default: "No comment",
},
func(c expectConsole) {
c.ExpectString("Edit git commit message (No comment) [Enter to launch editor]")
c.SendLine("")
time.Sleep(time.Millisecond)
c.Send("ccAdd editor prompt tests\x1b")
c.SendLine(":wq!")
c.ExpectEOF()
},
"Add editor prompt tests\n",
},
{
"Test Editor prompt interaction hiding default",
&Editor{
Editor: "vi",
Message: "Edit git commit message",
Default: "No comment",
HideDefault: true,
},
func(c expectConsole) {
c.ExpectString("Edit git commit message [Enter to launch editor]")
c.SendLine("")
time.Sleep(time.Millisecond)
c.SendLine(":q!")
c.ExpectEOF()
},
"No comment",
},
{
"Test Editor prompt interaction and prompt for help",
&Editor{
Editor: "vi",
Message: "Edit git commit message",
Help: "Describe your git commit",
},
func(c expectConsole) {
c.ExpectString(
fmt.Sprintf(
"Edit git commit message [%s for help] [Enter to launch editor]",
string(defaultPromptConfig().HelpInput),
),
)
c.SendLine("?")
c.ExpectString("Describe your git commit")
c.SendLine("")
time.Sleep(time.Millisecond)
c.Send("ccAdd editor prompt tests\x1b")
c.SendLine(":wq!")
c.ExpectEOF()
},
"Add editor prompt tests\n",
},
{
"Test Editor prompt interaction with default and append default",
&Editor{
Editor: "vi",
Message: "Edit git commit message",
Default: "No comment",
AppendDefault: true,
},
func(c expectConsole) {
c.ExpectString("Edit git commit message (No comment) [Enter to launch editor]")
c.SendLine("")
c.ExpectString("No comment")
c.SendLine("dd")
c.SendLine(":wq!")
c.ExpectEOF()
},
"",
},
{
"Test Editor prompt interaction with editor args",
&Editor{
Editor: "vi --",
Message: "Edit git commit message",
},
func(c expectConsole) {
c.ExpectString("Edit git commit message [Enter to launch editor]")
c.SendLine("")
time.Sleep(time.Millisecond)
c.Send("ccAdd editor prompt tests\x1b")
c.SendLine(":wq!")
c.ExpectEOF()
},
"Add editor prompt tests\n",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
RunPromptTest(t, test)
})
}
}
survey-2.3.7/examples/ 0000775 0000000 0000000 00000000000 14344402622 0014677 5 ustar 00root root 0000000 0000000 survey-2.3.7/examples/countrylist.go 0000664 0000000 0000000 00000012070 14344402622 0017625 0 ustar 00root root 0000000 0000000 //go:build ignore
package main
import (
"fmt"
"github.com/AlecAivazis/survey/v2"
)
// the questions to ask
var countryQs = []*survey.Question{
{
Name: "country",
Prompt: &survey.Select{
Message: "Choose a country:",
Options: []string{
"Afghanistan",
"Åland Islands",
"Albania",
"Algeria",
"American Samoa",
"AndorrA",
"Angola",
"Anguilla",
"Antarctica",
"Antigua and Barbuda",
"Argentina",
"Armenia",
"Aruba",
"Australia",
"Austria",
"Azerbaijan",
"Bahamas",
"Bahrain",
"Bangladesh",
"Barbados",
"Belarus",
"Belgium",
"Belize",
"Benin",
"Bermuda",
"Bhutan",
"Bolivia",
"Bosnia and Herzegovina",
"Botswana",
"Bouvet Island",
"Brazil",
"British Indian Ocean Territory",
"Brunei Darussalam",
"Bulgaria",
"Burkina Faso",
"Burundi",
"Cambodia",
"Cameroon",
"Canada",
"Cape Verde",
"Cayman Islands",
"Central African Republic",
"Chad",
"Chile",
"China",
"Christmas Island",
"Cocos (Keeling) Islands",
"Colombia",
"Comoros",
"Congo",
"Congo, The Democratic Republic of the",
"Cook Islands",
"Costa Rica",
"Cote D'Ivoire",
"Croatia",
"Cuba",
"Cyprus",
"Czech Republic",
"Denmark",
"Djibouti",
"Dominica",
"Dominican Republic",
"Ecuador",
"Egypt",
"El Salvador",
"Equatorial Guinea",
"Eritrea",
"Estonia",
"Ethiopia",
"Falkland Islands (Malvinas)",
"Faroe Islands",
"Fiji",
"Finland",
"France",
"French Guiana",
"French Polynesia",
"French Southern Territories",
"Gabon",
"Gambia",
"Georgia",
"Germany",
"Ghana",
"Gibraltar",
"Greece",
"Greenland",
"Grenada",
"Guadeloupe",
"Guam",
"Guatemala",
"Guernsey",
"Guinea",
"Guinea-Bissau",
"Guyana",
"Haiti",
"Heard Island and Mcdonald Islands",
"Holy See (Vatican City State)",
"Honduras",
"Hong Kong",
"Hungary",
"Iceland",
"India",
"Indonesia",
"Iran, Islamic Republic Of",
"Iraq",
"Ireland",
"Isle of Man",
"Israel",
"Italy",
"Jamaica",
"Japan",
"Jersey",
"Jordan",
"Kazakhstan",
"Kenya",
"Kiribati",
"Korea, Democratic People'S Republic of",
"Korea, Republic of",
"Kuwait",
"Kyrgyzstan",
"Lao People'S Democratic Republic",
"Latvia",
"Lebanon",
"Lesotho",
"Liberia",
"Libyan Arab Jamahiriya",
"Liechtenstein",
"Lithuania",
"Luxembourg",
"Macao",
"Macedonia, The Former Yugoslav Republic of",
"Madagascar",
"Malawi",
"Malaysia",
"Maldives",
"Mali",
"Malta",
"Marshall Islands",
"Martinique",
"Mauritania",
"Mauritius",
"Mayotte",
"Mexico",
"Micronesia, Federated States of",
"Moldova, Republic of",
"Monaco",
"Mongolia",
"Montserrat",
"Morocco",
"Mozambique",
"Myanmar",
"Namibia",
"Nauru",
"Nepal",
"Netherlands",
"Netherlands Antilles",
"New Caledonia",
"New Zealand",
"Nicaragua",
"Niger",
"Nigeria",
"Niue",
"Norfolk Island",
"Northern Mariana Islands",
"Norway",
"Oman",
"Pakistan",
"Palau",
"Palestinian Territory, Occupied",
"Panama",
"Papua New Guinea",
"Paraguay",
"Peru",
"Philippines",
"Pitcairn",
"Poland",
"Portugal",
"Puerto Rico",
"Qatar",
"Reunion",
"Romania",
"Russian Federation",
"RWANDA",
"Saint Helena",
"Saint Kitts and Nevis",
"Saint Lucia",
"Saint Pierre and Miquelon",
"Saint Vincent and the Grenadines",
"Samoa",
"San Marino",
"Sao Tome and Principe",
"Saudi Arabia",
"Senegal",
"Serbia and Montenegro",
"Seychelles",
"Sierra Leone",
"Singapore",
"Slovakia",
"Slovenia",
"Solomon Islands",
"Somalia",
"South Africa",
"South Georgia and the South Sandwich Islands",
"Spain",
"Sri Lanka",
"Sudan",
"Suriname",
"Svalbard and Jan Mayen",
"Swaziland",
"Sweden",
"Switzerland",
"Syrian Arab Republic",
"Taiwan, Province of China",
"Tajikistan",
"Tanzania, United Republic of",
"Thailand",
"Timor-Leste",
"Togo",
"Tokelau",
"Tonga",
"Trinidad and Tobago",
"Tunisia",
"Turkey",
"Turkmenistan",
"Turks and Caicos Islands",
"Tuvalu",
"Uganda",
"Ukraine",
"United Arab Emirates",
"United Kingdom",
"United States",
"United States Minor Outlying Islands",
"Uruguay",
"Uzbekistan",
"Vanuatu",
"Venezuela",
"Viet Nam",
"Virgin Islands, British",
"Virgin Islands, U.S.",
"Wallis and Futuna",
"Western Sahara",
"Yemen",
"Zambia",
"Zimbabwe",
},
},
Validate: survey.Required,
},
}
func main() {
answers := struct {
Country string
}{}
// ask the question
err := survey.Ask(countryQs, &answers)
if err != nil {
fmt.Println(err.Error())
return
}
// print the answers
fmt.Printf("you chose %s.\n", answers.Country)
}
survey-2.3.7/examples/cursor.go 0000664 0000000 0000000 00000001035 14344402622 0016542 0 ustar 00root root 0000000 0000000 //go:build ignore
package main
import (
"fmt"
"github.com/AlecAivazis/survey/v2"
)
// the questions to ask
var simpleQs = []*survey.Question{
{
Name: "name",
Prompt: &survey.Input{
Message: "What is your name?",
},
Validate: survey.Required,
},
}
func main() {
ansmap := make(map[string]interface{})
// ask the question
err := survey.Ask(simpleQs, &ansmap, survey.WithShowCursor(true))
if err != nil {
fmt.Println(err.Error())
return
}
// print the answers
fmt.Printf("Your name is %s.\n", ansmap["name"])
}
survey-2.3.7/examples/inputfilesuggestion.go 0000664 0000000 0000000 00000001303 14344402622 0021332 0 ustar 00root root 0000000 0000000 //go:build ignore
package main
import (
"fmt"
"path/filepath"
"github.com/AlecAivazis/survey/v2"
)
func suggestFiles(toComplete string) []string {
files, _ := filepath.Glob(toComplete + "*")
return files
}
// the questions to ask
var q = []*survey.Question{
{
Name: "file",
Prompt: &survey.Input{
Message: "Which file should be read?",
Suggest: suggestFiles,
Help: "Any file; do not need to exist yet",
},
Validate: survey.Required,
},
}
func main() {
answers := struct {
File string
}{}
// ask the question
err := survey.Ask(q, &answers)
if err != nil {
fmt.Println(err.Error())
return
}
// print the answers
fmt.Printf("File chosen %s.\n", answers.File)
}
survey-2.3.7/examples/longlist.go 0000664 0000000 0000000 00000001167 14344402622 0017066 0 ustar 00root root 0000000 0000000 //go:build ignore
package main
import (
"fmt"
"github.com/AlecAivazis/survey/v2"
)
// the questions to ask
var simpleQs = []*survey.Question{
{
Name: "letter",
Prompt: &survey.Select{
Message: "Choose a letter:",
Options: []string{
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
},
},
Validate: survey.Required,
},
}
func main() {
answers := struct {
Letter string
}{}
// ask the question
err := survey.Ask(simpleQs, &answers)
if err != nil {
fmt.Println(err.Error())
return
}
// print the answers
fmt.Printf("you chose %s.\n", answers.Letter)
}
survey-2.3.7/examples/longmulti.go 0000664 0000000 0000000 00000012056 14344402622 0017244 0 ustar 00root root 0000000 0000000 //go:build ignore
package main
import (
"fmt"
"strings"
survey "github.com/AlecAivazis/survey/v2"
)
// the questions to ask
var multiQs = []*survey.Question{
{
Name: "letter",
Prompt: &survey.MultiSelect{
Message: "Choose one or more words :",
Options: []string{
"Afghanistan",
"Åland Islands",
"Albania",
"Algeria",
"American Samoa",
"AndorrA",
"Angola",
"Anguilla",
"Antarctica",
"Antigua and Barbuda",
"Argentina",
"Armenia",
"Aruba",
"Australia",
"Austria",
"Azerbaijan",
"Bahamas",
"Bahrain",
"Bangladesh",
"Barbados",
"Belarus",
"Belgium",
"Belize",
"Benin",
"Bermuda",
"Bhutan",
"Bolivia",
"Bosnia and Herzegovina",
"Botswana",
"Bouvet Island",
"Brazil",
"British Indian Ocean Territory",
"Brunei Darussalam",
"Bulgaria",
"Burkina Faso",
"Burundi",
"Cambodia",
"Cameroon",
"Canada",
"Cape Verde",
"Cayman Islands",
"Central African Republic",
"Chad",
"Chile",
"China",
"Christmas Island",
"Cocos (Keeling) Islands",
"Colombia",
"Comoros",
"Congo",
"Congo, The Democratic Republic of the",
"Cook Islands",
"Costa Rica",
"Cote D'Ivoire",
"Croatia",
"Cuba",
"Cyprus",
"Czech Republic",
"Denmark",
"Djibouti",
"Dominica",
"Dominican Republic",
"Ecuador",
"Egypt",
"El Salvador",
"Equatorial Guinea",
"Eritrea",
"Estonia",
"Ethiopia",
"Falkland Islands (Malvinas)",
"Faroe Islands",
"Fiji",
"Finland",
"France",
"French Guiana",
"French Polynesia",
"French Southern Territories",
"Gabon",
"Gambia",
"Georgia",
"Germany",
"Ghana",
"Gibraltar",
"Greece",
"Greenland",
"Grenada",
"Guadeloupe",
"Guam",
"Guatemala",
"Guernsey",
"Guinea",
"Guinea-Bissau",
"Guyana",
"Haiti",
"Heard Island and Mcdonald Islands",
"Holy See (Vatican City State)",
"Honduras",
"Hong Kong",
"Hungary",
"Iceland",
"India",
"Indonesia",
"Iran, Islamic Republic Of",
"Iraq",
"Ireland",
"Isle of Man",
"Israel",
"Italy",
"Jamaica",
"Japan",
"Jersey",
"Jordan",
"Kazakhstan",
"Kenya",
"Kiribati",
"Korea, Democratic People'S Republic of",
"Korea, Republic of",
"Kuwait",
"Kyrgyzstan",
"Lao People'S Democratic Republic",
"Latvia",
"Lebanon",
"Lesotho",
"Liberia",
"Libyan Arab Jamahiriya",
"Liechtenstein",
"Lithuania",
"Luxembourg",
"Macao",
"Macedonia, The Former Yugoslav Republic of",
"Madagascar",
"Malawi",
"Malaysia",
"Maldives",
"Mali",
"Malta",
"Marshall Islands",
"Martinique",
"Mauritania",
"Mauritius",
"Mayotte",
"Mexico",
"Micronesia, Federated States of",
"Moldova, Republic of",
"Monaco",
"Mongolia",
"Montserrat",
"Morocco",
"Mozambique",
"Myanmar",
"Namibia",
"Nauru",
"Nepal",
"Netherlands",
"Netherlands Antilles",
"New Caledonia",
"New Zealand",
"Nicaragua",
"Niger",
"Nigeria",
"Niue",
"Norfolk Island",
"Northern Mariana Islands",
"Norway",
"Oman",
"Pakistan",
"Palau",
"Palestinian Territory, Occupied",
"Panama",
"Papua New Guinea",
"Paraguay",
"Peru",
"Philippines",
"Pitcairn",
"Poland",
"Portugal",
"Puerto Rico",
"Qatar",
"Reunion",
"Romania",
"Russian Federation",
"RWANDA",
"Saint Helena",
"Saint Kitts and Nevis",
"Saint Lucia",
"Saint Pierre and Miquelon",
"Saint Vincent and the Grenadines",
"Samoa",
"San Marino",
"Sao Tome and Principe",
"Saudi Arabia",
"Senegal",
"Serbia and Montenegro",
"Seychelles",
"Sierra Leone",
"Singapore",
"Slovakia",
"Slovenia",
"Solomon Islands",
"Somalia",
"South Africa",
"South Georgia and the South Sandwich Islands",
"Spain",
"Sri Lanka",
"Sudan",
"Suriname",
"Svalbard and Jan Mayen",
"Swaziland",
"Sweden",
"Switzerland",
"Syrian Arab Republic",
"Taiwan, Province of China",
"Tajikistan",
"Tanzania, United Republic of",
"Thailand",
"Timor-Leste",
"Togo",
"Tokelau",
"Tonga",
"Trinidad and Tobago",
"Tunisia",
"Turkey",
"Turkmenistan",
"Turks and Caicos Islands",
"Tuvalu",
"Uganda",
"Ukraine",
"United Arab Emirates",
"United Kingdom",
"United States",
"United States Minor Outlying Islands",
"Uruguay",
"Uzbekistan",
"Vanuatu",
"Venezuela",
"Viet Nam",
"Virgin Islands, British",
"Virgin Islands, U.S.",
"Wallis and Futuna",
"Western Sahara",
"Yemen",
"Zambia",
"Zimbabwe",
},
},
},
}
func main() {
answers := []string{}
// ask the question
err := survey.Ask(multiQs, &answers)
if err != nil {
fmt.Println(err.Error())
return
}
// print the answers
fmt.Printf("you chose: %s\n", strings.Join(answers, ", "))
}
survey-2.3.7/examples/longmultikeepfilter.go 0000664 0000000 0000000 00000012113 14344402622 0021311 0 ustar 00root root 0000000 0000000 //go:build ignore
package main
import (
"fmt"
"strings"
survey "github.com/AlecAivazis/survey/v2"
)
// the questions to ask
var multiQs = []*survey.Question{
{
Name: "letter",
Prompt: &survey.MultiSelect{
Message: "Choose one or more words :",
Options: []string{
"Afghanistan",
"Åland Islands",
"Albania",
"Algeria",
"American Samoa",
"AndorrA",
"Angola",
"Anguilla",
"Antarctica",
"Antigua and Barbuda",
"Argentina",
"Armenia",
"Aruba",
"Australia",
"Austria",
"Azerbaijan",
"Bahamas",
"Bahrain",
"Bangladesh",
"Barbados",
"Belarus",
"Belgium",
"Belize",
"Benin",
"Bermuda",
"Bhutan",
"Bolivia",
"Bosnia and Herzegovina",
"Botswana",
"Bouvet Island",
"Brazil",
"British Indian Ocean Territory",
"Brunei Darussalam",
"Bulgaria",
"Burkina Faso",
"Burundi",
"Cambodia",
"Cameroon",
"Canada",
"Cape Verde",
"Cayman Islands",
"Central African Republic",
"Chad",
"Chile",
"China",
"Christmas Island",
"Cocos (Keeling) Islands",
"Colombia",
"Comoros",
"Congo",
"Congo, The Democratic Republic of the",
"Cook Islands",
"Costa Rica",
"Cote D'Ivoire",
"Croatia",
"Cuba",
"Cyprus",
"Czech Republic",
"Denmark",
"Djibouti",
"Dominica",
"Dominican Republic",
"Ecuador",
"Egypt",
"El Salvador",
"Equatorial Guinea",
"Eritrea",
"Estonia",
"Ethiopia",
"Falkland Islands (Malvinas)",
"Faroe Islands",
"Fiji",
"Finland",
"France",
"French Guiana",
"French Polynesia",
"French Southern Territories",
"Gabon",
"Gambia",
"Georgia",
"Germany",
"Ghana",
"Gibraltar",
"Greece",
"Greenland",
"Grenada",
"Guadeloupe",
"Guam",
"Guatemala",
"Guernsey",
"Guinea",
"Guinea-Bissau",
"Guyana",
"Haiti",
"Heard Island and Mcdonald Islands",
"Holy See (Vatican City State)",
"Honduras",
"Hong Kong",
"Hungary",
"Iceland",
"India",
"Indonesia",
"Iran, Islamic Republic Of",
"Iraq",
"Ireland",
"Isle of Man",
"Israel",
"Italy",
"Jamaica",
"Japan",
"Jersey",
"Jordan",
"Kazakhstan",
"Kenya",
"Kiribati",
"Korea, Democratic People'S Republic of",
"Korea, Republic of",
"Kuwait",
"Kyrgyzstan",
"Lao People'S Democratic Republic",
"Latvia",
"Lebanon",
"Lesotho",
"Liberia",
"Libyan Arab Jamahiriya",
"Liechtenstein",
"Lithuania",
"Luxembourg",
"Macao",
"Macedonia, The Former Yugoslav Republic of",
"Madagascar",
"Malawi",
"Malaysia",
"Maldives",
"Mali",
"Malta",
"Marshall Islands",
"Martinique",
"Mauritania",
"Mauritius",
"Mayotte",
"Mexico",
"Micronesia, Federated States of",
"Moldova, Republic of",
"Monaco",
"Mongolia",
"Montserrat",
"Morocco",
"Mozambique",
"Myanmar",
"Namibia",
"Nauru",
"Nepal",
"Netherlands",
"Netherlands Antilles",
"New Caledonia",
"New Zealand",
"Nicaragua",
"Niger",
"Nigeria",
"Niue",
"Norfolk Island",
"Northern Mariana Islands",
"Norway",
"Oman",
"Pakistan",
"Palau",
"Palestinian Territory, Occupied",
"Panama",
"Papua New Guinea",
"Paraguay",
"Peru",
"Philippines",
"Pitcairn",
"Poland",
"Portugal",
"Puerto Rico",
"Qatar",
"Reunion",
"Romania",
"Russian Federation",
"RWANDA",
"Saint Helena",
"Saint Kitts and Nevis",
"Saint Lucia",
"Saint Pierre and Miquelon",
"Saint Vincent and the Grenadines",
"Samoa",
"San Marino",
"Sao Tome and Principe",
"Saudi Arabia",
"Senegal",
"Serbia and Montenegro",
"Seychelles",
"Sierra Leone",
"Singapore",
"Slovakia",
"Slovenia",
"Solomon Islands",
"Somalia",
"South Africa",
"South Georgia and the South Sandwich Islands",
"Spain",
"Sri Lanka",
"Sudan",
"Suriname",
"Svalbard and Jan Mayen",
"Swaziland",
"Sweden",
"Switzerland",
"Syrian Arab Republic",
"Taiwan, Province of China",
"Tajikistan",
"Tanzania, United Republic of",
"Thailand",
"Timor-Leste",
"Togo",
"Tokelau",
"Tonga",
"Trinidad and Tobago",
"Tunisia",
"Turkey",
"Turkmenistan",
"Turks and Caicos Islands",
"Tuvalu",
"Uganda",
"Ukraine",
"United Arab Emirates",
"United Kingdom",
"United States",
"United States Minor Outlying Islands",
"Uruguay",
"Uzbekistan",
"Vanuatu",
"Venezuela",
"Viet Nam",
"Virgin Islands, British",
"Virgin Islands, U.S.",
"Wallis and Futuna",
"Western Sahara",
"Yemen",
"Zambia",
"Zimbabwe",
},
},
},
}
func main() {
answers := []string{}
// ask the question
err := survey.Ask(multiQs, &answers, survey.WithKeepFilter(true))
if err != nil {
fmt.Println(err.Error())
return
}
// print the answers
fmt.Printf("you chose: %s\n", strings.Join(answers, ", "))
}
survey-2.3.7/examples/map.go 0000664 0000000 0000000 00000001256 14344402622 0016007 0 ustar 00root root 0000000 0000000 //go:build ignore
package main
import (
"fmt"
"github.com/AlecAivazis/survey/v2"
)
// the questions to ask
var simpleQs = []*survey.Question{
{
Name: "name",
Prompt: &survey.Input{
Message: "What is your name?",
},
Validate: survey.Required,
},
{
Name: "color",
Prompt: &survey.Select{
Message: "Choose a color:",
Options: []string{"red", "blue", "green"},
},
Validate: survey.Required,
},
}
func main() {
ansmap := make(map[string]interface{})
// ask the question
err := survey.Ask(simpleQs, &ansmap)
if err != nil {
fmt.Println(err.Error())
return
}
// print the answers
fmt.Printf("%s chose %s.\n", ansmap["name"], ansmap["color"])
}
survey-2.3.7/examples/password.go 0000664 0000000 0000000 00000001466 14344402622 0017077 0 ustar 00root root 0000000 0000000 //go:build ignore
package main
import (
"fmt"
"github.com/AlecAivazis/survey/v2"
)
// the questions to ask
var defaultPasswordCharacterPrompt = &survey.Password{
Message: "What is your password? (Default hide character)",
}
var customPasswordCharacterPrompt = &survey.Password{
Message: "What is your password? (Custom hide character)",
}
func main() {
var defaultPass string
var customPass string
// ask the question
err := survey.AskOne(defaultPasswordCharacterPrompt, &defaultPass)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println()
err = survey.AskOne(customPasswordCharacterPrompt, &customPass, survey.WithHideCharacter('-'))
if err != nil {
fmt.Println(err.Error())
return
}
// print the answers
fmt.Printf("Password 1: %s.\n Password 2: %s\n", defaultPass, customPass)
}
survey-2.3.7/examples/select_description.go 0000664 0000000 0000000 00000001544 14344402622 0021114 0 ustar 00root root 0000000 0000000 //go:build ignore
package main
import (
"fmt"
"github.com/AlecAivazis/survey/v2"
)
type Meal struct {
Title string
Comment string
}
func main() {
var meals = []Meal{
{Title: "Bread", Comment: "Contains gluten"},
{Title: "Eggs", Comment: "Free-range"},
{Title: "Apple", Comment: ""},
{Title: "Burger", Comment: "Veggie patties available"},
}
titles := make([]string, len(meals))
for i, m := range meals {
titles[i] = m.Title
}
var qs = &survey.Select{
Message: "Choose a meal:",
Options: titles,
Description: func(value string, index int) string {
return meals[index].Comment
},
}
answerIndex := 0
// ask the question
err := survey.AskOne(qs, &answerIndex)
if err != nil {
fmt.Println(err.Error())
return
}
meal := meals[answerIndex]
// print the answers
fmt.Printf("you picked %s, nice choice.\n", meal.Title)
}
survey-2.3.7/examples/simple.go 0000664 0000000 0000000 00000001327 14344402622 0016522 0 ustar 00root root 0000000 0000000 //go:build ignore
package main
import (
"fmt"
"github.com/AlecAivazis/survey/v2"
)
// the questions to ask
var simpleQs = []*survey.Question{
{
Name: "name",
Prompt: &survey.Input{
Message: "What is your name?",
},
Validate: survey.Required,
Transform: survey.Title,
},
{
Name: "color",
Prompt: &survey.Select{
Message: "Choose a color:",
Options: []string{"red", "blue", "green"},
},
Validate: survey.Required,
},
}
func main() {
answers := struct {
Name string
Color string
}{}
// ask the question
err := survey.Ask(simpleQs, &answers)
if err != nil {
fmt.Println(err.Error())
return
}
// print the answers
fmt.Printf("%s chose %s.\n", answers.Name, answers.Color)
}
survey-2.3.7/examples/validation.go 0000664 0000000 0000000 00000001440 14344402622 0017357 0 ustar 00root root 0000000 0000000 //go:build ignore
package main
import (
"fmt"
"github.com/AlecAivazis/survey/v2"
)
// the questions to ask
var validationQs = []*survey.Question{
{
Name: "name",
Prompt: &survey.Input{Message: "What is your name?"},
Validate: survey.Required,
},
{
Name: "valid",
Prompt: &survey.Input{Message: "Enter 'foo':", Default: "not foo"},
Validate: func(val interface{}) error {
// if the input matches the expectation
if str := val.(string); str != "foo" {
return fmt.Errorf("You entered %s, not 'foo'.", str)
}
// nothing was wrong
return nil
},
},
}
func main() {
// the place to hold the answers
answers := struct {
Name string
Valid string
}{}
err := survey.Ask(validationQs, &answers)
if err != nil {
fmt.Println("\n", err.Error())
}
}
survey-2.3.7/filter.go 0000664 0000000 0000000 00000000017 14344402622 0014673 0 ustar 00root root 0000000 0000000 package survey
survey-2.3.7/go.mod 0000664 0000000 0000000 00000001123 14344402622 0014164 0 ustar 00root root 0000000 0000000 module github.com/AlecAivazis/survey/v2
require (
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2
github.com/creack/pty v1.1.17
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/mattn/go-colorable v0.1.2 // indirect
github.com/mattn/go-isatty v0.0.8
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
github.com/stretchr/testify v1.6.1
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
golang.org/x/text v0.4.0
)
go 1.13
survey-2.3.7/go.sum 0000664 0000000 0000000 00000012355 14344402622 0014222 0 ustar 00root root 0000000 0000000 github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
survey-2.3.7/img/ 0000775 0000000 0000000 00000000000 14344402622 0013635 5 ustar 00root root 0000000 0000000 survey-2.3.7/img/multi-select-all-none.gif 0000664 0000000 0000000 00000210242 14344402622 0020437 0 ustar 00root root 0000000 0000000 GIF89a 1 " % 1
0
? B $ J 2 $D %S *] 4^ ?r ,?: &97OCvEbGgPW~!Q*,,,5i8=Qqbzcet$Z:<A-sE(R%W5aEcNlQrxr,d-D]^-g6l<vK|oh7`.b1q@qBrQuPvKQ`o~~sv U B 9p/$.E"2'Q DbG~5]^_ M ^Ti w%!'%\((-5) + ,`, -.A/4R0C`34124Qp55X6 7[V7n8 8;8Uz9 ;;(>,.> A_vBBbCCzD?NI#I;@J KK:8K{LLMQQRmT V[.\]A%_(`amaO:a1dwde}ef;hU`kC*kI9kk#m5pr^s}xwsx3yyzvXz{z|E}~|u|Ld@S!jQyX/Uzbf5s^(zSck3vw?zD~FLTgo[\pzk|!NETSCAPE2.0 !
, HA*\0 #t(ŋ3j.lIɓ(S\ɲe(
*Uϔ͕r{eԩU@9{dHc]˶mIPᶢb., J]pxn9FQ#K`q }X̲gQ(uZ0Kh"CJϔ ~1\0?ZO>yl!gG-2xBD2@Sgw.x@ߪg zU4`)x0D ixMHyU &$}e~Eu\sLi@ ,x
?dsmJB"QWl >ӜBjN7f+ǒM
O9h(9N{la9lXAԊKd̎F$o>Xc/h >;- tM$>@oC L-
r)jt3PFu XpK8
a@1" FDpz:s D=3$,+qECEt{OD0 C ]##' .,3
I C*0@eieyCMq