Skip to content

yandzee/config

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

36 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Config Go Reference

Extensible utility for initializing fields / configs with values, taken from StringSources, like environment variables. Allows to easily get []slog.LogRecord for logging purposes. Convenient, frequently used value checkers and value transformers included.

Basic usage

import "github.com/yandzee/config"

func main() {
	cfg := AppConfig{}

	// Return value form
	cfg.Port := config.Uint16().Env("PORT", 8080)
	// Note that optional second argument is used to specify the default value

	// Generic setter form
	config.Set(&cfg.Port).Env("PORT", 8080)
}

Value checks

Validators (checkers) can be engaged using appropriate methods on value getters:

import (
	"github.com/yandzee/config"
	"github.com/yandzee/config/checkers"
)

type Config struct {
	TLSCerts []string
}

func (c *Config) Init() {
	c.TLSCerts = config.
		Strings(",", ";"). // Multiple separators can be used for string split
		Checks(checkers.FilesExist).
		Env("TLS_CERTS", []string{}) // Default value as an optional second argument
}

Example: logic for connected values

Sometimes variables are connected logically and should be checked appropriately:

type Config struct {
	DatabaseURL    string
	IsInMemoryMode bool
}

func (c *Config) Init() {
	c.IsInMemoryMode = config.Bool().Env("IN_MEMORY_MODE", false)

	c.DatabaseURL = config.
		String().
		Check(func(r *result.Result[string]) (bool, string) {
			// Custom check function for wiring up logically connected values
			if c.IsInMemoryMode || !r.Flags.IsDefaulted() {
				return true, ""
			}

			return false, "Must be specified unless IN_MEMORY_MODE is on"
		}).
		Env("DATABASE_URL", "")
}

Value transformers

Some values may require additional preprocessing before being stored in config:

import (
	"github.com/yandzee/config"
	"github.com/yandzee/config/checkers"
)

type Config struct {
	AESKey []byte
}

func (c *Config) Init() {
	c.AESKey = config.Bytes().
        Pre(transformers.Unhex).  // <--- hex.DecodeString()
        Env("AES_KEY")
}

Example: custom transformers

Custom getters with custom transformers can be easily written by hand and reused as it was there built in.

import (
	"crypto/ecdsa"
	"crypto/x509"
	"encoding/pem"

	"github.com/yandzee/config"
	"github.com/yandzee/config/configurator"
	"github.com/yandzee/config/transform"
	"github.com/yandzee/config/transformers"
)

type Config struct {
	SignPrivateKey *ecdsa.PrivateKey
}

func (c *Config) Init() {
	c.SignPrivateKey = c.ECPrivateKey(). // <--- Custom getter
		Pre(transformers.Unbase64).
		Env("SIGNATURE_PRIVATE_KEY")
}

func (c *Config) ECPrivateKey() *configurator.Getter[*ecdsa.PrivateKey] {
	return config.
		Custom[*ecdsa.PrivateKey]().
		Post(
			transformers.ToBytes,
			transform.Map(func(v []byte) (*ecdsa.PrivateKey, error) {
				block, _ := pem.Decode([]byte(v))
				if block == nil {
					return nil, fmt.Errorf("PEM block is not found")
				}

				pk, err := x509.ParseECPrivateKey(block.Bytes)
				if err != nil {
					return nil, errors.Join(
						fmt.Errorf("Failed to x509.ParseECPrivateKey"),
						err,
					)
				}

				return pk, nil
			}),
		)
}

Logging

Library doesn't force the program to log anything. Instead, []slog.Record can be obtained for further handling:

func main() {
	cfg := Config{}
	cfg.Init()

	log := slog.Default()
	fctx := context.Background()

	// Logging is decoupled and managed by your code
	hasFatal := false
	for _, logRecord := range config.LogRecords() {
		_ = log.Handler().Handle(ctx, logRecord)

		hasFatal = hasFatal || logRecord.Level == slog.LevelError
	}

	if hasFatal {
		os.Exit(1)
	}
}

Note that by default, no value is included as log record attribute for security reasons. It can be changed by appropriate option for config.LogRecords() method.

Example output:

➜  config-usage-ex go run main.go
2025/02/27 18:55:48 WARN Value set name=PORT kind=env is-defaulted=true value=8080
2025/02/27 18:55:48 WARN Value set name=TLS_CERTS kind=env is-defaulted=true value=[]
2025/02/27 18:55:48 ERROR Not set name=SIGNATURE_KEY kind=env is-required=true value=<nil>
2025/02/27 18:55:48 WARN Value set name=IN_MEMORY_MODE kind=env is-defaulted=true value=false
2025/02/27 18:55:48 ERROR Database url must be specified unless in memory mode is on name=DATABASE_URL kind=env is-check-failed=true value=""
exit status 1

About

Set of utilities for convenient initialization from environment

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages