promptui icon indicating copy to clipboard operation
promptui copied to clipboard

IsConfirm generates error if response is not "y"

Open decafdev opened this issue 7 years ago • 9 comments

when I perform a confirmation prompt, I am getting an empty error when entering any value other than y is this expected behavior?

to reproduce

package main

import (
	"fmt"

	"github.com/manifoldco/promptui"
)

func main() {
	prompt := promptui.Prompt{
		Label:     "does this work?",
		IsConfirm: true,
		Default:   "n",
	}
	answer, err := prompt.Run()
	fmt.Printf("error <%v>, answer <%s> \n", err, answer)
}

// type "y" => error <<nil>>, answer <y>	// err == nil (true)
// type "n" => error <>, answer <n> 		// err == nil (false)
// type "" => error <>, answer <>		// err == nil (false)
// type "foo" => error <>, answer <foo>		// err == nil (false)

decafdev avatar Nov 06 '18 15:11 decafdev

I also ran into this multiple times. Just accepted that err is the result.

imo there just should be a separate ConfirmPrompt that returns bool, error

fiws avatar Jan 06 '19 16:01 fiws

Also ran into the same issue and took the same path as @fiws... err != nil -> No / Empty

danlsgiga avatar Mar 05 '19 19:03 danlsgiga

It looks like this is expected behavior. Reading the godocs states:

var ErrAbort = errors.New("")

ErrAbort is the error returned when confirm prompts are supplied "n"

smitt04 avatar May 23 '19 15:05 smitt04

Yeah this is frustrating behavior. No shouldn't be an error. If you want to split the logic tree based on the confirm result you can't do that, since it's an error. Also an acceptable answer should be "y, yes, YES, Y, 1", etc.

randallmlough avatar Nov 27 '19 08:11 randallmlough

for anyone coming across this issue, I've gotten around this issue by mocking a normal prompt to look and act like a confirm prompt. I run the results through regex to get a true/false statement.


func confirm(label string) (bool, error) {
	val := func(input string) error {
		//if input == "" {
		//	return errors.New("input required")
		//}
		if len(input) > 3 {
			return errors.New("Please give a yes or no answer")
		}
		return nil
	}
	faintText := promptui.Styler(promptui.FGFaint)
	boldText := promptui.Styler(promptui.FGBold)
	greenText := promptui.Styler(promptui.FGGreen)
	redText := promptui.Styler(promptui.FGRed)
	defaultLabel := fmt.Sprintf("%s %s ", boldText(label), faintText("[y/N]"))

	template := promptui.PromptTemplates{
		Prompt:  defaultLabel,
		Valid:   defaultLabel,
		Invalid: fmt.Sprintf("%s %s ", redText(label), faintText("[y/N]")),
		Success: greenText(label) + " ",
	}

	prompt := promptui.Prompt{
		Validate:  val,
		Templates: &template,
	}
	result, err := prompt.Run()
	if err != nil {
		return false, err
	}
	return isConfirm(result), nil
}

// acceptable answers: y,yes,Y,YES,1
var confirmExp = regexp.MustCompile("(?i)y(?:es)?|1")

func isConfirm(confirm string) bool {
	return confirmExp.MatchString(confirm)
}

randallmlough avatar Nov 27 '19 16:11 randallmlough

Repeated with latest version both in Windows and Linux

perolo avatar Oct 08 '20 09:10 perolo

oh, man...this threw me for a loop. The confirm within _examples doesn't make this clear either. I agree with @randallmlough that "No" shouldn't be an error since it's a valid user response.

pgcayen avatar Jul 19 '21 21:07 pgcayen

If in your want to use boolean expression of the prompt then following would do the the job.

package main

import (
  "errors"
  "fmt"
  "strings"

  "github.com/manifoldco/promptui"
)

func main() {
  prompt := promptui.Prompt{
    Label:     "does this work?",
    IsConfirm: true,
    Default:   "y",
  }
  validate := func(s string) error {
    if len(s) == 1 && strings.Contains("YyNn", s) || prompt.Default != "" && len(s) == 0 {
      return nil
    }
    return errors.New("invalid input")
  }
  prompt.Validate = validate
  
  answer, err := prompt.Run()
  confirmed := !errors.Is(err, promptui.ErrAbort)
  if err != nil && confirmed {
    fmt.Println("ERROR: ", err)
    return
  }
 
  fmt.Printf("error <%v>, answer <%s> confirmed <%t>\n", err, answer, confirmed)
}
type output
y error <<nil>>, answer <y> confirmed <true>
Y error <<nil>>, answer <Y> confirmed <true>
n error <>, answer <n> confirmed <false>
N error <>, answer <n> confirmed <false>
`` error <<nil>>, answer <> confirmed <true>
foo shows <invalid input> and keeps prompt open

Since implementation is emphasizing Y/N

https://github.com/manifoldco/promptui/blob/981a3cab68f6f3481bf42c6a98521af7fbd14fae/prompt.go#L43-L45

My Two Cents to this topic is that perhaps there should be new error added to the package and return ErrInvalidInput when input string is invalid option instead returning promptui.ErrAbort any input which is not y or Default.

mkungla avatar Aug 26 '21 17:08 mkungla

Just test for ErrAbort like

if err != nil {
  if err == promptui.ErrAbort {
    // deal with abort
  }

  // deal with error
}

OR use the errors package to test if the returned error is/will be wrapped:

if errors.Is(err, promptui.ErrAbort) {
  ...
}

feketegy avatar Sep 07 '23 13:09 feketegy