flagsfiller

package module
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Nov 19, 2019 License: MIT Imports: 8 Imported by: 31

README

go-flagsfiller

Bring your own struct and make Go's flag package pleasant to use.

Install

go get github.com/itzg/go-flagsfiller

Import

import "github.com/itzg/go-flagsfiller"

Features

  • Populates Go's flag.FagSet from a struct of your choosing
  • By default, field names are converted to flag names using kebab-case, but can be configured.
  • Use nested structs where flag name is prefixed by the nesting struct field names
  • Allows defaults to be given via struct tag default
  • Falls back to using instance field values as declared default
  • Declare flag usage via struct tag usage
  • Easily combines with jamiealquiza/envy for environment variable parsing and google/subcommands for sub-command processing. Can also be integrated with spf13/cobra by using pflag's AddGoFlagSet
  • Beyond the standard types supported by flag.FlagSet also includes support for:
    • []string where repetition of the argument appends to the slice and/or an argument value can contain a comma-separated list of values. For example: --arg one --arg two,three
    • map[string]string where each entry is a key=value and/or repetition of the arguments adds to the map or multiple entries can be comma-separated in a single argument value. For example: --arg k1=v1 --arg k2=v2,k3=v3

Quick example

package main

import (
	"flag"
	"fmt"
	"github.com/itzg/go-flagsfiller"
	"log"
	"time"
)

type Config struct {
	Host         string        `default:"localhost" usage:"The remote host"`
	DebugEnabled bool          `default:"true" usage:"Show debugs"`
	MaxTimeout   time.Duration `default:"5s" usage:"How long to wait"`
	Feature      struct {
		Faster         bool `usage:"Go faster"`
		LudicrousSpeed bool `usage:"Go even faster"`
	}
}

func main() {
	var config Config
    
    // create a FlagSetFiller
	filler := flagsfiller.New()
    // fill and map struct fields to flags
	err := filler.Fill(flag.CommandLine, &config)
	if err != nil {
		log.Fatal(err)
	}

    // parse command-line like usual
	flag.Parse()

	fmt.Printf("Loaded: %+v\n", config)
}

The following shows an example of the usage provided when passing --help:

  -debug-enabled
    	Show debugs (default true)
  -feature-faster
    	Go faster
  -feature-ludicrous-speed
    	Go even faster
  -host string
    	The remote host (default "localhost")
  -max-timeout duration
    	How long to wait (default 5s)

Real world example

saml-auth-proxy shows an end-to-end usage of flagsfiller where the main function fills the flags, maps those to environment variables with envy, and parses the command line:

func main() {
	var serverConfig server.Config

	filler := flagsfiller.New()
	err := filler.Fill(flag.CommandLine, &serverConfig)
	if err != nil {
		log.Fatal(err)
	}

	envy.Parse("SAML_PROXY")
	flag.Parse()

where server.Config is declared as

type Config struct {
	Version                 bool              `usage:"show version and exit"`
	Bind                    string            `default:":8080" usage:"host:port to bind for serving HTTP"`
	BaseUrl                 string            `usage:"External URL of this proxy"`
	BackendUrl              string            `usage:"URL of the backend being proxied"`
	IdpMetadataUrl          string            `usage:"URL of the IdP's metadata XML"`
	IdpCaPath               string            `usage:"Optional path to a CA certificate PEM file for the IdP"`
    // ...see https://github.com/itzg/saml-auth-proxy/blob/master/server/server.go for full set
}

Documentation

Overview

Package flagsfiller makes Go's flag package pleasant to use by mapping the fields of a given struct into flags in a FlagSet.

Quick Start

A FlagSetFiller is created with the New constructor, passing it any desired FillerOptions. With that, call Fill, passing it a flag.FlatSet, such as flag.CommandLine, and your struct to be mapped.

Even a simple struct with no special changes can be used, such as:

type Config struct {
	Host string
	Enabled bool
}
var config Config

// create a FlagSetFiller
filler := flagsfiller.New()
// fill and map struct fields to flags
filler.Fill(flag.CommandLine, &config)
// parse command-line like usual
flag.Parse()

After calling Parse on the flag.FlagSet, the corresponding fields of the mapped struct will be populated with values passed from the command-line.

For an even quicker start, flagsfiller provides a convenience Parse function that does the same as the snippet above in one call:

type Config struct {
	Host string
	Enabled bool
}
var config Config

flagsfiller.Parse(&config)

Flag Naming

By default, the flags are named by taking the field name and performing a word-wise conversion to kebab-case. For example the field named "MyMultiWordField" becomes the flag named "my-multi-word-field".

The naming strategy can be changed by passing a custom Renamer using the WithFieldRenamer option in the constructor.

Nested Structs

FlagSetFiller supports nested structs and computes the flag names by prefixing the field name of the struct to the names of the fields it contains. For example, the following maps to the flags named remote-host, remote-auth-username, and remote-auth-password:

type Config struct {
	Remote struct {
		Host string
		Auth struct {
			Username string
			Password string
		}
	}
}

Flag Usage

To declare a flag's usage add a `usage:""` tag to the field, such as:

type Config struct {
	Host string `usage:"the name of the host to access"`
}

Since flag.UnquoteUsage normally uses back quotes to locate the argument placeholder name but struct tags also use back quotes, flagsfiller will instead use [square brackets] to define the placeholder name, such as:

SomeUrl      string `usage:"a [URL] to configure"`

results in the rendered output:

-some-url URL
	a URL to configure

Defaults

To declare the default value of a flag, you can either set a field's value before passing the struct to process, such as:

type Config struct {
	Host string
}
var config = Config{Host:"localhost"}

or add a `default:""` tag to the field. Be sure to provide a valid string that can be converted into the field's type. For example,

type Config struct {
	Host 	string `default:"localhost"`
	Timeout time.Duration `default:"1m"`
}

String Slices

FlagSetFiller also includes support for []string fields. Repetition of the argument appends to the slice and/or an argument value can contain a comma-separated list of values.

For example:

--arg one --arg two,three

results in a three element slice.

The default tag's value is provided as a comma-separated list, such as

MultiValues []string `default:"one,two,three"`

Maps of String to String

FlagSetFiller also includes support for map[string]string fields. Each argument entry is a key=value and/or repetition of the arguments adds to the map or multiple entries can be comma-separated in a single argument value.

For example:

--arg k1=v1 --arg k2=v2,k3=v3

results in a map with three entries.

The default tag's value is provided a comma-separate list of key=value entries, such as

Mappings map[string]string `default:"k1=v1,k2=v2,k3=v3"`
Example
type Config struct {
	Host    string        `default:"localhost" usage:"The remote host"`
	Enabled bool          `default:"true" usage:"Turn it on"`
	Timeout time.Duration `default:"5s" usage:"How long to wait"`
}

var config Config

flagset := flag.NewFlagSet("ExampleBasic", flag.ExitOnError)

filler := flagsfiller.New()
err := filler.Fill(flagset, &config)
if err != nil {
	log.Fatal(err)
}

err = flagset.Parse([]string{"--host", "external.svc", "--timeout", "10m"})
if err != nil {
	log.Fatal(err)
}

fmt.Printf("%+v\n", config)
Output:

{Host:external.svc Enabled:true Timeout:10m0s}

Index

Examples

Constants

This section is empty.

Variables

View Source
var DefaultFieldRenamer = strcase.ToKebab

DefaultFieldRenamer is used when no WithFieldRenamer option is passed to the FlagSetFiller constructor.

Functions

func Parse added in v1.3.0

func Parse(from interface{}, options ...FillerOption) error

Parse is a convenience function that creates a FlagSetFiller with the given options, fills and maps the flags from the given struct reference into flag.CommandLine, and uses flag.Parse to parse the os.Args. Returns an error if the given struct could not be used for filling flags.

Types

type FillerOption

type FillerOption func(opt *fillerOptions)

FillerOption instances are passed to the FlagSetFiller constructor.

func WithFieldRenamer

func WithFieldRenamer(renamer Renamer) FillerOption

WithFieldRenamer declares an option to customize the Renamer used to convert field names to flag names.

type FlagSetFiller

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

FlagSetFiller is used to map the fields of a struct into flags of a flag.FlagSet

func New

func New(options ...FillerOption) *FlagSetFiller

New creates a new FlagSetFiller with zero or more of the given FillerOption's

func (*FlagSetFiller) Fill

func (f *FlagSetFiller) Fill(flagSet *flag.FlagSet, from interface{}) error

Fill populates the flagSet with a flag for each field in given struct passed in the 'from' argument which must be a struct reference. Fill returns an error when a non-struct reference is passed as 'from' or a field has a default tag which could not converted to the field's type.

type Renamer

type Renamer func(name string) string

Renamer takes a field's name and returns the flag name to be used

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

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