Skip to content

Commit 9572e90

Browse files
committed
feat: add cli first class validation
1 parent f75d497 commit 9572e90

File tree

2 files changed

+63
-9
lines changed

2 files changed

+63
-9
lines changed

cli/clibase/values.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,59 @@ import (
1616
"gopkg.in/yaml.v3"
1717
)
1818

19+
// Validator is a wrapper around a pflag.Value that allows for validation
20+
// of the value after or before it has been set.
21+
type Validator[T pflag.Value] struct {
22+
Value T
23+
// ValidateBefore is called before the value is set.
24+
ValidateBefore func(input string) error
25+
// ValidateAfter is called after the value is set.
26+
ValidateAfter func(T) error
27+
}
28+
29+
func Validate[T pflag.Value](opt T) *Validator[T] {
30+
return &Validator[T]{Value: opt}
31+
}
32+
33+
func (i *Validator[T]) Before(fn func(input string) error) *Validator[T] {
34+
i.ValidateBefore = fn
35+
return i
36+
}
37+
38+
func (i *Validator[T]) After(fn func(value T) error) *Validator[T] {
39+
i.ValidateAfter = fn
40+
return i
41+
}
42+
43+
func (i *Validator[T]) String() string {
44+
return i.Value.String()
45+
}
46+
47+
func (i *Validator[T]) Set(input string) error {
48+
if i.ValidateBefore != nil {
49+
err := i.ValidateBefore(input)
50+
if err != nil {
51+
return err
52+
}
53+
}
54+
55+
err := i.Value.Set(input)
56+
if err != nil {
57+
return err
58+
}
59+
if i.ValidateAfter != nil {
60+
err = i.ValidateAfter(i.Value)
61+
if err != nil {
62+
return err
63+
}
64+
}
65+
return nil
66+
}
67+
68+
func (i *Validator[T]) Type() string {
69+
return i.Value.Type()
70+
}
71+
1972
// NoOptDefValuer describes behavior when no
2073
// option is passed into the flag.
2174
//

enterprise/cli/workspaceproxy.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,12 @@ func (r *RootCmd) createProxy() *clibase.Cmd {
235235
noPrompts bool
236236
formatter = newUpdateProxyResponseFormatter()
237237
)
238+
validateIcon := func(s string) error {
239+
if !(strings.HasPrefix(s, "/emojis/") || strings.HasPrefix(s, "http")) {
240+
return xerrors.New("icon must be a relative path to an emoji or a publicly hosted image URL")
241+
}
242+
return nil
243+
}
238244

239245
client := new(codersdk.Client)
240246
cmd := &clibase.Cmd{
@@ -268,14 +274,9 @@ func (r *RootCmd) createProxy() *clibase.Cmd {
268274

269275
if proxyIcon == "" && !noPrompts {
270276
proxyIcon, err = cliui.Prompt(inv, cliui.PromptOptions{
271-
Text: "Icon URL:",
272-
Default: "/emojis/1f5fa.png",
273-
Validate: func(s string) error {
274-
if !(strings.HasPrefix(s, "/emojis/") || strings.HasPrefix(s, "http")) {
275-
return xerrors.New("icon must be a relative path to an emoji or a publicly hosted image URL")
276-
}
277-
return nil
278-
},
277+
Text: "Icon URL:",
278+
Default: "/emojis/1f5fa.png",
279+
Validate: validateIcon,
279280
})
280281
if err != nil {
281282
return err
@@ -319,7 +320,7 @@ func (r *RootCmd) createProxy() *clibase.Cmd {
319320
clibase.Option{
320321
Flag: "icon",
321322
Description: "Display icon of the proxy.",
322-
Value: clibase.StringOf(&proxyIcon),
323+
Value: clibase.Validate(clibase.StringOf(&proxyIcon)).Before(validateIcon),
323324
},
324325
clibase.Option{
325326
Flag: "no-prompt",

0 commit comments

Comments
 (0)