configuration

package module
v4.5.1 Latest Latest
Warning

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

Go to latest
Published: Apr 13, 2024 License: MIT Imports: 11 Imported by: 4

README

Go Report Card codecov OpenSSF Best Practices GoDoc Mentioned in Awesome Go

Configuration

is a library for injecting values recursively into structs - a convenient way of setting up a configuration object. Available features:

  • setting default values for struct fields - NewDefaultProvider()
  • setting values from environment variables - NewEnvProvider()
  • setting values from command line flags - NewFlagProvider(&cfg)
  • setting values from a JSON file - NewJSONFileProvider("./testdata/input.json")

Supported types:

  • string, *string, []string, []*string
  • bool, *bool, []bool, []*bool
  • int, int8, int16, int32, int64 + slices of these types
  • *int, *int8, *int16, *int32, *int64 + slices of these types
  • uint, uint8, uint16, uint32, uint64 + slices of these types
  • *uint, *uint8, *uint16, *uint32, *uint64 + slices of these types
  • float32, float64 + slices of these types
  • *float32, *float64 + slices of these types
  • time.Duration from strings like 12ms, 2s etc.
  • embedded structs and pointers to structs
  • any custom type which satisfies FieldSetter interface

Why?

  • your entire configuration can be defined in one model
  • all metadata is in your model (defined with tags)
  • easy to set/change a source of data for your configuration
  • easy to set a priority of sources to fetch data from (e.g., 1.flags, 2.env, 3.default or another order)
  • you can implement your custom provider
  • no external dependencies
  • complies with 12-factor app

Quick start

Import path github.com/BoRuDar/configuration/v4

	// defining a struct
cfg := struct {
    Name     string `flag:"name"`
    LastName string `default:"defaultLastName"`
    Age      byte   `env:"AGE_ENV"    default:"-1"`
    BoolPtr  *bool  `default:"false"`
    
    ObjPtr *struct {
        F32       float32       `default:"32"`
        StrPtr    *string       `default:"str_ptr_test"`
        HundredMS time.Duration `default:"100ms"`
    }

    Obj struct {
        IntPtr     *int16   `default:"123"`
        Beta       int      `file_json:"inside.beta"   default:"24"`
        StrSlice   []string `default:"one;two"`
        IntSlice   []int64  `default:"3; 4"`
        unexported string   `xml:"ignored"`
    }
}{}

configurator := configuration.New(
    &cfg,
    // order of execution will be preserved: 
    configuration.NewFlagProvider(),             // 1st
    configuration.NewEnvProvider(),              // 2nd 
    configuration.NewJSONFileProvider(fileName), // 3rd 
    configuration.NewDefaultProvider(),          // 4th
)

if err := configurator.InitValues(); err != nil {
    log.Fatalf("unexpected error: ", err)
}

If you need only ENV variables and default values you can use a shorter form:

err := configuration.FromEnvAndDefault(&cfg)

Providers

You can specify one or more providers. They will be executed in order of definition:

[]Provider{
    NewFlagProvider(&cfg), // 1
    NewEnvProvider(),      // 2
    NewDefaultProvider(),  // 3
} 

If provider set value successfully next ones will not be executed (if flag provider from the sample above found a value env and default providers are skipped). The value of first successfully executed provider will be set. If none of providers found value - an application will be terminated. This behavior can be changed with configurator.OnFailFnOpt option:

err := configuration.New(
    &cfg,
    configuration.NewEnvProvider(),
    configuration.NewDefaultProvider()).
    SetOptions(
        configuration.OnFailFnOpt(func(err error) {
            log.Println(err)
        }),
    ).InitValues()
Custom provider

You can define a custom provider which should satisfy next interface:

type Provider interface {
    Name() string
    Init(ptr any) error
    Provide(field reflect.StructField, v reflect.Value) error
}
Default provider

Looks for default tag and set value from it:

struct {
    // ...
    Name string `default:"defaultName"`
    // ...
}
Env provider

Looks for env tag and tries to find an ENV variable with the name from the tag (AGE in this example):

struct {
    // ...
    Age      byte   `env:"AGE"`
    // ...
}

Name inside tag env:"<name>" must be unique for each field. Only UPPER register for ENV vars is accepted:

bad_env_var_name=bad
GOOD_ENV_VAR_NAME=good
Flag provider

Looks for flag tag and tries to set value from the command line flag -first_name

struct {
    // ...
    Name     string `flag:"first_name|default_value|Description"`
    // ...
}

Name inside tag flag:"<name>" must be unique for each field. default_value and description sections are optional and can be omitted. NewFlagProvider(&cfg) expects a pointer to the same object for initialization.

Note: if program is executed with -help or -h flag you will see all available flags with description:

Flags: 
	-first_name		"Description (default: default_value)"

And program execution will be terminated.

Options for NewFlagProvider
  • WithFlagSet - sets a custom FlagSet
JSON File provider

Requires file_json:"<path_to_json_field>" tag.

NewJSONFileProvider("./testdata/input.json")

For example, tag file_json:"cache.retention" will assume that you have this structure of your JSON file:

{
  "cache": {
    "retention": 1
  }
}
Additional providers

FieldSetter interface

You can define how to set fields with any custom types:

type FieldSetter interface {
	SetField(field reflect.StructField, val reflect.Value, valStr string) error
}

Example:

type ipTest net.IP

func (it *ipTest) SetField(_ reflect.StructField, val reflect.Value, valStr string) error {
	i := ipTest(net.ParseIP(valStr))

	if val.Kind() == reflect.Pointer {
		val.Set(reflect.ValueOf(&i))
	} else {
		val.Set(reflect.ValueOf(i))
	}

	return nil
}

Contribution

  1. Open a feature request or a bug report in issues
  2. Fork and create a PR into dev branch

Documentation

Overview

Package configuration provides ability to initialize your custom configuration struct from: flags, environment variables, `default` tag, files (json, yaml)

Index

Constants

View Source
const DefaultProviderName = `DefaultProvider`
View Source
const EnvProviderName = `EnvProvider`
View Source
const (
	FlagProviderName = `FlagProvider`
)
View Source
const JSONFileProviderName = `JSONFileProvider`

Variables

View Source
var (
	ErrNoTag                 = errors.New("no tag")
	ErrTagNotUnique          = errors.New("tag is not unique")
	ErrEmptyValue            = errors.New("empty value")
	ErrNotAPointer           = errors.New("not a pointer to a struct")
	ErrNoProviders           = errors.New("no providers")
	ErrProviderNameCollision = errors.New("provider name collision")
)
View Source
var ErrFileMustHaveJSONExt = errors.New("file must have .json extension")

Functions

func FromEnvAndDefault

func FromEnvAndDefault(cfg any) error

FromEnvAndDefault is a shortcut for `New(cfg, NewEnvProvider(), NewDefaultProvider()).InitValues()`.

func NewDefaultProvider

func NewDefaultProvider() defaultProvider

NewDefaultProvider creates new provider which sets values from `default` tag nolint:revive

func NewEnvProvider

func NewEnvProvider() envProvider

NewEnvProvider creates provider which sets values from ENV variables (gets variable name from `env` tag) nolint:revive

func NewFlagProvider

func NewFlagProvider(opts ...FlagProviderOption) flagProvider

NewFlagProvider creates a new provider to fetch data from flags like: --flag_name some_value nolint:revive

func SetField

func SetField(field reflect.StructField, val reflect.Value, valStr string) error

SetField sets field with `valStr` value (and converts it into the proper type beforehand)

func ToPtr added in v4.5.0

func ToPtr[T any](val T) *T

Types

type Configurator

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

func New

func New(
	cfgPtr any,
	providers ...Provider,
) *Configurator

New creates a new instance of the Configurator.

func (*Configurator) InitValues

func (c *Configurator) InitValues() error

InitValues sets values into struct field using given set of providers respecting their order: first defined -> first executed

func (*Configurator) SetOptions

func (c *Configurator) SetOptions(options ...ConfiguratorOption) *Configurator

type ConfiguratorOption

type ConfiguratorOption func(*Configurator)

ConfiguratorOption defines Option function for Configuration

func OnFailFnOpt

func OnFailFnOpt(fn func(error)) ConfiguratorOption

OnFailFnOpt sets function which will be called when an error occurs during Configurator.applyProviders()

type FieldSetter added in v4.2.0

type FieldSetter interface {
	SetField(field reflect.StructField, val reflect.Value, valStr string) error
}

FieldSetter interface

type FileProvider added in v4.4.0

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

func NewJSONFileProvider

func NewJSONFileProvider(fileName string) (fp *FileProvider)

NewJSONFileProvider creates new provider which reads values from JSON files.

func (*FileProvider) Init added in v4.4.0

func (fp *FileProvider) Init(_ any) error

func (*FileProvider) Name added in v4.4.0

func (fp *FileProvider) Name() string

func (*FileProvider) Provide added in v4.4.0

func (fp *FileProvider) Provide(field reflect.StructField, v reflect.Value) error

type FlagProviderOption

type FlagProviderOption func(*flagProvider)

func WithFlagSet

func WithFlagSet(s FlagSet) FlagProviderOption

WithFlagSet allows the flag.FlagSet to be provided to NewFlagProvider. This allows compatibility with other flag parsing utilities.

type FlagSet

type FlagSet interface {
	Parse(arguments []string) error
	String(name string, value string, usage string) *string
}

FlagSet is the part of flag.FlagSet that NewFlagProvider uses

type Provider

type Provider interface {
	Name() string
	Init(ptr any) error
	Provide(field reflect.StructField, v reflect.Value) error
}

Provider defines interface for existing and future custom providers.

Jump to

Keyboard shortcuts

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