Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions examples/full/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,64 @@ func makeUsrC() *cobra.Command {
return usrC
}

var _ structcli.ValidatableOptions = (*PresetDemoOptions)(nil)
var _ structcli.TransformableOptions = (*PresetDemoOptions)(nil)

// PresetDemoOptions demonstrates flagpreset values flowing through
// transform and validation logic.
type PresetDemoOptions struct {
Role string `flag:"role" flagpreset:"as-admin=admin;as-guest=guest;as-super=superadmin" validate:"required,oneof=admin guest"`
Label string `flag:"label" flagpreset:"auto-label= john doe " mod:"trim,title" validate:"required,min=3,max=32"`
}

func (o *PresetDemoOptions) Validate(ctx context.Context) []error {
var errs []error
err := validator.New().Struct(o)
if err != nil {
if validationErrs, ok := err.(validator.ValidationErrors); ok {
for _, fieldErr := range validationErrs {
errs = append(errs, fieldErr)
}
} else {
errs = append(errs, fmt.Errorf("validator.Struct() failed unexpectedly: %w", err))
}
}
if len(errs) == 0 {
return nil
}

return errs
}

func (o *PresetDemoOptions) Transform(ctx context.Context) error {
return modifiers.New().Struct(ctx, o)
}

func (o *PresetDemoOptions) Attach(c *cobra.Command) error {
return structcli.Define(c, o)
}

func makePresetC() *cobra.Command {
opts := &PresetDemoOptions{}

presetC := &cobra.Command{
Use: "preset",
Short: "Demonstrate flag presets with validation and transformation",
Long: "Demonstrate that flagpreset aliases are syntactic sugar and still flow through Transform and Validate",
RunE: func(c *cobra.Command, args []string) error {
if err := structcli.Unmarshal(c, opts); err != nil {
return err
}
fmt.Fprintln(c.OutOrStdout(), pretty(opts))

return nil
},
}
opts.Attach(presetC)

return presetC
}

var _ structcli.ContextOptions = (*UtilityFlags)(nil)

type UtilityFlags struct {
Expand Down Expand Up @@ -324,6 +382,7 @@ func NewRootC(exitOnDebug bool) (*cobra.Command, error) {
commonOpts.Attach(rootC)
rootC.AddCommand(makeSrvC())
rootC.AddCommand(makeUsrC())
rootC.AddCommand(makePresetC())

// This single line enables the debugging global flag
if err := structcli.SetupDebug(rootC, debug.Options{Exit: exitOnDebug}); err != nil {
Expand Down
18 changes: 18 additions & 0 deletions examples/full/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,24 @@ srv:
assert.Contains(t, output, `"Name": "Test User"`)
},
},
{
name: "Flagpreset aliases pass through transform and validation",
args: []string{"preset", "--as-admin", "--auto-label"},
assertFunc: func(t *testing.T, output string, err error) {
require.NoError(t, err)
assert.Contains(t, output, `"Role": "admin"`)
assert.Contains(t, output, `"Label": "John Doe"`)
},
},
{
name: "Flagpreset alias can fail validation when preset value is invalid",
args: []string{"preset", "--as-super", "--auto-label"},
assertFunc: func(t *testing.T, output string, err error) {
require.Error(t, err)
assert.ErrorContains(t, err, "invalid options for preset")
assert.ErrorContains(t, err, "Field validation for 'Role' failed on the 'oneof' tag")
},
},
{
name: "Flag value overrides both Environment and Config",
args: []string{"srv", "--port", "1111"}, // Flag has highest precedence
Expand Down