Skip to content

Commit f733f23

Browse files
committed
cmd/tui: Use own asker implementation
This allows influencing the error caused on wrong inputs. To stay consistent with the other tui errors we cannot use LXD's asker as we cannot modify the yielded error. Signed-off-by: Julian Pelizäus <julian.pelizaeus@canonical.com>
1 parent e9ec420 commit f733f23

1 file changed

Lines changed: 57 additions & 9 deletions

File tree

cmd/tui/handler.go

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
1515
var 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.
1821
type 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.
7798
func (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

Comments
 (0)