@@ -4,16 +4,19 @@ import (
44 "bufio"
55 "fmt"
66 "os"
7+ "slices"
78 "strings"
89 "sync"
910
10- "github.com/canonical/lxd/shared/cmd"
1111 tea "github.com/charmbracelet/bubbletea"
1212)
1313
1414// ContextError is the charmbracelet representation of a context cancellation error.
1515var ContextError error = tea .ErrProgramKilled
1616
17+ // InvalidInputError is used to indicate false input to an asked question.
18+ var InvalidInputError func () = func () { PrintError ("Invalid input, try again" ) }
19+
1720// InputHandler handles input dialogs.
1821type InputHandler struct {
1922 input * os.File
@@ -73,6 +76,24 @@ func (i *InputHandler) formatQuestion(question string, defaultAnswer string, acc
7376 return fmt .Sprintf ("%s%s%s: " , question , acceptedAnswersBlock , defaultAnswerBlock )
7477}
7578
79+ // Ask a question on the output stream and read the answer from the input stream.
80+ func (i * InputHandler ) askQuestion (question , defaultAnswer string ) (string , error ) {
81+ fmt .Print (question )
82+
83+ return i .readAnswer (defaultAnswer )
84+ }
85+
86+ // Read the user's answer from the input stream, trimming newline and providing a default.
87+ func (i * InputHandler ) readAnswer (defaultAnswer string ) (string , error ) {
88+ answer , err := bufio .NewReader (i .input ).ReadString ('\n' )
89+ answer = strings .TrimSpace (strings .TrimSuffix (answer , "\n " ))
90+ if answer == "" {
91+ answer = defaultAnswer
92+ }
93+
94+ return answer , err
95+ }
96+
7697// AskBoolWarn is the same as AskBool but it prints the given warning before asking.
7798func (i * InputHandler ) AskBoolWarn (warning string , question string , defaultAnswer bool ) (bool , error ) {
7899 PrintWarning (warning )
@@ -88,8 +109,20 @@ func (i *InputHandler) AskBool(question string, defaultAnswer bool) (bool, error
88109 defaultAnswerStr = "yes"
89110 }
90111
91- asker := cmd .NewAsker (bufio .NewReader (i .input ), nil )
92- return asker .AskBool (i .formatQuestion (question , defaultAnswerStr , []string {"yes" , "no" }), defaultAnswerStr )
112+ for {
113+ answer , err := i .askQuestion (i .formatQuestion (question , defaultAnswerStr , []string {"yes" , "no" }), defaultAnswerStr )
114+ if err != nil {
115+ return false , err
116+ }
117+
118+ if slices .Contains ([]string {"yes" , "y" }, strings .ToLower (answer )) {
119+ return true , nil
120+ } else if slices .Contains ([]string {"no" , "n" }, strings .ToLower (answer )) {
121+ return false , nil
122+ }
123+
124+ InvalidInputError ()
125+ }
93126}
94127
95128// AskStringWarn is the same as AskString but it prints the given warning before asking.
@@ -103,11 +136,26 @@ func (i *InputHandler) AskString(question string, defaultAnswer string, validato
103136 i .setActive (true )
104137 defer i .setActive (false )
105138
106- asker := cmd . NewAsker ( bufio . NewReader ( i . input ), nil )
107- result , err := asker . AskString (i .formatQuestion (question , defaultAnswer , nil ), defaultAnswer , validator )
108- if err != nil {
109- return "" , err
110- }
139+ for {
140+ answer , err := i . askQuestion (i .formatQuestion (question , defaultAnswer , nil ), defaultAnswer )
141+ if err != nil {
142+ return "" , err
143+ }
111144
112- return result , nil
145+ if validator != nil {
146+ err = validator (answer )
147+ if err != nil {
148+ InvalidInputError ()
149+ continue
150+ }
151+
152+ return answer , err
153+ }
154+
155+ if len (answer ) != 0 {
156+ return answer , err
157+ }
158+
159+ InvalidInputError ()
160+ }
113161}
0 commit comments