Files
influx-cli/pkg/stdio/console.go
2021-06-30 14:47:23 -04:00

115 lines
3.2 KiB
Go

package stdio
import (
"errors"
"io"
"os"
"github.com/AlecAivazis/survey/v2"
"github.com/AlecAivazis/survey/v2/terminal"
"github.com/mattn/go-isatty"
)
// terminalStdio interacts with the user via an interactive terminal.
type terminalStdio struct {
Stdin terminal.FileReader
Stdout terminal.FileWriter
Stderr io.Writer
}
// TerminalStdio interacts with users over stdin/stdout/stderr.
var TerminalStdio StdIO = &terminalStdio{
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
}
// Write prints some bytes to stdout.
func (t *terminalStdio) Write(p []byte) (int, error) {
return t.Stdout.Write(p)
}
// WriteErr prints some bytes to stderr.
func (t *terminalStdio) WriteErr(p []byte) (int, error) {
return t.Stderr.Write(p)
}
type bannerTemplateData struct {
Message string
}
var bannerTemplate = `{{color "cyan+hb"}}> {{ .Message }}{{color "reset"}}
`
// Banner displays informational text to the user.
func (t *terminalStdio) Banner(message string) error {
r := survey.Renderer{}
r.WithStdio(terminal.Stdio{In: t.Stdin, Out: t.Stdout, Err: t.Stderr})
return r.Render(bannerTemplate, &bannerTemplateData{Message: message})
}
// Error displays an error message to the user.
func (t *terminalStdio) Error(message string) error {
r := survey.Renderer{}
r.WithStdio(terminal.Stdio{In: t.Stdin, Out: t.Stdout, Err: t.Stderr})
cfg := survey.PromptConfig{Icons: survey.IconSet{Error: survey.Icon{Text: "X", Format: "red"}}}
return r.Error(&cfg, errors.New(message))
}
func (t *terminalStdio) InputIsInteractive() bool {
return isatty.IsTerminal(t.Stdin.Fd()) || isatty.IsCygwinTerminal(t.Stdin.Fd())
}
// GetStringInput prompts the user for arbitrary input.
func (t *terminalStdio) GetStringInput(prompt, defaultValue string) (input string, err error) {
question := survey.Input{
Message: prompt,
Default: defaultValue,
}
err = survey.AskOne(&question, &input,
survey.WithStdio(t.Stdin, t.Stdout, t.Stderr),
survey.WithValidator(survey.Required))
return
}
// GetSecret prompts the user for a secret.
func (t *terminalStdio) GetSecret(prompt string, minLen int) (password string, err error) {
question := survey.Password{Message: prompt}
opts := []survey.AskOpt{survey.WithStdio(t.Stdin, t.Stdout, t.Stderr)}
if minLen > 0 {
opts = append(opts, survey.WithValidator(survey.MinLength(minLen)))
}
err = survey.AskOne(&question, &password, opts...)
question.NewCursor().HorizontalAbsolute(0)
return
}
// GetPassword prompts the user for a secret twice, and inputs must match.
// Uses stdio.MinPasswordLen as the minimum input length
func (t *terminalStdio) GetPassword(prompt string) (string, error) {
pass1, err := t.GetSecret(prompt, MinPasswordLen)
if err != nil {
return "", err
}
// Don't bother with the length check the 2nd time, since we check equality to pass1.
pass2, err := t.GetSecret(prompt+" again", 0)
if err != nil {
return "", err
}
if pass1 == pass2 {
return pass1, nil
}
return "", t.Error("Passwords do not match")
}
// GetConfirm asks the user for a y/n answer to a prompt.
func (t *terminalStdio) GetConfirm(prompt string) (answer bool) {
question := survey.Confirm{
Message: prompt,
}
if err := survey.AskOne(&question, &answer, survey.WithStdio(t.Stdin, t.Stdout, t.Stderr)); err != nil {
answer = false
}
return
}