Documentation
¶
Overview ¶
Package flagbind makes defining flags as simple as defining a struct type.
flagbind.Bind parses the exported fields of a struct and binds them to a FlagSet, which can be the standard flag package or the popular pflag package github.com/spf13/pflag.
By coupling the flag definitions with a type definition, the use of globals is discouraged, and flag types may be coupled with related behavior allowing for better organized and documented code.
Flag names, usage, defaults and other options can be set using struct tags on exported fields. Using struct nesting, flags can be composed and assigned a flag name prefix. Exposing the settings of another package as flags is as simple as embedding the relevant types.
Alternatively, any type may implement the Binder interface, which allows for more direct control over the FlagSet, much like json.Unmarshal passes control to types that implement json.Unmarshaler. Also similar to json.Unmarshal, Bind will initialize any fields that are nil, and leave any fields that are already populated, as defaults.
See Bind documentation for the full details on controling flags.
Bind works seamlessly with both the standard library flag package and the popular github.com/spf13/pflag package.
If pflag is used, Bind adapts a flag.Value to a pflag.Value. The underlying type name of the flag.Value is used as the return value of the additional `Type() string` function required by the pflag.Value interface.
Getting Started ¶
Start by declaring a struct type for your flags.
var flags := struct { StringFlag string `flag:"flag-name;default value;Usage for string-flag"` Int int `flag:"integer;5"` // Flag names default to `auto-kebab-case` AutoKebabCase int // If pflag is used, -s is be used as the shorthand flag name, // otherwise it is ignored for use with the standard flag package. ShortName bool `flag:"short,s"` // Optionally extende the usage tag with subsequent `use` tags // on _ fields. URL string `flag:"url,u;http://www.example.com/;Start usage here" _ struct{} `use:"continue longer usage string for --url below it", // Nested and Embedded structs can add a flag name prefix, or not. Nested StructA NestedFlat StructB `flag:";;;flatten"` StructA // Flat by default StructB `flag:"embedded"` // Add prefix to nested field flag names. // Ignored ExplicitlyIgnored bool `flag:"-"` unexported bool }{ // Default values may also be set directly to override the tag. StringFlag: "override tag default", } fs := pflag.NewFlagSet("", pflag.ContinueOnError) flagbind.Bind(fs, &flags) fs.Parse([]string{"--auto-kebab-case"})
Index ¶
- Variables
- func Bind(fs FlagSet, v interface{}, opts ...Option) error
- func BindCobra(cmd *cobra.Command, v interface{}, opts ...Option) (err error)
- func FromCamelCase(name, sep string) string
- type Binder
- type ErrorDefaultValue
- type ErrorFlagOverrideUndefined
- type ErrorInvalidType
- type ErrorNestedStruct
- type FlagSet
- type JSONRawMessage
- type Option
- type PFlagSet
- type STDFlagSet
- type URL
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrorInvalidFlagSet = fmt.Errorf("flg must implement STDFlagSet or PFlagSet")
ErrorInvalidFlagSet is returned from Bind if flg doesn't implement STDFlagSet or PFlagSet.
var Separator = "-"
Separator is used to separate a prefix from a flag name and as the separator passed to FromCamelCase.
Functions ¶
func Bind ¶
Bind the exported fields of struct `v` to new flags in the FlagSet `fs`.
Bind returns ErrorInvalidFlagSet if `fs` does not implement STDFlagSet or PFlagSet.
Bind returns ErrorInvalidType if `v` is not a pointer to a struct.
Bind recovers from FlagSet panics and instead returns the panic as an error if a duplicate flag name occurs.
For each exported field of `v` Bind attempts to define one or more corresponding flags in `fs` according to the following rules.
If the field is a nil pointer, it is initialized.
If the field implements Binder, then only FlagBind is called on the field.
If the field implements flag.Value and not Binder, then it is bound as a Value on the FlagSet.
Otherwise, if the field is a struct, or struct pointer, then Bind is recursively called on a pointer to the struct field.
If the field is any supported type, a new flag is defined in `fs` with the settings defined in the field's `flag:"..."` tag. If the field is non-zero, its value is used as the default for that flag instead of whatever is defined in the `flag:";<default>"` tag. See FlagTag Settings below.
For a complete list of supported types see STDFlagSet and PFlagSet. Additionally, a json.RawMessage is also natively supported and is bound as a JSONRawMessage flag.
Ignoring a Field ¶
Use the tag `flag:"-"` to prevent an exported field from being bound to any flag. If the field is a nested or embedded struct then its fields are also ignored.
Flag Tag Settings ¶
The flag settings for a particular field can be customized using a struct field tag of the form:
`flag:"[<long>][,<short>][;<default>[;<usage>[;<options>]]]"`
The tag and all of its settings are [optional]. Semi-colons are used to distinguish subsequent settings.
<long>[,<short>] - Explicitly set the long and short names of the flag. All leading dashes are trimmed from both names. The two names are sorted for length, and the short name must be a single character, else it is ignored.
If `fs` does not implement PFlagSet, then the short name is ignored if a long name is defined, otherwise the short name is used as the long name.
If `fs` does implement PFlagSet, and only a short flag is defined, the long name defaults to the field name in kebab-case.
If no name is set, the long name defaults to the field name in "kebab-case". For example, "ThisFieldName" becomes "this-field-name". See FromCamelCase and Separator.
If the field is a nested or embedded struct and the "flatten" option is not set (see below), then the name is used as a prefix for all nested field flag names.
<default> - Bind attempts to parse <default> as the field's default, just like it would be parsed as a flag. Non-zero field values override this as the default.
<usage> - The usage string for the flag. See Extended Usage below for a way to break longer usage strings across multiple lines.
<options> - A comma separated list of additional options for the flag.
hide-default - Do not print the default value of this flag in the usage output. hidden - (PFlagSet only) Do not show this flag in the usage output. flatten - (Nested/embedded structs only) Do not prefix the name of the struct to the names of its fields. This overrides any explicit name on an embedded struct which would otherwise unflatten it.
Extended Usage ¶
Usage lines can frequently be longer than what comfortably fits in a flag tag on a single line. To keep line lengths shorter, use any number of blank identifier fields of any type with a `use` field tag to extend the usage of a flag. Each `use` tag is joined with a single space.
type Flags struct { URL string `flag:"url;;Usage starts here"` _ struct{} `use:"and continues here"` _ struct{} `use:"and ends here."` }
Auto-Adapt flag.Value To pflag.Value ¶
The pflag.Value interface is the flag.Value interface, but with an additional Type() string function. This means that flag.Value cannot be used directly as a pflag.Value.
In order to work around this when fs implements PFlagSet, Bind wraps any fields that implement flag.Value but not pflag.Value in a shim adapter that uses the underlying type name as the Type() string. This allows you to only need to implement flag.Value. If the field does implement pflag.Value, it is used directly.
Nested/Embedded Structs Flag Prefix
If the field is a nested or embedded struct, its fields are also recursively parsed.
In order to help avoid flag name collisions, child flag names may be prepended with a prefix. The prefix defaults to the parent's field name passed through FromCamelCase, with a trailing Separator.
The prefix may be set explicitly using the <name> on the parent's Flag Tag.
To allow for a distinct separator symbol to be used just for a prefix, an explicitly set prefix that ends in "-", "_", or "." will not have Separator appended.
By default, flags in nested structs always have a prefix, but this can be omitted with Flag Tag `flatten` <option>.
By default, flags in embedded structs do not given a prefix, but one can be added by setting an explicit Flag Tag <name>.
Overriding Flag Settings ¶
It is not always possible to set a Flag Tag on the fields of a nested struct type, such as when the type is from an external package. To allow for setting the default value, usage, or other options, an Overriding Flag Tag may be specified on a blank identifier field (`_`) that occurs anywhere after the field that defined the overridden flag.
The name specified in the Overriding Flag Tag must exactly match the flag name of the overridden flag, including any prefixes that were prepended due to nesting. Bind returns ErrorFlagOverrideUndefined if the flag name cannot be found.
Extended Usage may also defined immediately after an Overriding Flag Tag field.
For example, this sets the default value and usage on the flag for Timeout on an embedded http.Client.
type Flags struct { http.Client // Defines the -timeout flag _ struct{} `flag:"timeout;5s;HTTP request timeout"` _ struct{} `use:"... continued usage"` }
Example ¶
package main import ( "fmt" "log" "strings" "time" "github.com/spf13/pflag" ) // Flags is a struct that exercises all ways to declare flag tags. type Flags struct { // Unexported fields are ignored. skip bool // Explicitly ignored field. Ignored bool `flag:"-"` // Explicitly set <default>. Default bool `flag:";true"` // Explicitly set <usage>, but not <name> or <default> Usage bool `flag:";;Unique usage goes here"` // Custom <name> CustomName bool `flag:"different-flag-name"` // Dashes are trimmed and have no effect. WithDash bool `flag:"-with-dash"` WithTwoDash bool `flag:"--with-two-dash"` // The default name for this flag is `auto-kebab`. AutoKebab bool // When using pflag this is named --short and -s. // When using flag, this is just be named -s. Short bool `flag:"s"` // Order doesn't matter for short flags, only length. // Short flags are ignored when using flag, and not pflag. LongShort bool `flag:"long,l"` ShortLong bool `flag:"r,-rlong"` // When using pflag, this flag is hidden from the usage. Hidden bool `flag:";;Hidden usage;hidden"` // The default value of this flag is hidden from the usage. HideDefault string `flag:";default value;Hide default;hide-default"` // These pointers are allocated by Bind if they are nil. Ptr *bool // This is allocated, if not nil, and set to true, if not zero, by // Bind. PtrDefault *bool `flag:";true"` // Set <name> and <usage> but not <default>. Bool bool `flag:"bool;;Set the Bool true."` Int int `flag:";0"` Int64 int64 `flag:";0"` Uint uint `flag:";0"` Uint64 uint64 `flag:";0"` Float64 float64 `flag:";0"` // The <default> is 1*time.Hour. Duration time.Duration `flag:";1h"` String string Value TestValue ValueDefault TestValue `flag:";true"` // Nested and embedded structs are also parsed Nested StructA NestedFlat StructB `flag:";;;flatten"` StructA // embedded StructB `flag:"embedded"` } type StructA struct { StructABool bool } type StructB struct { StructBBool bool } type TestValue bool func (v *TestValue) Set(text string) error { switch strings.ToLower(text) { case "true": *v = true case "false": *v = false default: return fmt.Errorf("could not parse %q as TestValue", text) } return nil } func (v TestValue) String() string { return fmt.Sprint(bool(v)) } func main() { // Set some defaults f := Flags{String: "inherit this default"} fs := pflag.NewFlagSet("", pflag.ContinueOnError) if err := flagbind.Bind(fs, &f); err != nil { log.Fatal(err) } if err := fs.Parse([]string{ "--bool", "--hidden", "-s", "--duration", "1m", "--auto-kebab", "--nested-struct-a-bool", "--struct-b-bool", "--struct-a-bool", "--embedded-struct-b-bool", }); err != nil { log.Fatal(err) } fmt.Println("--bool", f.Bool) fmt.Println("--hidden", f.Hidden) fmt.Println("--short", f.Short) fmt.Println("--duration", f.Duration) }
Output: --bool true --hidden true --short true --duration 1m0s
func FromCamelCase ¶
FromCamelCase converts CamelCase to kebab-case, or snake_case, or lowercase, depending on `sep`.
It makes a best effort at respecting capitalized acronyms. For example:
camel -> camel CamelCamel -> camel-camel CamelID -> camel-id IDCamel -> id-camel APICamel -> api-camel APIURL -> apiurl ApiUrl -> api-url APIUrlID -> api-url-id
Types ¶
type Binder ¶
Binder binds itself to a FlagSet.
FlagBind should prepend the `prefix` to any flag names when adding flags to `fs` to avoid potential flag name conflicts and allow more portable implementations.
Additionally the opt should be passed down to Bind to preserve original opts if called again by the implementation.
The underlying type of `fs` is the same as the original FlagSet passed to Bind.
type ErrorDefaultValue ¶
ErrorDefaultValue is returned from Bind if the <default> value given in the tag cannot be parsed and assigned to the field.
func (ErrorDefaultValue) Error ¶
func (err ErrorDefaultValue) Error() string
Error implements error.
func (ErrorDefaultValue) Unwrap ¶
func (err ErrorDefaultValue) Unwrap() error
Unwrap implements Unwrap.
type ErrorFlagOverrideUndefined ¶
type ErrorFlagOverrideUndefined struct {
FlagName string
}
ErrorFlagOverrideUndefined is returned by Bind if a flag override tag is defined for a FlagName that has yet to be defined in the flag set.
func (ErrorFlagOverrideUndefined) Error ¶
func (err ErrorFlagOverrideUndefined) Error() string
type ErrorInvalidType ¶
type ErrorInvalidType struct { Type interface{} Nil bool }
ErrorInvalidType is returned from Bind if v is not a pointer to a struct."
type ErrorNestedStruct ¶
ErrorNestedStruct is returned from Bind if a recursive call to bind on a nested struct returns an error.
func (ErrorNestedStruct) Error ¶
func (err ErrorNestedStruct) Error() string
Error implements error.
func (ErrorNestedStruct) Unwrap ¶
func (err ErrorNestedStruct) Unwrap() error
Unwrap implements unwrap.
type FlagSet ¶
type FlagSet interface { Arg(i int) string Args() []string NArg() int NFlag() int Set(name, value string) error Parse([]string) error BoolVar(p *bool, name string, value bool, usage string) DurationVar(p *time.Duration, name string, value time.Duration, usage string) Float64Var(p *float64, name string, value float64, usage string) Int64Var(p *int64, name string, value int64, usage string) IntVar(p *int, name string, value int, usage string) StringVar(p *string, name string, value string, usage string) Uint64Var(p *uint64, name string, value uint64, usage string) UintVar(p *uint, name string, value uint, usage string) }
FlagSet is an interface satisfied by both *flag.FlagSet and *pflag.FlagSet.
type JSONRawMessage ¶
type JSONRawMessage json.RawMessage
func (*JSONRawMessage) Set ¶
func (data *JSONRawMessage) Set(text string) error
func (JSONRawMessage) String ¶
func (data JSONRawMessage) String() string
func (JSONRawMessage) Type ¶
func (data JSONRawMessage) Type() string
type Option ¶
type Option func(*bind)
Option is an option that may be passed to Bind.
func CobraFilter ¶
func NoAutoFlatten ¶
func NoAutoFlatten() Option
By default the flags in embedded struct fields are not given a prefix unless they explicitly have a `flag:"name"` in their tag.
This overrides this behavior so the flags in embedded struct fields are prefixed with their type name unless explicitly flattened with the tag `flag:";;;flatten"`.
type PFlagSet ¶
type PFlagSet interface { Lookup(name string) *pflag.Flag BoolVarP(p *bool, name, short string, value bool, usage string) BoolSliceVarP(p *[]bool, name, shorthand string, value []bool, usage string) DurationVarP(p *time.Duration, name, short string, value time.Duration, usage string) DurationSliceVarP(p *[]time.Duration, name, short string, value []time.Duration, usage string) Float32VarP(p *float32, name, short string, value float32, usage string) Float32SliceVarP(p *[]float32, name, short string, value []float32, usage string) Float64VarP(p *float64, name, short string, value float64, usage string) Float64SliceVarP(p *[]float64, name, short string, value []float64, usage string) Int64VarP(p *int64, name, short string, value int64, usage string) Int64SliceVarP(p *[]int64, name, short string, value []int64, usage string) IntVarP(p *int, name, short string, value int, usage string) IntSliceVarP(p *[]int, name, short string, value []int, usage string) StringVarP(p *string, name, short string, value string, usage string) StringSliceVarP(p *[]string, name, short string, value []string, usage string) Uint64VarP(p *uint64, name, short string, value uint64, usage string) UintVarP(p *uint, name, short string, value uint, usage string) UintSliceVarP(p *[]uint, name, short string, value []uint, usage string) IPVarP(p *net.IP, name, shorthand string, value net.IP, usage string) IPSliceVarP(p *[]net.IP, name, shorthand string, value []net.IP, usage string) VarPF(value pflag.Value, name, short string, usage string) *pflag.Flag Visit(func(*pflag.Flag)) VisitAll(func(*pflag.Flag)) }
PFlagSet is an interface satisfied by *pflag.FlagSet.