The library is a wrapper around the standard library's flag
package providing a way
to parse command line flags into a struct fields
- Ignore unknown flags
- Distinguish not passed flags from default values that is difficult with the standard
flag
package - More convenient way to define flags using struct tags
- Using nested structs for similar flag groups
go get github.com/cardinalby/go-struct-flags
import (
flago "github.com/cardinalby/go-struct-flags"
)
type MyFlags struct {
// Verbose can be filled both by "-v" and "-verbose" flags
Verbose int `flags:"verbose,v" flagUsage:"verbose mode: 1,2,3"`
// Login is optional, it will be set only if passed
Login *string `flag:"login" flagUsage:"user login"`
// FileNames will contain flag.Args() after Parse()
FileNames []string `flagArgs:"true"`
}
// This is the way to set defaults:
myFlags := MyFlags{}
flagSet := flago.NewFlagSet("myApp", flag.ExitOnError)
if err := flagSet.StructVar(&myFlags); err != nil {
// error can happen if struct tags are invalid
panic(err)
}
// Use short form of "-v"
// Normally you would use os.Args[1:] instead of hardcoded values
if err := flagSet.Parse([]string{"-v", "2", "--login", "user1", "file1", "file2"}); err != nil {
// Parse has default behavior
panic(err)
}
// myFlags.Verbose == 2
// *myFlags.Login == "user1"
// myFlags.FileNames == []string{"file1", "file2"}
Using pointer fields enables you to distinguish not passed flags from default values:
// Use long form of "--verbose" and don't pass "--login"
if err := flagSet.Parse([]string{"--verbose", "2"}); err != nil {
// Parse has default behavior
panic(err)
}
// myFlags.Verbose == 2
// myFlags.Login == nil
// len(myFlags.FileNames) == 0
Unlike std flag
package the library doesn't provide a way to explicitly set "default" values.
You can do it in native way just assigning them to struct fields before parsing (both for pointer and non-pointer fields)
defaultLogin := "admin"
myFlags := MyFlags{
Verbose: 1,
Login: &defaultLogin,
}
// No flags are passed
if err := flagSet.Parse("file1", "file2"); err != nil {
// Parse has default behavior
panic(err)
}
// myFlags.Verbose == 1 <--- hasn't been changed
// *myFlags.Login == "admin" <--- hasn't been changed
// myFlags.FileNames == []string{"file1", "file2"}
You can create logically similar flag groups assigning them a prefix using nested structs:
type PersonFlags struct {
Name string `flag:"name" flagUsage:"person name"`
Email string `flag:"email" flagUsage:"person email"`
}
type MyParentFlags struct {
Sender PersonFlags `flagPrefix:"sender-" flagUsagePrefix:"sender "`
Receiver PersonFlags `flagPrefix:"receiver-" flagUsagePrefix:"receiver "`
}
flagSet := NewFlagSet("myApp", flag.ExitOnError)
myFlags := MyParentFlags{}
if err := flagSet.StructVar(&myFlags); err != nil {
panic(err)
}
if err := flagSet.Parse([]string{
"--sender-name", "John",
"--sender-email", "[email protected]",
"--receiver-name", "Dave",
"--receiver-email", "[email protected]",
}); err != nil {
panic(err)
}
// myFlags.Sender.Name == "John"
// myFlags.Sender.Email == "[email protected]"
// myFlags.Receiver.Name == "Dave"
// myFlags.Receiver.Email == "[email protected]"
See tests for more examples.
The library provides two constructors for flago.FlagSet
:
Wrap(*flag.FlagSet)
that wraps the existing stdflag.FlagSet
instance that can have some flags already registered or be used to register new flags using its methods.NewFlagSet()
creates newflag.FlagSet
instance and sets itsUsage
toflago.DefaultUsage
- Functions with the names matching
flago.FlagSet
method names for a defaultflago.CommandLine
FlagSet instance are available in the same manner as in the standardflag
package.
SetIgnoreUnknown(true)
method call will make Parse()
ignore unknown flags instead of returning an error.
To retrieve unknown flags that have been ignored, call GetIgnoredArgs()
after Parse()
.
With unknown flags it's not always clear how to treat them: as bool flags or as flags with the following values.
See docs for SetIgnoreUnknownAmbiguousAsBoolFlags(...)
for details.
SetAllowParsingMultipleAliases(true)
method call will make Parse()
not return an error if multiple aliases
of the same field are passed. The last passed value will be used.
Default behavior is to return an error containing flago.ErrMultipleAliases
.
To parse flags and args to struct fields you should use StructVar()
or StructVarWithPrefix()
methods.
The methods accept optional arguments of pointers to ignored fields. These fields will not be registered as flags.
The struct fields that don't have any "flag" tags will be ignored.
Defines flag name for a field. Unlike json
package, if the name is not set, the field will be ignored.
- Field should have type supported by
flag
package or be pointer to such type. - If the field is a pointer, it will be set only if the flag is passed to
Parse()
. - If it's not a pointer and is the correspondent flag is not passed, its default value will remain.
Same as flag
but defines multiple comma-separated flag names (aliases) for a field.
- You should use either
flag
orflags
tag for a field, not both. - By default,
Parse()
will return an error containingflago.ErrMultipleAliases
if multiple aliases of the same field are passed. You can change this behavior usingSetAllowParsingMultipleAliases(true)
Defines flag usage message for a field. Should be used only for fields with flag
or flags
tag.
Defines that the field is required and Parse()
will return an error containing flago.ErrIsRequired
if the flag is not passed and doesn't have a default value (field is not initialized by non-zero value at the
moment of registration)
Field will be filled with flag.Args()
(remaining command line args after named flags) after Parse()
.
- Other "flag" tags should not be used for such fields.
The library parses fields in nested structs if explicitly instructed with flagPrefix
tag on a
field containing another struct.
Instructs the library to parse fields in nested struct.
- All resulting flag names (specified by
flag
andflags
tags) in the nested struct will have the specified prefix. - With
flagPrefix=""
nested struct will still be parsed but without using prefix for its fields. - The prefix does not affect fields tagged with
flagArgs
.
This tag is used only for nested struct fields with flagPrefix
tag.
Instructs the library to use the specified prefix for flag usage messages of fields in nested struct.
If you use flago.NewFlagSet()
constructor, resulting FlagSet will assign own default implementation
of Usage
that prints help message in the same manner as the standard flag
package but
grouping aliases assigned to a field using flags
tag in one line.
If you use flago.Wrap()
constructor, it doesn't override default Usage
of the standard FlagSet
.
You can do it manually: flagSet.Usage = flago.DefaultUsage
StructVar()
method parses fields and their tags and calls the correspondentFlagSet.***Var()
methods depending on the field type.- So fields should have types supported by
flag
package or be pointers to such types. - Fields implementing
flag.Value
andfunc(string) error
fields are also supported (but can't be pointers).
If a field has encoding.TextUnmarshaler
interface, it also should implement encoding.TextMarshaler
.
The library will call FlagSet.TextVar()
on such fields that requires a default "marshaler" value.
Provides helper tools for manipulating command line arguments:
- Iterate over arguments as tokens with known roles
- Iterate over flags with values, unnamed args, ...
- Lookup for flags by name
- Delete flag by name
- Upsert flag by name
- Mutate flags (make it inline, change value, name, ...)
- Strip unknown flags