flags

package
v2.5.1+incompatible Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 3, 2019 License: Apache-2.0 Imports: 6 Imported by: 12

Documentation

Overview

Package flags provides an interface for automatically creating command line options from a struct.

Typically, if one wants to load from a yaml, one has to define a proper struct, then yaml.Unmarshal(), this is all good. However, there are situations where we want to load most of the configs from the file but overriding some configs.

Let's say we use a yaml to config our Db connections and upon start of the application we load from the yaml file to get the necessary parameters to create the connection. Our base.yaml looks like this

base.yaml
---
mysql:
  user: 'foo'
  password: 'xxxxxx'
  mysql_defaults_file: ./mysql_defaults.ini
  mysql_socket_path: /var/run/mysqld/mysqld.sock
  ... more config options ...

we want to load all the configs from it but we want to provide some flexibility for the program to connect via a different db user. We could define a --user command flag then after loading the yaml file, we override the user field with what we get from --user flag.

If there are many overriding like this, manual define these flags is tedious. This package provides an automatic way to define this override, which is, given a struct, it'll create all the flags which are name using the field names of the struct. If one of these flags are set via command line, the struct will be modified in-place to reflect the value from command line, therefore the values of the fields in the struct are overridden

YAML is just used as an example here. In practice, one can use any struct to define flags.

Let's say we have our configration object as the following.

type logging struct {
	 Interval int
	 Path     string
}

type socket struct {
	 ReadTimeout  time.Duration
	 WriteTimeout time.Duration
}

type tcp struct {
	 ReadTimeout time.Duration
	 socket
}

type network struct {
	 ReadTimeout  time.Duration
	 WriteTimeout time.Duration
	 tcp
}

type Cfg struct {
	 logging
	 network
}

The following code

func main() {
  c := &Cfg{}
  flags.ParseArgs(c, os.Args[1:])
}

will create the following flags

-logging.interval int
      logging.interval
-logging.path string
      logging.path
-network.readtimeout duration
      network.readtimeout
-network.tcp.readtimeout duration
      network.tcp.readtimeout
-network.tcp.socket.readtimeout duration
      network.tcp.socket.readtimeout
-network.tcp.socket.writetimeout duration
      network.tcp.socket.writetimeout
-network.writetimeout duration
      network.writetimeout

flags to subcommands are naturally suported.

func main() {
  cmd := os.Args[1]
  switch cmd {
    case "new"
    c1 := &Cfg1{}
    ParseArgs(c1, os.Args[2:])
  case "update":
    c2 := &Cfg2{}
    ParseArgs(c2, os.Args[2:])

  ... more sub commands ...
  }
}

One can set Flatten to true when calling NewFlagMakerAdv, in which case, flags are created without namespacing. For example,

type auth struct {
 Token string
 Tag   float64
}

type credentials struct {
 User     string
 Password string
 auth
}

type database struct {
 DBName    string
 TableName string
 credentials
}

type Cfg struct {
 logging
 database
}

func main() {
 c := &Cfg{}
 flags.ParseArgs(c, os.Args[1:])
}

will create the following flags

-dbname string
      dbname
-interval int
      interval
-password string
      password
-path string
      path
-tablename string
      tablename
-tag float
      tag
-token string
      token
-user string
      user

Please be aware that usual GoLang flag creation rules apply, i.e., if there are duplication in flag names (in the flattened case it's more likely to happen unless the caller make due dilligence to create the struct properly), it panics.

Note that not all types can have command line flags created for. map, channel and function type will not defien a flag corresponding to the field. Pointer types are properly handled and slice type will create multi-value command line flags. That is, e.g. if a field foo's type is []int, one can use --foo 10 --foo 15 --foo 20 to override this field value to be []int{10, 15, 20}. For now, only []int, []string and []float64 are supported in this fashion.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ParseArgs

func ParseArgs(obj interface{}, args []string) ([]string, error)

ParseArgs parses the string arguments which should not contain the program name.

obj is the struct to populate. args are the command line arguments, typically obtained from os.Args.

Types

type FlagMaker

type FlagMaker struct {
	// contains filtered or unexported fields
}

FlagMaker enumerate all the exported fields of a struct recursively and create corresponding command line flags. For anonymous fields, they are only enumerated if they are pointers to structs. Usual GoLang flag rules apply, e.g. duplicated flag names leads to panic.

func NewFlagMaker

func NewFlagMaker() *FlagMaker

NewFlagMaker creates a default FlagMaker which creates namespaced flags

func NewFlagMakerAdv

func NewFlagMakerAdv(options *FlagMakingOptions) *FlagMaker

NewFlagMakerAdv gives full control to create flags.

func NewFlagMakerFlagSet

func NewFlagMakerFlagSet(options *FlagMakingOptions, fs *flag.FlagSet) *FlagMaker

NewFlagMakerFlagSet gives full control to create flags.

func (*FlagMaker) ParseArgs

func (fm *FlagMaker) ParseArgs(obj interface{}, args []string) ([]string, error)

ParseArgs parses the arguments based on the FlagMaker's setting.

func (*FlagMaker) PrintDefaults

func (fm *FlagMaker) PrintDefaults()

PrintDefaults prints the default value and type of defined flags. It just calls the standard 'flag' package's PrintDefaults.

type FlagMakingOptions

type FlagMakingOptions struct {
	// Use lower case flag names rather than the field name/tag name directly.
	UseLowerCase bool
	// Create flags in namespaced fashion
	Flatten bool
	// If there is a struct tag named 'TagName', use its value as the flag name.
	// The purpose is that, for yaml/json parsing we often have something like
	// Foobar string `yaml:"host_name"`, in which case the flag will be named
	// 'host_name' rather than 'foobar'.
	TagName string
	// If there is a struct tag named 'TagUsage', use its value as the usage description.
	TagUsage string
}

FlagMakingOptions control the way FlagMaker's behavior when defining flags.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL