cfgenv

package module
v1.4.0 Latest Latest
Warning

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

Go to latest
Published: Jan 26, 2025 License: Apache-2.0 Imports: 14 Imported by: 0

README

CFGENV

GoDoc Latest Version Go Report Card

Cfgenv loads config structs from environment vars.

Struct field types supported:

  • native type - string, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, time.Duration
  • pointer native type - *string, *bool, *int, *int8, *int16, *int32, *int64, *uint, *uint8, *uint16, *uint32, *uint64, *float32, *float64, *time.Duration - environment var is optional and value is not set if the env var is missing
  • []V (slice) where V is native type or pointer native type
  • map[K]V where K is native type and V is native type or pointer native type
  • embedded structs & struct fields
  • other types can be handled by providing a cfgenv.CustomerSetterOption
  • load config from environment variables or from file (e.g. .env file) or any other io.Reader

Example:

package main

import (
    "fmt"
    "github.com/go-andiamo/cfgenv"
)

type DbConfig struct {
    Host     string `env:"optional,default=localhost"`
    Port     uint   `env:"optional,default=3601"`
    Username string
    Password string
}

type Config struct {
    ServiceName string
    Database    DbConfig `env:"prefix=DB"`
}

func main() {
    cfg := &Config{}
    err := cfgenv.Load(cfg)
    if err != nil {
        panic(err)
    } else {
        fmt.Printf("%+v\n", cfg)
    }
}

would effectively load from environment...

SERVICE_NAME=foo
DB_HOST=localhost
DB_PORT=33601
DB_USERNAME=root
DB_PASSWORD=root

Installation

To install cfgenv, use go get:

go get github.com/go-andiamo/cfgenv

To update cfgenv to the latest version, run:

go get -u github.com/go-andiamo/cfgenv

Tags

Fields in config structs can use the env tag to override cfgenv loading behaviour

Tag Purpose
env:"MY"
env:"name=MY
env:"name='MY'"
overrides the environment var name to read with MY
env:"optional" denotes the environment var is optional
env:"default=foo" denotes the default value if the environment var is missing
env:"prefix=SUB" (on a struct field) denotes all fields in the struct will load from env var names prefixed with SUB_
env:"prefix=SUB_" (on a map[string]string field) denotes the map will read all env vars whose name starts with SUB_
env:"match='\d{3}'" (on a map[string]string field) denotes the map will read all env vars whose name matches the regexp \d{3}
env:"delimiter=;"
env:"delim=;"
(on slice and map fields) denotes the character used to delimit items
(the default is ,)
env:"separator=:"
env:"sep=:"
(on map fields) denotes the character used to separate key and value
(the default is :)
env:"encodng=base64" denotes the environment var is encoded as base64 and will be decoded.
Built-in decoders are base64, base64url, rawBase64 (no padding) & rawBase64url (no padding)
Other decoders are supported by passing a Decoder interface as an option to Load()/LoadAs()
env:"expand" denotes the environment var is always expanded (even if no Expand() is passed to Load()/LoadAs())
env:"no-expand" denotes the environment var is never expanded (even if an Expand() is passed to Load()/LoadAs())

Options

When loading config from environment vars, several option interfaces can be passed to cfgenv.Load() function to alter the names of expected environment vars or provide support for extra field types.

cfgenv.PrefixOption
cfgenv.PrefixOption

Alters the prefix for all environment vars

(Implement interface or use cfgenv.NewPrefix(prefix string)

Example:

package main

import (
    "fmt"
    "github.com/go-andiamo/cfgenv"
)

type Config struct {
    ServiceName string
}

func main() {
    cfg := &Config{}
    err := cfgenv.Load(cfg, cfgenv.NewPrefix("MYAPP"))
    if err != nil {
        panic(err)
    } else {
        fmt.Printf("%+v\n", cfg)
    }
}

to load from environment variables...

MYAPP_SERVICE_NAME=foo

cfgenv.SeparatorOption
cfgenv.SeparatorOption

Alters the separators used between prefixes and field names for environment vars

(Implement interface or use cfgenv.NewSeparator(separator string)

Example:

package main

import (
    "fmt"
    "github.com/go-andiamo/cfgenv"
)

type DbConfig struct {
    Host     string `env:"optional,default=localhost"`
    Port     uint   `env:"optional,default=3601"`
    Username string
    Password string
}

type Config struct {
    ServiceName string
    Database    DbConfig `env:"prefix=DB"`
}

func main() {
    cfg := &Config{}
    err := cfgenv.Load(cfg, cfgenv.NewPrefix("MYAPP"), cfgenv.NewSeparator("."))
    if err != nil {
        panic(err)
    } else {
        fmt.Printf("%+v\n", cfg)
    }
}

to load from environment variables...

MYAPP.SERVICE_NAME=foo
MYAPP.DB.HOST=localhost
MYAPP.DB.PORT=33601
MYAPP.DB.USERNAME=root
MYAPP.DB.PASSWORD=root

cfgenv.NamingOption
cfgenv.NamingOption

Overrides how environment variable names are deduced from field names

Example:

package main

import (
    "fmt"
    "github.com/go-andiamo/cfgenv"
    "reflect"
    "strings"
)

type DbConfig struct {
    Host     string `env:"optional,default=localhost"`
    Port     uint   `env:"optional,default=3601"`
    Username string
    Password string
}

type Config struct {
    ServiceName string
    Database    DbConfig `env:"prefix=DB"`
}

func main() {
    cfg := &Config{}
    err := cfgenv.Load(cfg, &LowercaseFieldNames{}, cfgenv.NewSeparator("."))
    if err != nil {
        panic(err)
    } else {
        fmt.Printf("%+v\n", cfg)
    }
}

type LowercaseFieldNames struct{}

func (l *LowercaseFieldNames) BuildName(prefix string, separator string, fld reflect.StructField, overrideName string) string {
    name := overrideName
    if name == "" {
        name = strings.ToLower(fld.Name)
    }
    if prefix != "" {
        name = prefix + separator + name
    }
    return name
}

to load from environment variables...

servicename=foo
DB.host=localhost
DB.port=33601
DB.username=root
DB.password=root

cfgenv.ExpandOption
cfgenv.ExpandOption

Providing an cfgenv.ExpandOption to the cfgenv.Load() function allows support for resolving substitute environment variables - e.g. EXAMPLE=${FOO}-{$BAR}

Use the Expand() function - or implement your own ExpandOption

Example - see expand_option


cfgenv.CustomSetterOption
cfgenv.CustomSetterOption

Provides support for custom struct field types

Example - see custom_setter_option


cfgenv.EnvReader
cfgenv.EnvReader

Reads environment vars from specified reader (e.g. cfgenv.NewEnvFileReader())

Example:

package main

import (
    "fmt"
    "github.com/go-andiamo/cfgenv"
    "os"
)

type Config struct {
    ServiceName string
}

func main() {
    cfg := &Config{}
    f, err := os.Open("local.env")
    if err != nil {
        panic(err)
    }
    defer f.Close()
    err = cfgenv.Load(cfg, cfgenv.NewEnvFileReader(f, nil))
    if err != nil {
        panic(err)
    } else {
        fmt.Printf("%+v\n", cfg)
    }
}

where file local.env looks like...

# this is the service name...
SERVICE_NAME=foo

Write Example

Cfgenv can also write examples and current config using the cfgenv.Example() or cfgenv.Write() functions.

Example - see write_example

Documentation

Overview

Package cfgenv - Go package for loading config structs from environment vars

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Example

func Example(w io.Writer, cfg any, options ...any) error

Example writes an example of the config

the supplied cfg arg must be a pointer to a struct

Use any options (such as PrefixOption, SeparatorOption, NamingOption or multiple CustomSetterOption) to alter loading behaviour

func ExampleOf

func ExampleOf[T any](w io.Writer, options ...any) error

ExampleOf writes an example of the specified T config

the type of T must be a struct

Use any options (such as PrefixOption, SeparatorOption, NamingOption or multiple CustomSetterOption) to alter loading behaviour

func Load

func Load(cfg any, options ...any) error

Load loads a config struct from environment vars

the supplied cfg arg must be a pointer to a struct

Use any options (such as PrefixOption, SeparatorOption, NamingOption, EnvReader, Decoder or multiple CustomSetterOption) to alter loading behaviour

func LoadAs

func LoadAs[T any](options ...any) (*T, error)

LoadAs loads the specified T config struct type from environment vars

the type of T must be a struct

Use any options (such as PrefixOption, SeparatorOption, NamingOption, EnvReader, Decoder or multiple CustomSetterOption) to alter loading behaviour

func Write

func Write(w io.Writer, cfg any, options ...any) error

Write writes the current config

the type of T must be a struct

Use any options (such as PrefixOption, SeparatorOption, NamingOption or multiple CustomSetterOption) to alter loading behaviour

Types

type CustomSetterOption

type CustomSetterOption interface {
	// IsApplicable should return true if the fld type is supported by this custom setter
	IsApplicable(fld reflect.StructField) bool
	// Set sets the field value `v` using the environment var `raw` value
	Set(fld reflect.StructField, v reflect.Value, raw string, present bool) error
}

CustomSetterOption is an option that can be passed to Load or LoadAs and provides support for reading additional struct field types

func NewDatetimeSetter

func NewDatetimeSetter(format string) CustomSetterOption

NewDatetimeSetter creates a CustomSetterOption that can be passed to Load or LoadAs and provides support for reading time.Time fields

func NewDurationSetter added in v1.3.0

func NewDurationSetter() CustomSetterOption

NewDurationSetter creates a CustomSetterOption that can be passed to Load or LoadAs and provides support for reading time.Duration fields

type Decoder added in v1.3.0

type Decoder interface {
	// Encoding returns the encoding (e.g. "base64") that this Decoder supports
	Encoding() string
	// Decode returns the decoded value
	Decode(value string) (string, error)
}

Decoder is an option that can be passed to Load or LoadAs and provides support for decoding values

Values that are encoded can be denoted with field tags, e.g.

type MyConfig struct {
  Key string `env:"encoding=base64"`
}

func NewBase64Decoder added in v1.3.0

func NewBase64Decoder() Decoder

NewBase64Decoder returns a new Decoder for decoding base64

func NewBase64UrlDecoder added in v1.3.0

func NewBase64UrlDecoder() Decoder

NewBase64UrlDecoder returns a new Decoder for decoding base64url

func NewRawBase64Decoder added in v1.3.0

func NewRawBase64Decoder() Decoder

NewRawBase64Decoder returns a new Decoder for decoding raw base64 (no padding)

func NewRawBase64UrlDecoder added in v1.3.0

func NewRawBase64UrlDecoder() Decoder

NewRawBase64UrlDecoder returns a new Decoder for decoding raw base64url (no padding)

type EnvReader added in v1.2.0

type EnvReader interface {
	// LookupEnv see os.LookupEnv
	LookupEnv(key string) (string, bool)
	// Environ see os.Environ
	Environ() []string
}

EnvReader is an option that can be passed to Load or LoadAs an abstraction around reading environment variables fom various sources

func NewEnvFileReader added in v1.2.0

func NewEnvFileReader(f io.Reader, errHandler func(err error)) EnvReader

NewEnvFileReader creates a new EnvReader that reads from a file (or any other io.Reader)

func NewEnvReader added in v1.4.0

func NewEnvReader() EnvReader

NewEnvReader creates a new EnvReader that reads from environment vars using os.LookupEnv and os.Environ

func NewFlagReader added in v1.4.0

func NewFlagReader(nameConverter FlagNameConverter, useDefaults bool) EnvReader

NewFlagReader creates a new EnvReader that reads from flags (i.e. flag.CommandLine)

If flag names differ from env var names pass a nameConverter arg or nil if no name conversion is needed

The useDefaults args determines whether flag defaults are used

func NewFlagSetReader added in v1.4.0

func NewFlagSetReader(fs *flag.FlagSet, nameConverter FlagNameConverter, useDefaults bool) EnvReader

NewFlagSetReader creates a new EnvReader that reads from flags from the provided *flag.FlagSet

If flag names differ from env var names pass a nameConverter arg or nil if no name conversion is needed

The useDefaults args determines whether flag defaults are used

func NewMultiEnvReader added in v1.4.0

func NewMultiEnvReader(readers ...EnvReader) EnvReader

NewMultiEnvReader creates an EnvReader that reads environment vars from multiple EnvReader's e.g. MapEnvReader, NewEnvReader, NewEnvFileReader, NewFlagReader

when calling LookupEnv, it successively tries all the provided readers - returning the first found

When call Environ, it merges all environs into one

type ExpandOption added in v1.1.0

type ExpandOption interface {
	// Expand expands the env var value s
	Expand(s string, er EnvReader) string
}

ExpandOption is an option that can be passed to Load or LoadAs and provides support for expanding environment var values like...

FOO=${BAR}

func Expand added in v1.1.0

func Expand(lookups ...map[string]string) ExpandOption

Expand creates a default ExpandOption (for use in Load / LoadAs)

Any supplied lookup maps are checked first - if a given env var name, e.g. "${FOO}", is not found in lookups then the value is taken from env var

type FlagNameConverter added in v1.4.0

type FlagNameConverter interface {
	ToFlagName(envKey string) string
	ToEnvName(flagName string) string
}

FlagNameConverter is an interface that can be used with NewFlagReader and converts env var names to flag names or vice versa

type MapEnvReader added in v1.3.0

type MapEnvReader map[string]string

MapEnvReader is a map[string]string that implements the EnvReader interface

func (MapEnvReader) Environ added in v1.3.0

func (m MapEnvReader) Environ() []string

func (MapEnvReader) LookupEnv added in v1.3.0

func (m MapEnvReader) LookupEnv(key string) (string, bool)

type NamingOption

type NamingOption interface {
	BuildName(prefix string, separator string, fld reflect.StructField, overrideName string) string
}

NamingOption is an option that can be passed to Load or LoadAs and provides a means of overriding how env var names are deduced

type PrefixOption

type PrefixOption interface {
	// GetPrefix returns the prefix for all env var names
	GetPrefix() string
}

PrefixOption is an option that can be passed to Load or LoadAs and provides a prefix for all env var names

func NewPrefix

func NewPrefix(prefix string) PrefixOption

NewPrefix creates a new PrefixOption with the specified prefix

type SeparatorOption

type SeparatorOption interface {
	// GetSeparator returns the separator to be used between prefixes used in env var names
	GetSeparator() string
}

SeparatorOption is an option that can be passed to Load or LoadAs and provides the separator to be used between prefixes used in env var names

func NewSeparator

func NewSeparator(separator string) SeparatorOption

NewSeparator creates a new NewSeparator with the specified separator

Directories

Path Synopsis
_examples
basic command
expand_option command
flags_example command
naming_option command
prefix_option command
readme command
write_example command

Jump to

Keyboard shortcuts

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