Skip to content
maxlandon edited this page Jan 8, 2023 · 4 revisions

Now that we have commands and positional arguments covered, we can declare flags for our commands. The behavior is much simpler for those, despite them having a larger set of tags available.

All struct fields marked as option flags are parsed as pflag.Flag objects, which is the flag type used by cobra commands.

Basic flags

Taking back our command with positional arguments, we can add a basic set of flags to it, like below (note that NoFail uses the octago/sflags notation, while Perms uses go-flags one: both are valid):

type Create struct {
    Args struct {
      Host   string   `desc:"Host where to create the resource"`
      Files  []string `desc:"Files to create on the remote host"`
    } `positional-args:"yes" required:"true"`
        
    // Flags
    NoFail bool         `flag:"n no-fail" desc:"a flag cancelling the command if a failure arises"`
    Perms  []string     `short:"p" long:"perms" desc:"a list of permission modes to apply when creating files"`
    APIKey string       `flag:"k key" desc:"API key to authenticate" required:"yes"`
}

As for the positional arguments, we can directly use those fields within the command Execute() if declared.

Default values, NoOptDefVal, valid choices

Struct tags also allow to specify default values, no-opt-def-values (values that are used when the flag is invoked, but without an argument) or valid choices. The three of these can be specified multiple times, or within the same tag if the values are space-separated, like below:

type Create struct {
    NoFail bool       `flag:"n no-fail"`
    Perms  []string   `short:"p" long:"perms" optional-value:"create append" default:"readonly"`
    Choices []string  `flag:"c choices" choice:"value1 value2" choice:"value3"`
}

The following invocation would then be valid, with their results:

./program create -np                   # Perms = [create append]
./program create -p=readonly,append    # Perms = [readonly append]
./program create -n                    # Perms = [readonly]
./program create -nc value5            # Will fail: value5 is not a valid choice.

Grouping flags

As for commands, flags can be grouped within a given struct, like the following:

type FilesystemOptions struct {
	Path  string            `short:"P" long:"path" `
	Elems map[string]string `short:"e" long:"elems"`
	Files []string          `short:"f" long:"files"`
	Check bool              `long:"check" short:"c"`
}

type Create struct {
    NoFail bool      `flag:"n no-fail"`
    Perms  []string  `short:"p" long:"perms" optional-value:"create,append" default:"readonly"`
        
    // Another set of flags coexisting with the ones above.
    FilesystemOptions `group:"filesystem"`
}

Custom flag types

A wide range of native Go types (including arrays and maps) are supported by default. However, would a user want to implement a custom flag type, the latter should implement the following interface:

type Value interface {
	String() string
	Set(string) error

	// pflag.Flag require this
	Type() string
}

If a field that is not supported out-of-the-box by this library is marked as flag despite not satisfying the interface, the program will panic when scanning it.

Valid flag tags

The following is the section for valid flags' tags from the godoc file:

Notation from jessevdk/go-flags

flag:             Short and/or long names for the flag, space-separated.
                  (ex: `flag:"-v --verbose`).
short:            The short name of the option (single character)
long:             The long name of the option
required:         If non empty, makes the option required to appear on the command
                  line. If a required option is not present, the parser will
                  return ErrRequired (optional)
description:      The description of the option (optional)
desc:             Same as 'description'
long-description: The long description of the option. Currently only
                  displayed in generated man pages (optional)
no-flag:          If non-empty, this field is ignored as an option (optional)
optional:         If non-empty, makes the argument of the option optional. When an
                  argument is optional it can only be specified using
                  --option=argument (optional)
optional-value:   The value of an optional option when the option occurs
                  without an argument. This tag can be specified multiple
                  times in the case of maps or slices (optional)
default:          The default value of an option. This tag can be specified
                  multiple times in the case of slices or maps (optional)
default-mask:     When specified, this value will be displayed in the help
                  instead of the actual default value. This is useful
                  mostly for hiding otherwise sensitive information from
                  showing up in the help. If default-mask takes the special
                  value "-", then no default value will be shown at all
                  (optional)
env:              The default value of the option is overridden from the
                  specified environment variable, if one has been defined.
                  (optional)
env-delim:        The 'env' default value from environment is split into
                  multiple values with the given delimiter string, use with
                  slices and maps (optional)
choice:           Limits the values for an option to a set of values.
                  You can either specify multiple values in a single tag
                  if they are space-separated, and/or with multiple tags.
                  (e.g. `long:"animal" choice:"cat bird" choice:"dog"`)
hidden:           If non-empty, the option is not visible in the help or man page.

Notation from octago/sflags

`flag:"-"`           Field is ignored by this package.
`flag:"myName"`      Field appears in flags as "myName".
`flag:"~myName"`     If this field is from nested struct, prefix from parent struct will be ingored.
`flag:"myName a"`    You can set short name for flags by providing it's value after a space.
`flag:",hidden"`     This field will be removed from generated help text.
`flag:",deprecated"` This field will be marked as deprecated in generated help text