flagutil

package module
v0.11.0 Latest Latest
Warning

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

Go to latest
Published: Sep 21, 2021 License: MIT Imports: 11 Imported by: 4

README

flagutil

GoDoc CI

A library to populate flag.FlagSet from various sources.

Features

  • Uses standard flag package
  • Structured configuration
  • Reads values from multiple sources
  • Ability to write your own parser

Why

Defining your program parameters and reading their values should be as simple as possible. There is no reason to use some library or even monstrous framework instead of standard flag package.

There is Configuration in Go article, which describes in detail the reasons of creating this library.

Available parsers

Note that it is very easy to implement your own Parser.

At the moment these parsers are already implemented:

  • Flag syntax arguments parser
  • Posix program arguments syntax parser
  • Environment variables parser
  • Prompt interactive parser
  • File parsers:
    • json
    • yaml
    • toml

Custom help message

It is possible to print custom help message which may include, for example, names of the environment variables used by env parser. See the WithCustomUsage() parse option.

Custom usage currently looks like this:

Usage of test:
  $TEST_FOO, --foo
        bool
        bool flag description (default false)

  $TEST_BAR, --bar
        int
        int flag description (default 42)

Usage

A simple example could be like this:

package main

import (
	"flag"

	"github.com/gobwas/flagutil"
	"github.com/gobwas/flagutil/parse/pargs"
	"github.com/gobwas/flagutil/parse/file/json"
)

func main() {
	flags := flag.NewFlagSet("my-app", flag.ExitOnError)
	
	port := flag.Int(&port,
		"port", "port",
		"port to bind to",
	)

	// This flag will be required by the file.Parser below.
	_ = flags.String(
		"config", "/etc/app/config.json", 
		"path to configuration file",
	)

	flagutil.Parse(flags,
		// Use posix options syntax instead of `flag` – just to illustrate that
		// it is possible.
		flagutil.WithParser(&pargs.Parser{
			Args: os.Args[1:],
		}),	

		// Then lookup flag values among environment.
		flagutil.WithParser(&env.Parser{
			Prefix: "MY_APP_",
		}),

		// Finally lookup for "config" flag value and try to interpret its
		// value as a path to json configuration file.
		flagutil.WithParser(
			&file.Parser{
				Lookup: file.LookupFlag(flags, "config"),
				Syntax: new(json.Syntax),
			},
			// Don't allow to setup "config" flag from file.
			flagutil.WithStashName("config"),
		),
	)

	// Work with received values.
}

Subsets

flagutil provides ability to define so called flag subsets:

package main

import (
	"flag"

	"github.com/gobwas/flagutil"
	"github.com/gobwas/flagutil/parse/pargs"
	"github.com/gobwas/flagutil/parse/file/json"
)

func main() {
	flags := flag.NewFlagSet("my-app", flag.ExitOnError)
	
	port := flag.Int(&port,
		"port", "port",
		"port to bind to",
	)

	// Define parameters for some third-party library.
	var endpoint string
	flagutil.Subset(flags, "database", func(sub *flag.FlagSet) {
		sub.StringVar(&endpoint,
			"endpoint", "localhost",
			"database endpoint to connect to"
		)
	})
	
	flagutil.Parse(flags,
		flagutil.WithParser(&pargs.Parser{
			Args: os.Args[1:],
		}),	
		flagutil.WithParser(
			&file.Parser{
				Lookup: file.PathLookup("/etc/my-app/config.json"),
				Syntax: new(json.Syntax),
			},
		),
	)

	// Work with received values.
}

The configuration file may look as follows:

{
  "port": 4050,
  
  "database": {
    "endpoint": "localhost:5432",
  }
}

And, if you want to override, say, database endpoint, you can execute your program as follows:

$ app --database.endpoint 4055

Allowing name collisions

It's rare, but still possible, when you want to receive single flag value from multiple places in code. To avoid panics with "flag redefined" reason you can (if you really need to) merge two flag values into single by using flagutil.Merge() function:

var (
	s0 string
	s1 string
)
flag.StringVar(&s0,
	"foo", "default",
	"foo flag usage",
)
flagutil.MergeInto(flag.CommandLine, func(safe *flag.FlagSet) {
	safe.StringVar(&s1,
		"foo", "",
		"foo flag another usage", // This usage will be joined with previous.
	)
})

// After parsing, s0 and s1 will be filled with single `-foo` flag value.
// If value is not provided, both s0 and s1 will have its default values (which
// may be _different_).

Conventions and limitations

Any structure from parsed configuration is converted into a pairs of a flat key and a value. Keys are flattened recursively until there is no such flag defined within flag.FlagSet.

Keys flattening happens just as two keys concatenation with . as a > delimiter.

There are three scenarios when the flag was found:

  1. If value is a mapping or an object, then its key-value pairs are concatenated with : as a delimiter and are passed to the flag.Value.Set() in appropriate number of calls.

  2. If value is an array, then its items are passed to the flag.Value.Set() in appropriate number of calls.

  3. In other way, flag.Value.Set() will be called once with value as is.

Note that for any type of values the flag.Value.String() method is never used to access the "real" value – only for defaults when printing help message. To provide "real" value implementations must satisfy flag.Getter interface.

Suppose you have this json configuration:

{
  "foo": {
    "bar": "1",
    "baz": "2"
  }
}

If you define foo.bar flag, you will receive "1" in a single call to its flag.Value.Set() method. No surprise here. But if you define foo flag, then its flag.Value.Set() will be called twice with "bar:1" and "baz:2".

The same thing happens with slices:

{
  "foo": [
    "bar",
    "baz"
  ]
}

Your foo's flag.Value.Set() will be called twice with "bar" and "baz".

This still allows you to use command line arguments to override or declare parameter complex values:

$ app --slice 1 --slice 2 --slice 3 --map foo:bar --map bar:baz

Misc

Creation of this library was greatly inspired by peterburgon/ff – and I wouldn't write flagutil if I didn't met some design disagreement with it.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var MergeUsage = func(name string, usage0, usage1 string) string {
	return usage0 + " / " + usage1
}

MergeUsage specifies way of joining two different flag usage strings.

View Source
var SetSeparator = "."

Functions

func CombineFlags added in v0.8.0

func CombineFlags(f0, f1 *flag.Flag) *flag.Flag

CombineFlags combines given flags into a third one. Setting value of returned flag will cause both given flags change their values as well. However, flag sets of both flags will not be aware that the flags were set.

Description of each flag (if differ) is joined by MergeUsage().

func CombineSets added in v0.8.0

func CombineSets(fs0, fs1 *flag.FlagSet) *flag.FlagSet

CombineSets combines given sets into a third one. Every collided flags are combined into third one in a way that setting value to it sets value of both original flags.

func Copy added in v0.9.0

func Copy(dst, src *flag.FlagSet)

Copy defines all flags from src with dst. It panics on any flag name collision.

func LinkFlag added in v0.11.0

func LinkFlag(fs *flag.FlagSet, src, dst string)

LinkFlag links dst to be updated when src value is set. It panics if any of the given names doesn't exist in fs.

Note that it caches the both src and dst flag.Value pointers internally, so it is possible to link src to dst and dst to src without infinite recursion. However, if any of the src or dst flag value get overwritten after LinkFlag() call, created link will not work properly anymore.

func MergeInto added in v0.8.0

func MergeInto(super *flag.FlagSet, setup func(*flag.FlagSet))

MergeInto merges new flagset into superset and resolves any name collisions. It calls setup function to let caller register needed flags within subset before they are merged into the superset.

If name of the flag defined in the subset already present in a superset, then subset flag is merged into superset's one. That is, flag will remain in the superset, but setting its value will make both parameters filled with received value.

Description of each flag (if differ) is joined by MergeUsage().

Note that default values (and initial values of where flag.Value points to) are kept untouched and may differ if no value is set during parsing phase.

func OverrideSet added in v0.8.0

func OverrideSet(v flag.Value, f func(string) error) flag.Value

OverrideSet returns a wrapper around v which Set() method is replaced by f.

func Parse

func Parse(ctx context.Context, flags *flag.FlagSet, opts ...ParseOption) (err error)

func PrintDefaults added in v0.3.0

func PrintDefaults(ctx context.Context, flags *flag.FlagSet, opts ...ParseOption) error

PrintDefaults prints parsers aware usage message to flags.Output().

func SetActual added in v0.9.0

func SetActual(fs *flag.FlagSet, name string)

SetActual makes flag look like it has been set within flag set. If flag set doesn't has flag with given SetActual() does nothing. Original value of found flag remains untouched, so it is safe to use with flags that accumulate values of multiple Set() calls.

func Subset

func Subset(super *flag.FlagSet, prefix string, setup func(sub *flag.FlagSet)) (err error)

Subset registers new flag subset with given prefix within given flag superset. It calls setup function to let caller register needed flags within created subset.

Types

type ParseOption

type ParseOption interface {
	// contains filtered or unexported methods
}

type ParseOptionFunc added in v0.4.0

type ParseOptionFunc func(*config)

func WithCustomUsage added in v0.4.0

func WithCustomUsage() ParseOptionFunc

WithCustomUsage makes Parse() to ignore flag.FlagSet.Usage field when receiving flag.ErrHelp error from some parser and print results of flagutil.PrintDefaults() instead.

func WithParseOptions added in v0.5.0

func WithParseOptions(opts ...ParseOption) ParseOptionFunc

func WithParser

func WithParser(p Parser, opts ...ParserOption) ParseOptionFunc

WithParser returns a parse option and makes p to be used during Parse().

func WithUnquoteUsageMode added in v0.3.0

func WithUnquoteUsageMode(m UnquoteUsageMode) ParseOptionFunc

type ParseOrParserOptionFunc added in v0.9.0

type ParseOrParserOptionFunc func(*config, *parser)

func WithAllowResetSpecified added in v0.9.0

func WithAllowResetSpecified() (opt ParseOrParserOptionFunc)

func WithIgnoreUndefined

func WithIgnoreUndefined() (opt ParseOrParserOptionFunc)

WithIgnoreUndefined makes Parse() to not fail on setting undefined flag.

func WithStashName added in v0.4.0

func WithStashName(name string) ParseOrParserOptionFunc

func WithStashPrefix added in v0.4.0

func WithStashPrefix(prefix string) ParseOrParserOptionFunc

func WithStashRegexp added in v0.4.0

func WithStashRegexp(re *regexp.Regexp) ParseOrParserOptionFunc

type Parser

type Parser interface {
	Parse(context.Context, parse.FlagSet) error
}

type ParserFunc added in v0.3.0

type ParserFunc func(context.Context, parse.FlagSet) error

func (ParserFunc) Parse added in v0.3.0

func (fn ParserFunc) Parse(ctx context.Context, fs parse.FlagSet) error

type ParserOption added in v0.3.0

type ParserOption interface {
	// contains filtered or unexported methods
}

type ParserOptionFunc added in v0.4.0

type ParserOptionFunc func(*parser)

func WithResetSpecified added in v0.9.0

func WithResetSpecified() ParserOptionFunc

type Printer added in v0.3.0

type Printer interface {
	Name(context.Context, parse.FlagSet) (func(*flag.Flag, func(string)), error)
}

type PrinterFunc added in v0.3.0

type PrinterFunc func(context.Context, parse.FlagSet) (func(*flag.Flag, func(string)), error)

func (PrinterFunc) Name added in v0.3.0

func (fn PrinterFunc) Name(ctx context.Context, fs parse.FlagSet) (func(*flag.Flag, func(string)), error)

type UnquoteUsageMode added in v0.3.0

type UnquoteUsageMode uint8
const (
	UnquoteNothing UnquoteUsageMode = 1 << iota >> 1
	UnquoteQuoted
	UnquoteInferType
	UnquoteClean

	UnquoteDefault UnquoteUsageMode = UnquoteQuoted | UnquoteInferType
)

func (UnquoteUsageMode) String added in v0.3.0

func (m UnquoteUsageMode) String() string

Directories

Path Synopsis
Package parse provides tools for common flag parsing tasks.
Package parse provides tools for common flag parsing tasks.
env
pargs
Package pargs implements POSIX program argument syntax conventions.
Package pargs implements POSIX program argument syntax conventions.

Jump to

Keyboard shortcuts

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