Documentation ¶
Overview ¶
Example ¶
package main import ( "fmt" "log" "os" "path" "strings" "time" "github.com/brumhard/alligotor" ) type Config struct { WillStayDefault string // not overwritten in any source -> will keep default SomeList []string `description:"list that can ba assigned in format: a,b,c"` // description used for flag usage SomeMap map[string]string `config:"env=MAP"` // overwrite the env var to read SomeCustomType SomeCustomType `config:"env=custom"` // implements text.Unmarshaler API struct { Enabled *bool // pointers to basic types are also supported } DB struct { HostName string `config:"flag=h host"` // overwrite the cli flags to read, h is shortname Timeout time.Duration `config:"flag=time"` // only overwrite long name } TimeStamp time.Time `config:"file=custom"` // implements text.Unmarshaler, overwrite key in file Everything string `config:"env=every,flag=e every,file=every"` // set overwrites for every source } func main() { dir, _ := os.MkdirTemp("", "testing") defer os.RemoveAll(dir) jsonBytes := []byte(`{ "custom": "2007-01-02T15:04:05Z", "db": { "timeout": "2m0s" } }`) os.Args = []string{"cmdName", "--somelist", "a,b,c", "--api.enabled", "true", "-h", "dbhost", "--every", "every"} _ = os.Setenv("TEST_MAP", "a=a,b=b,c=c") _ = os.Setenv("TEST_DB_TIMEOUT", "1m0s") _ = os.Setenv("TEST_CUSTOM", "key=value") filePath := path.Join(dir, "example_config.json") _ = os.WriteFile(filePath, jsonBytes, os.ModePerm) cfg := Config{WillStayDefault: "yessir"} // The order of sources will also set the order in which the sources overwrite each other. // That's why the db timeout set in the json is overwritten with the one set in env variable. cfgReader := alligotor.New( alligotor.NewFilesSource(path.Join(dir, "example_config.*")), alligotor.NewEnvSource("TEST"), alligotor.NewFlagsSource(), ) // There's also a default reader, that can be used with alligotor.Get(). if err := cfgReader.Get(&cfg); err != nil { log.Fatal(err) } fmt.Println( cfg.WillStayDefault, cfg.SomeList, cfg.SomeMap, cfg.SomeCustomType, *cfg.API.Enabled, cfg.DB.HostName, cfg.DB.Timeout, cfg.TimeStamp.UTC(), cfg.Everything, ) } type SomeCustomType struct { key string value string } func (s *SomeCustomType) UnmarshalText(text []byte) error { split := strings.SplitN(string(text), "=", 2) for i := range split { split[i] = strings.TrimSpace(split[i]) } s.key = split[0] s.value = split[1] return nil } func (s SomeCustomType) String() string { return fmt.Sprintf("%s=%s", s.key, s.value) }
Output: yessir [a b c] map[a:a b:b c:c] key=value true dbhost 1m0s 2007-01-02 15:04:05 +0000 UTC every
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrPointerExpected = errors.New("expected a pointer as input") ErrStructExpected = errors.New("expected pointer to struct as input") ErrTypeMismatch = errors.New("type mismatch when trying to assign") ErrDuplicateConfigKey = errors.New("key already used for a config source") )
var ( ErrMalformedFlagConfig = errors.New("malformed flag config strings") ErrHelp = errors.New("help requested") )
var DefaultCollector = &Collector{ Sources: []ConfigSource{ NewFilesSource("./config.*"), NewEnvSource(""), NewFlagsSource(), }, }
DefaultCollector is the default Collector and is used by Get.
var ErrFileFormatNotSupported = errors.New("file format not supported or malformed content")
Functions ¶
func Get ¶
func Get(v interface{}) error
Get is a wrapper around DefaultCollector.Get. All predefined configuration sources are enabled. For environment variables it uses no prefix and "_" as the separator. For flags it use "-" as the separator. For config files it uses "config" as the basename and searches in the current directory.
Types ¶
type Collector ¶
type Collector struct {
Sources []ConfigSource
}
Collector is the root struct that implements the main package api. The only method that can be called is Collector.Get to unmarshal the found configuration values from the configured sources into the provided struct. If the default configuration suffices your needs you can just use the package level Get function instead without initializing a new Collector struct.
The order in which the different configuration sources overwrite each other can be configured by the order in which the sources are defined. The default is the following: defaults -> config files -> environment variables -> command line flags (each source is overwritten by the following source)
To define defaults for the config variables it can just be predefined in the struct that the configuration is supposed to be unmarshalled into. Properties that are not set in any of the configuration sources will keep the preset value.
Since environment variables and flags are purely text based it also supports types that implement the encoding.TextUnmarshaler interface like for example zapcore.Level and logrus.Level. On top of that custom implementations are already baked into the package to support duration strings using time.ParseDuration() and time using time.Parse() as well as string slices ([]string) in the format val1,val2,val3 and string maps (map[string]string) in the format key1=val1,key2=val2.
func New ¶ added in v0.1.2
func New(sources ...ConfigSource) *Collector
New returns a new Collector. It accepts multiple configuration sources that implement the ConfigSource interface. If no sources are present the resulting Collector won't have any configuration sources and return the input struct without any changes in the Collector.Get method.
func (*Collector) Get ¶
Get is the main package function and can be used by its wrapper Get or on a defined Collector struct. It expects a pointer to the config struct to write the config variables from the configured source to. If the input param is not a pointer to a struct, Get will return an error.
Get looks for config variables in all defined sources. Further usage details can be found in the examples or the Collector struct's documentation.
type ConfigSource ¶ added in v0.2.0
ConfigSource consists of one method that gets a certain field and should return its value. If this value is a string and should be parsed (for example env variables can only be retrieved as a string but could also resemble an int value or even a string slice), a []byte should be returned.
If anything else than a byte slice is returned the given value will be used as is and if there's a type mismatch an error will be reported.
type ConfigSourceInitializer ¶ added in v0.2.0
type ConfigSourceInitializer interface { // Init should be called right before Read to initialize stuff. // Some things shouldn't be initialized in the constructor since the environment or files (the config source) // could be altered in the time between constructing a config source and calling the Read method. Init(fields []Field) error }
ConfigSourceInitializer is an optional interface to implement and can be used to initialize the config source before reading the fields one by one with the Read method of ConfigSource.
type EnvOption ¶ added in v0.1.2
type EnvOption func(*EnvSource)
EnvOption takes an EnvSource as input and modifies it.
func WithEnvSeparator ¶ added in v0.1.2
WithEnvSeparator adds a custom separator to an EnvSource struct.
type EnvSource ¶ added in v0.2.0
type EnvSource struct {
// contains filtered or unexported fields
}
EnvSource is used to read the configuration from environment variables. prefix can be defined to look for environment variables with a certain prefix. separator is used for nested structs and also for the Prefix. As an example: If prefix is set to "example", the separator is set to "_" and the config struct's field is named Port, it will by default look for the environment variable "EXAMPLE_PORT".
func NewEnvSource ¶ added in v0.2.0
NewEnvSource returns a new EnvSource. prefix defines the prefix to be prepended to the automatically generated names when looking for the environment variables. prefix can be empty. It accepts an EnvOption to override the default env separator.
type Field ¶ added in v0.2.0
type Field struct {
// contains filtered or unexported fields
}
Field is a struct to hold all information for a struct's field that should be filled with configuration.
func (*Field) Description ¶ added in v0.2.0
type FilesSource ¶ added in v0.2.0
type FilesSource struct { ReadersSource // contains filtered or unexported fields }
FilesSource is a wrapper around ReadersSource to automatically find the needed readers in a filesystem. To use the local FS the NewFilesSource can be used to find the files. NewFSFilesSource can be used for other usecases where the FS is present on S3 or an embed.FS. This differentiation was implemented since the os.DirFS does not really support relative and absolute paths easily.
func NewFSFilesSource ¶ added in v0.3.0
func NewFSFilesSource(fsys fs.FS, globs ...string) *FilesSource
func NewFilesSource ¶ added in v0.2.0
func NewFilesSource(globs ...string) *FilesSource
func (*FilesSource) Init ¶ added in v0.2.0
func (s *FilesSource) Init(fields []Field) error
Init tries to find files on the filesystem matching the supplied globs and reads them. Afterwards the underlying ReadersSource is initialized.
type FlagOption ¶ added in v0.1.2
type FlagOption func(*FlagsSource)
FlagOption takes a FlagsSource as input and modifies it.
func WithFlagSeparator ¶ added in v0.1.2
func WithFlagSeparator(separator string) FlagOption
WithFlagSeparator adds a custom separator to a FlagsSource struct.
type FlagsSource ¶ added in v0.2.0
type FlagsSource struct {
// contains filtered or unexported fields
}
FlagsSource is used to read the configuration from command line flags. separator is used for nested structs to construct flag names from parent and child properties recursively.
func NewFlagsSource ¶ added in v0.2.0
func NewFlagsSource(opts ...FlagOption) *FlagsSource
NewFlagsSource returns a new FlagsSource. It accepts a FlagOption to override the default flag separator.
func (*FlagsSource) Init ¶ added in v0.2.0
func (s *FlagsSource) Init(fields []Field) error
Init initializes the fieldToFlagInfos property. It should be used right before calling the Read method to load the latest flags.
func (*FlagsSource) Read ¶ added in v0.2.0
func (s *FlagsSource) Read(field *Field) (interface{}, error)
Read reads the saved flagSet from the Init function and returns the set value for a certain field. If no value is set in the flags it returns nil.
type ReadersSource ¶ added in v0.3.0
type ReadersSource struct {
// contains filtered or unexported fields
}
ReadersSource is used to read configuration from any type that implements the io.Reader interface. The data in the readers should be in one of the supported file formats (currently yml and json). This enables a wide range of usages like for example reading the config from an http endpoint or a file.
The ReadersSource accepts io.Reader to support as many types as possible. To improve the experience with sources that need to be closed it will also check if the supplied type implements io.Closer and closes the reader if it does.
func NewReadersSource ¶ added in v0.3.0
func NewReadersSource(readers ...io.Reader) *ReadersSource
NewReadersSource returns a new ReadersSource that reads from one or more readers. If the input reader slice is empty this will be a noop reader.
func (*ReadersSource) Init ¶ added in v0.3.0
func (s *ReadersSource) Init(_ []Field) error
Init initializes the fileMaps property. It should be used right before calling the Read method to load the latest config files' states.
func (*ReadersSource) Read ¶ added in v0.3.0
func (s *ReadersSource) Read(field *Field) (interface{}, error)
Read reads the saved fileMaps from the Init function and returns the set value for a certain field. If not value is set in the flags it returns nil.