nfigure

package module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Apr 12, 2024 License: MIT Imports: 16 Imported by: 1

README

nfigure - per-library configuration

GoDoc unit tests report card codecov

Install:

go get github.com/muir/nfigure

Nfigure is a reflective configuration library. It supports:

  • Describing what to configure using struct tags
  • Configuration from: multiple configuration file formats, and multiple files
  • Configuration from: environment variables
  • Configuration from: the command line
  • Posix-style and Go-style command line parsing
  • Support for subcommands when parsing command lines
  • Multi-stage binding to allow independently-developed libraries to express their configruration needs ahead of program startup
  • Custom type support using reflectutils.RegisterStringSetter().
  • Filling all normal Go types, array, slices, maps, and time.Duration
  • Ability to export flag-based configuration requests to the "flag" module (useful for libraries)

While nfigure has lots of flexibility and many features, using it should be simple.

Example: In a library, in-house or published for others to use

It can pre-register configuration at the library level, before program startup. This allows library-specific configuration to be handled at the library-level rather than pushed to a central main.

type myLibraryConfig struct {
	Field1	string	  `env="FIELD1" flag:"field1" default:"f1" help:"Field1 controls the first field"`
	Field2	int	  `config:"mylibrary.field2"` 
	Field3	[]string  `flag:"field3"`
}

type MyLibrary struct {
	config	myLibraryConfig
}

func createMyLibrary(nreg *nfigure.Registry) *MyLibrary {
	lib := MyLibrary{}
	nreg.Request(&lib.config,
		nfigure.Prefix("myLibrary"),
		nfigure.ConfigFileFrom(`env="MYLIBRARY_CONFIG_FILE" flag:"mylibrary-config"`),
	)
	_ = nreg.Configure()
	return &lib
}

Example: At the program level

This is an example using nserve. Where this gets interesting is if multiple binaries are built from the same source, the set of libraires can exist in a list and only the ones that are needed for particular executables will have their configuration evaluated.

type programLevelConfig struct {
	Field1	string `env="field1" default:"F1"`
	Field4	float64	`flag:"field4" default:"3.9"`
}

func createMyApp(myLibrary *mylibrary.MyLibrary, nreg *nfigure.Registery) error {
	// start servers, do not return until time to shut down
	var config programLevelConfig
	nreg.Request(&config, "main")
	_ = nreg.Configure()
}

func main() {
	app, _ := nserve.CreateApp("myApp", 
		nfigure.NewRegistryFactory(),
		createMyLibrary, 
		createMyApp)
	_ = app.Do(nserve.Start)
	_ = app.Do(nserve.Stop)
}

Supported tags

Assuming a command line parser was bound, the follwing tags are supported:

  • nfigure: the meta tag, used to control filler interactions
  • default: a filler that provides a literal value
  • env: fill values from environment variables
  • config: fill values from configuration files
  • flag: fill values from the command line (Go style or Posix style)
  • help: per-item help text for command line Usage

Environment variables

Usage:

  • env:"VARNAME" specifies that a value can or should be loaded from an environment variable

Command line parsing

Both Go-style and Posix-style command line parsing is supported. In addition to the base features, counters are supported. Filling maps, arrays, and slices is supported.

  • flag:"name" specifies the name of the command line flag to fill a value.
  • flag:"name n" specifies a single letter alternative
  • flag:"name,split=comma for array values, specifies that strings will be split on comma, flag can only be given once
  • flag:"name,explode=true for array values, specifies that the flag can be given multiple times and is not split
  • flag:"name,counter for numberic values, counts the number of times the flag is used, flag cannot take argument
  • flag:"name,map=explode,split=equal for maps, support -name a=b -name b=c
  • flag:"name,map=prefix for maps, support --namex=a --nameb=c
Posix-style

When using Posix-style flags (PosixFlagHandler()), flags whose names are only a single rune can be combined on the command line:

--debug 
-d
--verbose
-v
-dv (debug and verbose)

For boolean values, negation is "--no-":

--no-verbose

Best Practices

Best Practices for existing libraries

Libraries that are already published and using the standard "flag" package can be refactored to use nfigure. If they register themselves with flag during init, then that behavior should be retained:

package mylibrary 

import (
	"flag"
	"github.com/muir/nfigure"
)

type MyConfig struct {
	MyField string `flag:"myfield"`
}
var myConfig MyConfig

sub init() {
	err := nfigure.ExportToFlagSet(flag.CommandLine, "flag", &myConfig)
	if err != nil {
		panic(err.Error())
	}
}

In a program that is using nfigure, MyConfig can be explicitly imported:

registery.Request(&mylibrary.MyConfig)

However, if there are other libraries that only support "flag" and they're being imported:

GoFlagHandler(nfigure.ImportFlagSet(flag.CommandLine))

Then MyConfig should not also be explicity imported since that would end up with the flags being defined twice.

Best practices for new libraries

New libraries should use nfigure to handle their configruation needs. The suggested way to do this is to have a New function that takes a registry as arguments.

Separate New() and Start() so that configuation can happen after New() but before Start().

Users of your library can use NewWithRegistry() if they're using nfigure. For other users, they can fill MyConfig by hand or use "flag" to populate the configuration

Library writer boilerplate
import "github.com/muir/nfigure"

func NewWithRegistry(registry *nfigure.Registry) MySelf {
	var config Config
	registry.Request(&config)
	...
}

func NewWithConfig(config *Config) MySelf {
	...
}

func (m MySelf) Start() {
	...
}
Library user not using nfigure boilerplate:
import (
	"flag"
	"library"
	"github.com/muir/nfigure"
)

func main() {
	var libConfig library.Config
	nfigure.MustExportToFlagSet(flag.CommandLine, "flag", &libConfig)
	lib := library.NewWithConfig(libConfig) // config is not used yet
	flag.Parse()
	lib.Start() // call to Start must be after config is filled in
}
Library user using nfigure boilerplate:
import (
	"flag"
	"library"
	"github.com/muir/nfigure"
)

func main() {
	registry := nfigure.NewRegistry(WithFiller("flag", GoFlagHandler()))
	lib := registry.NewWithRegistry(registry)
	registry.Configure()
	lib.Start()
}
Best practices for program writers

Use nfigure everywhere! Be careful not to combine ImportFlagSet with registry.Request() of the same models that are ExportToFlagSet()ed in library inits.

Separate library creation from library starting. Allow configuration to be deferred until until library start.

Documentation

Overview

Package nfigure uses reflection to fill configuration structs from command line arguments, environment variables, and configuration files.

The basic starts with NewRegistry(). Use functional args to control the set of fillers (environment, files, etc). Call Request() to add models (structs) that need to be filled out. Call ConfigFile() to add configuration files.

Once that's done, call Configure() to actually fill the structs.

For file fillers (filling from a configuration file), all data elements that are exported will be filled if there is a matching element in a configuration file. Disable filling an element by overriding its fill name to "-".

The default set of supported tags are:

nfigure: meta tag, override with WithMetaTag()
config:  config file filler
env:     environment variable filler
default: defaults filler

With the default tags, the behavior of an exported struct field with no tags matches the behavior of explicitly setting tags like:

type MyStruct struct {
	Exported string `config:"Exported" env:"-" default:"" nfigure:",!first,desc"`
}

That is to say that the field will be filled by a matching name in a configuration file, but it has no default and will not be filled from environment variables.

The meta tag, (default is "nfigure") controls behavior: it provides a default name for recursion, controls if the first, or last value is taken if multiple fillers can provide values. It controls if a sub-struct should be descended into.

By default, no command-line parser is included.

The general form for the tag parameters for handlers and the meta control is tag:"name,key=value,flag,!flag".

The name parameter, which is always first, specifies the name to match on. This overrides the field name. If you want to just use the field name, leave the name parameter empty. Do not skip it! A comma is good enough:

type MyStruct struct {
	Exported string `config:","`
}

The special value of "-" means to skip that filler entirely for this field.

Boolean parameters can be specified after the name. The following all mean false:

!flag
flag=false
flag=0
flag=n

Likewise, "flag,flag=true,flag=1,flag=y" is highly redundant, setting flag to be true four times.

Each filler defines it's own parameters. Here's a summary:

WithMetaTag(), the meta control, "nfigure":
name (defaults to "")
last: use the value from the last filler that has one
desc: descend into structures even if a filler has provided a value
combine: merge multiple sources for arrays/maps

NewEnvFiller(), environment variables, "env":
name (defaults to "-")
split: how to split strings into array/slice elements

NewFileFiller(), fill from config files, "config":
name (defaults to exported field name)

PosixFlagHandler()/GoFlagHandler, fill from the command line:
name
split: how to split strings into array/slice/map key/value elements
	special values: explode, quote, space, comma, equal, equals, none
map: how to treat maps, values are "explode" or "prefix"
counter: is this a numeric counter (eg: foo -v -v -v)
required: is this a required flag
argName: how to describe parameters for this in the usage message

Ultimately setting variables based on string values is done by https://pkg.go.dev/github.com/muir/reflectutils#MakeStringSetter See the documentation there for support for customer types.

Known bugs / limitations:

Combining of arrays, slices, and maps from multiple sources only works if all the sources are configuration files.

Example (Usage)
package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/muir/commonerrors"
)

type arguments struct {
	User      string          `flag:"u user,required,argName=email" help:"email address"`
	Hosts     []string        `flag:"host h,split=&"`
	Confusion map[int]float64 `flag:"confusion C,map=prefix"`
	OMap      map[string]bool `flag:"oset,split=/"`
}

func main() {
	fh := PosixFlagHandler(PositionalHelp("file(s)"))
	os.Args = strings.Split("program --flag-not-defined", " ")
	registry := NewRegistry(WithFiller("flag", fh))
	var arguments arguments
	_ = registry.Request(&arguments)
	err := registry.Configure()
	if commonerrors.IsUsageError(err) {
		fmt.Println(fh.Usage())
	}
}
Output:

Usage: program [-options args] -u email [parameters] file(s)

Options:
     -u email                       email address
     [--host=Hosts&Hosts...]        [-h Hosts&Hosts...]  set Hosts ([]string)
     [--confusion<int>=<N.N>]       [-C<int>=<N.N>]  set Confusion (map[int]float64)
     [--oset key/true|false]        set OMap (map[string]bool)

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExportToFlagSet

func ExportToFlagSet(fs FlagSet, tagName string, model interface{}, opts ...FlaghandlerOptArg) error

ExportToFlagSet provides a way to use the regular "flag" package to when defining flags in a model. This provides a compatibility option for library writers so that if nfigure is not the primary configuration system for a program, flag setting by libraries is still easy.

flag.CommandLine is the default FlagSet.

Only some of the FlaghandlerOptArgs make sense in this context. The others will be ignored.

ExportToFlagSet only exports flags.

Subcommands are not supported by the "flag" package and will be ignored by ExportToFlagSet. Counters are not supported by the "flag" package and will be treated as numerical types.

If a flag has multiple aliases, only the first name will be used.

func MustExportToFlagSet added in v0.1.0

func MustExportToFlagSet(fs FlagSet, tagName string, model interface{}, opts ...FlaghandlerOptArg)

MustExportToFlagSet wraps ExportToFlagSet with a panic if the export fails

Types

type CanAddConfigFileFiller

type CanAddConfigFileFiller interface {
	Filler
	// If the file type is not supported by this filler, then
	// nflex.UnknownFileTypeError must be returned.
	AddConfigFile(file string, keyPath []string) (Filler, error)
}

CanAddConfigFileFiller indicates AddConfigFile is supported

type CanConfigureCompleteFiller

type CanConfigureCompleteFiller interface {
	Filler
	// ConfigureComplete is called by Registry.Configure() when all configuration is complete.
	// This is currently skipped for Fillers that are subcommand specific.
	ConfigureComplete() error
}

CanConfigureCompleteFiller indicates that ConfigureComplete is supported

type CanKeysFiller

type CanKeysFiller interface {
	Filler
	// for filling maps
	Keys(t reflect.Type, tag reflectutils.Tag, firstFirst bool, combineObjects bool) ([]string, bool)
}

CanKeysFiller indicates that Keys() is supported

type CanLenFiller

type CanLenFiller interface {
	Filler
	// for filling arrays & slices
	Len(t reflect.Type, tag reflectutils.Tag, firstFirst bool, combineObjects bool) (int, bool)
}

CanLenFiller indicates that Len() is supported

type CanPreConfigureFiller

type CanPreConfigureFiller interface {
	Filler
	// PreConfigure is called by nfigure.Registry once just before configuration starts
	PreConfigure(tagName string, request *Registry) error
}

CanPreConfigureFiller indicates that PreConfigure is supported

type CanPreWalkFiller

type CanPreWalkFiller interface {
	Filler
	// PreWalk is called from nfigure.Request only on every known (at that time) configuration
	// struct before any call to Fill()
	PreWalk(tagName string, model interface{}) error
}

CanPreWalkFiller indicates the PreWalk is supported

type CanRecurseFiller

type CanRecurseFiller interface {
	Filler
	// Recurse is called during the filling process to indicate
	// that we are now filling a sub-struct, array element, map
	// element etc.
	//
	// If the filler knows that with the recursion it will no longer
	// try to fill anything, it can return nil for it's replacment
	// filler.
	Recurse(name string) (Filler, error)
}

CanRecurseFiller indicates that Recurse() is supported

type FileFiller

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

FileFiller implements the Filler contract

func NewFileFiller

func NewFileFiller(opts ...FileFillerOpts) FileFiller

NewFileFiller creates a CanAddConfigFileFiller filler that implements AddConfigFile. Unlike most other fillers, file fillers will fill values without explicit tags by matching config fields to struct field names.

To prevent a match, tag it with "-":

type MyStruct struct {
	PrivateField string `config:"-"`       // don't fill this one
	MyField      string `config:"myField"` // fill this one
}

func (FileFiller) AddConfigFile

func (s FileFiller) AddConfigFile(path string, keyPath []string) (Filler, error)

AddConfigFile is invoked by Registry.ConfigFile to note an additional file to fill.

func (FileFiller) Fill

func (s FileFiller) Fill(
	t reflect.Type,
	v reflect.Value,
	tag reflectutils.Tag,
	firstFirst bool,
	combineObjects bool,
) (bool, error)

Fill is part of the Filler contract and is called by registry.Configure()

func (FileFiller) Keys

func (s FileFiller) Keys(t reflect.Type, tag reflectutils.Tag, first, combine bool) ([]string, bool)

Keys is part of the CanKeysFiller contract and is called by registry.Configure()

func (FileFiller) Len

func (s FileFiller) Len(t reflect.Type, tag reflectutils.Tag, firstFirst bool, combineObjects bool) (int, bool)

Len is part of the CanLenFiller contract and is called by registry.Configure()

func (FileFiller) Recurse

func (s FileFiller) Recurse(name string) (Filler, error)

Recurse is part of the CanRecurseFiller contract and is called by registry.Configure()

type FileFillerOpts

type FileFillerOpts func(*FileFiller)

FileFillerOpts is a functional arugment for NewFileFiller()

func WithUnmarshalOpts

func WithUnmarshalOpts(opts ...nflex.UnmarshalFileArg) FileFillerOpts

WithUnmarshalOpts passes through to https://pkg.go.dev/github.com/muir/nflex#UnmarshalFile

type Filler

type Filler interface {
	// Fill is what's used to populate data in a configuration struct.
	// Fillers can choose: they can fill structs, maps, arrays, slices,
	// and pointers or they can wait for Recurse to be called and then
	// Fill to be called on slice items and struct fields and map values.
	// Map keys must come from Keys() or the struct as a whole.
	Fill(t reflect.Type, v reflect.Value, tag reflectutils.Tag, firstFirst bool, combineObjects bool) (filledAnything bool, err error)
}

Filler s are applied recursively to structures that need to be filled.

func NewDefaultFiller

func NewDefaultFiller(opts ...LookupFillerOpt) Filler

NewDefaultFiller creates a LookupFiller that simply fills in the value provided into the variable. Comma (",") is not allowed in the values because that is used to introduce options common to LookupFiller.

NewRegistry includes maps "default" to such a filler. To use "dflt" instead, add the following optional arguments to your NewRegistry invocation:

WithFiller("default", nil),
WithFiller("dflt", NewDefaultFiller)

To fill a slice, set a split value:

type MyStruct struct {
	Users []string `default:"root|nobody,split=|"`
}

func NewEnvFiller

func NewEnvFiller(opts ...LookupFillerOpt) Filler

NewEnvFiller creates a LookupFiller that looks up variables from the environment. NewRegistry includes maps "env" to such a filler.

The first word after the tag name is the name of the environment variable.

Options are:

"split" tag that can be used to specify how to split up a value when filling an array or slice from a single string. Special values for split are: "none", "comma", "equals".

"JSON" tag that specifies that the the string should be decoded as JSON. This overrides other decoding possibilities.

type SubStruct struct {
	Foo	     int  `json:"foo"`
}

type MyStruct struct {
	WeightFactor float64  `env:"WEIGHT_FACTOR"`
	Groups       []string `env:"GROUPS,split=|"`
	Foo	     *Foo     `env:",JSON"
}

func NewLookupFiller

func NewLookupFiller(
	lookup func(key string, tag string) (value string, ok bool, err error),
	opts ...LookupFillerOpt,
) Filler

NewLookupFiller creates a LookupFiller from a function that can return error in addition to a value and an indicator if a value is returned. An error return will likely cause program termination so it should be used when there is something blocking the ability to look up a value.

func NewLookupFillerSimple

func NewLookupFillerSimple(lookup func(string) (value string, ok bool), opts ...LookupFillerOpt) Filler

NewLookupFillerSimple creates a LookupFiller from a function that does a simple lookup like os.LookupEnv().

type FlagHandler

type FlagHandler struct {
	Parent *FlagHandler // set only for subcommands
	// contains filtered or unexported fields
}

FlagHandler is the common type for both PosixFlagHanlder() and GoFlagHandler(). The set of flags are found in struct tags, by default with the "flag" prefix.

type MyFlags struct {
	Verbose  int      `flag:"v verbose,counter"`    // each "-v" or "--verbose" increments the integer
	Comments []string `flag:"comment c,split=none"` // "-c value" and "--comment value" can be given multiple times
}

The first argument after flag: is the name or names of the flag. After that there are options. The supported options are:

"map=explode|prefix": specifies how to handle map types. With "map=explode", key/value pairs are given as arguments after the flag:

type MyFlags struct {
	Env map[string]string `flag:"env e,map=explode,split=equals"`
}

cmd --env FOO=bar --env XYZ=yes -e MORE=totally

With "map=prefix", the values are combined into the flag:

type MyFlags struct {
	Defs map[string]string `flag:"D,map=prefix"`
}

cmd -DFOO=bar -DXYZ=yes -DMORE=totally

The default is "map=explode"

"split=x": For arrays, slices, and maps, changes how single values are split into groups.

The special values of "none", "equal", "equals", "comma", "quote", and "space" translate to obvious values.

The default value is "," for arrays and slices and "=" for maps. For "map=prefix", only "=" is supported.

To indicate that a numeric value is a counter, use "counter".

To indicate that a value is required as a flag, use "required".

To tweak the usage message describing the value use "argName=name".

struct MyFlags struct {
	Depth      int `flag:"depth,required,argName=levels"`
	DebugLevel int `flag:"d,counter"`
}

FlagHandler implements the Filler interface

func GoFlagHandler

func GoFlagHandler(opts ...FlaghandlerOptArg) *FlagHandler

GoFlagHandler creates and configures a flaghandler that mirrors Go's native "flag" package in behavior. Long-form flags can have a single dash or double dashes (-flag and --flag).

Assignment or positional args are both supported -flag=value and -flag value.

Flags are found using struct tags. See the comment FlagHandler for details

func PosixFlagHandler

func PosixFlagHandler(opts ...FlaghandlerOptArg) *FlagHandler

PosixFlagHandler creates and configures a flaghandler that requires long options to be preceded with a double-dash and will combine short flags together.

Long-form booleans can be set to false with a "no-" prefix.

tar -xvf f.tgz --numeric-owner --hole-detection=raw --ownermap ownerfile --no-overwrite-dir

Long-form options require a double-dash (--flag). Flag values can be set two ways: "--flag=value" or "--flag value".

Multiple short-flags (-a -b -c) can be combined (-abc). Short flags that are not booleans or counters have arguments that follow. When combined they remain in the same order. The following are the same, assuming -a and -b are both short form flags that take an argument:

-a x -b y
-ab x y

Booleans are set with "--flag" or unset with "--no-flag".

Flags are found using struct tags. See the comment FlagHandler for details

func (*FlagHandler) AddSubcommand

func (h *FlagHandler) AddSubcommand(command string, usageSummary string, configModel interface{}, opts ...FlaghandlerOptArg) (*FlagHandler, error)

AddSubcommand adds behavior around the non-flags found in the list of arguments. An argument matching the "command" argument string will eventually trigger calling that subcommand. After a subcommand, only flags defined in the "configModel" argument will be recognized. Use OnStart to invoke the subcommand.

The "usageSummary" string is a one-line description of what the subcommand does.

func (*FlagHandler) ConfigureComplete

func (h *FlagHandler) ConfigureComplete() error

ConfigureComplete is part of the Filler contract. It is called by Registery.Configure

func (*FlagHandler) Fill

func (h *FlagHandler) Fill(
	t reflect.Type,
	v reflect.Value,
	tag reflectutils.Tag,
	firstFirst bool,
	combineObjects bool,
) (bool, error)

Fill is part of the Filler interface and will be invoked by Registry.Configure().

Fill may be called multiple times for the same field: if it's a pointer, then fill will first be called for it as a pointer, and then later it will be called for it as a regular value. Generally, we only want to respond when called as the regular value.

func (*FlagHandler) PreConfigure

func (h *FlagHandler) PreConfigure(tagName string, registry *Registry) error

PreConfigure is part of the Filler contract. It is called by Registery.Configure

func (*FlagHandler) PreWalk

func (h *FlagHandler) PreWalk(tagName string, model interface{}) error

PreWalk is part of the Filler contract and is invoked by Registry.Configure()

PreWalk examines configuration blocks and figures out the flags that are defined. It's possible that more than one field in various config blocks references the same flag name.

func (*FlagHandler) Remaining added in v0.3.0

func (h *FlagHandler) Remaining() []string

Remaining returns the arguments that were not consumed from os.Args. The other way to get the remaining arguments is to add an OnStart callback.

func (*FlagHandler) Usage

func (h *FlagHandler) Usage() string

Usage produces a usage summary. It is not called automatically unless WithHelpText is used in creation of the flag handler.

type FlagSet added in v0.1.0

type FlagSet interface {
	BoolVar(*bool, string, bool, string)
	StringVar(*string, string, string, string)
	DurationVar(*time.Duration, string, time.Duration, string)
	IntVar(*int, string, int, string)
	Int64Var(*int64, string, int64, string)
	UintVar(*uint, string, uint, string)
	Uint64Var(*uint64, string, uint64, string)
	Float64Var(*float64, string, float64, string)
	Func(string, string, func(string) error)
	Parsed() bool
	VisitAll(func(*flag.Flag))
}

FlagSet is a subset of what flag.FlagSet supports, defined as an interface to lesson the dependency on flag.

type FlaghandlerOptArg

type FlaghandlerOptArg func(*FlagHandler) error

FlaghandlerOptArg are options for flaghandlers

func FlagHelpTag

func FlagHelpTag(helpTagName string) FlaghandlerOptArg

FlagHelpTag specifies the name of the tag to use for providing per-flag help summaries. For example, you may want:

type MyConfig struct {
	User string `flag:"u,argName=email" help:"Set email address"`
}

The default is "help". To just change how the flag arguments are displayed use "argName" in the "flag" tag.

func ImportFlagSet

func ImportFlagSet(fs FlagSet) FlaghandlerOptArg

ImportFlagSet pulls in flags defined with the standard "flag" package. This is useful when there are libaries being used that define flags.

flag.CommandLine is the default FlagSet.

ImportFlagSet is not the recommended way to use nfigure, but sometimes there is no choice.

func OnActivate

func OnActivate(chain ...interface{}) FlaghandlerOptArg

OnActivate is called before flags are parsed. It's mostly for subcommands. The callback will be invoked as soon as it is known that the subcommand is being used.

func OnStart

func OnStart(chain ...interface{}) FlaghandlerOptArg

OnStart is called at the end of configuration. It does not need to return until the program terminates (assuming there are no other Fillers in use that take action during ConfigureComplete and also assuming that there isn't an OnStart in at the subommmand level also).

func PositionalHelp

func PositionalHelp(positionalHelp string) FlaghandlerOptArg

PositionalHelp provides a help string for what to display in the usage summary after the flags, and options. For example: "file(s)"

func WithDefaultsTag

func WithDefaultsTag(defaultTag string) FlaghandlerOptArg

WithDefaultsTag is only relevant when used with ExportToFlagSet(). It overrides the tag used for finding default values. The default default tag is "default". Default values are only available for some kinds of flags because the "flag" package does not support defaults on flags that are defined with functions. Defaults are available for:

bool
duration
float64
int
int64
string
uint
uint64

func WithHelpText

func WithHelpText(helpText string) FlaghandlerOptArg

WithHelpText adds to the usage output and establishes a "--help" flag and also a "help" subcommand (if there are any other subcommands). If there are other subcommands, it is recommended that with WithHelpText be used to set help text for each one.

type LookupFiller

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

LookupFiller is a variable provider that is based upon looking up strings based on their name. An example is filling data from environment variables.

func (LookupFiller) Fill

func (e LookupFiller) Fill(
	t reflect.Type,
	v reflect.Value,
	tag reflectutils.Tag,
	firstFirst bool,
	combineObjects bool,
) (bool, error)

Fill is part of the Filler contract. It is used by Registry.Configure.

func (LookupFiller) Len

func (e LookupFiller) Len(
	t reflect.Type,
	tag reflectutils.Tag,
	firstFirst bool,
	combineObjects bool,
) (int, bool)

Len is part of the Filler contract

type LookupFillerOpt

type LookupFillerOpt func(*LookupFiller)

LookupFillerOpt are options for creating LookupFillers

func WrapLookupErrors

func WrapLookupErrors(f func(error) error) LookupFillerOpt

WrapLookupErrors applies a transformation to errors returned by LookupFillers.

type Registry

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

Registry is the overall configuration context that is shared among sources of configuration (Filler interface) and consumers of configuration (Requests).

func NewRegistry

func NewRegistry(options ...RegistryFuncArg) *Registry

NewRegistry creates a configuration context that is shared among sources of configuration (Filler interface) and consumers of configuration (Requests). Eventually call Configure() on the returned registry.

The following default Fillers are registered:

env		fill from environment variables
config		fill from configuration files
default		fill from the tag value

Use WithFiller to adjust the set of fillers and to add a command-line flags filler.

By default, no command-line filler is used. If you want one, add WithFiller(PosixFlagHanlder()) or WithFiller(GoFlagHandler()) to NewRegistry's functional parameters.

func (*Registry) ConfigFile

func (r *Registry) ConfigFile(path string, prefix ...string) error

ConfigFile adds a source of configuration to all Fillers that implement CanAddConfigFileFiller will be be offered the config file.

func (*Registry) Configure

func (r *Registry) Configure() error

Configure evaluates all configuration requests. New configuration requests can be added while configure is running. For example, by having a struct field that implements ConfigureReactive. New configuration files can also be added while Configure is running but the new data will only be used for configuration that has not already happened.

func (*Registry) Request

func (r *Registry) Request(model interface{}, options ...RegistryFuncArg) error

Request regsiters a struct to be filled in when configuration is done. The model should be a pointer to a struct.

type RegistryFuncArg

type RegistryFuncArg func(*registryConfig)

RegistryFuncArg is used to set Registry options.

func FromRoot

func FromRoot(keys ...string) RegistryFuncArg

FromRoot is intened for use when creating a Request rather than creating a Registry. Not that it won't work for a registry, but it's more useful at the Request level.

FromRoot specifies a path prefix for how the request "mounts" into the configuration hierarchy.

func WithFiller

func WithFiller(tag string, filler Filler) RegistryFuncArg

WithFiller provides a source of configuration to a Registry.

The tag parameter specifies how to invoke that source of configuration. For example, if you have a function to lookup information from KMS, you might register it as the "secret" filler:

myStruct := struct {
	DbPassword string `secret:"dbpasswd"`
}

registry := NewRegistry(WithFiller("secret", NewLookupFiller(myFunc)))
registry.Request(myStruct)

If the filler is nil, then any pre-existing filler for that tag is removed.

func WithMetaTag

func WithMetaTag(tag string) RegistryFuncArg

WithMetaTag specifies the name of the meta tag.

The default meta tag is "nfigure".

Meta-level controls in struct tags can control the name for recursion (over-ridden by filler-level tags) and the behavior for when multiple fillers can potentially provide values.

The first meta tag value is positional and is the name used for recursion or "-" to indicate that no filling should happen.

If "first" is true then filling stops after the first filler succeeds in filling anything. This is the default.

If "last" is true then filling starts with the last filler and stops when a filler succeeds

If "desc" is false then filling doesn't descend into the keys, elements, values of something that has been filled at a higher level.

If "combine" is true, then multple sources can be combined together when filling arrays, slices, and maps

func WithValidate

func WithValidate(v Validate) RegistryFuncArg

WithValidate registers a validation function to be used to check configuration structs after the configuration is complete. Errors reported by the validation function will be wrapped with commonerrors.ValidationError and returned by Registry.Configgure()

func WithoutFillers

func WithoutFillers() RegistryFuncArg

WithoutFillers drops all of the fillers that have been registered so far, including the default ones.

type Request

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

Request tracks a config struct that needs to be filled in.

func (*Request) Registry

func (r *Request) Registry() *Registry

Registry is a getter: it returns the Registry that the Request is bound to.

type RequestFuncArg

type RequestFuncArg func(*Request)

RequestFuncArg is type functional argument type used by Registry.Request().

type Validate

type Validate interface {
	Struct(s interface{}) error
	// StructPartial will only be called with a single Field
	StructPartial(s interface{}, fields ...string) error
}

Validate is a subset of the Validate provided by https://github.com/go-playground/validator, allowing other implementations to be provided if desired

Jump to

Keyboard shortcuts

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