conf

package
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: May 4, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package conf provides a flexible configuration binding system for Go applications.

It enables declarative configuration binding from various file formats and providers into Go structs with automatic type conversion, placeholder resolution, and expression-based validation.

Core Concepts

conf works by:

  1. Loading configuration data from a source via providers
  2. Flattening nested data into a key-value store
  3. Binding properties to struct fields using tagged annotations
  4. Resolving variable placeholders recursively
  5. Validating values using expressions

Tag Syntax

Struct fields use the `value` tag to specify the configuration key:

value:"${key:=default}"

Where:

  • `${key}` - references the configuration key
  • `:=default` - optional default value if the key is not found

Features:

  • Nested keys: `${db.host}`, `${service.endpoint}`
  • Default values: `${DB_HOST:=localhost}`
  • Chained defaults: `${A:=${B:=default}}` - if A is missing, try B, then fall back to default
  • Nested references: `${prefix${suffix}}` - nested variable expansion
  • Root binding: `${ROOT}` - binds from the root level (used at the top-level struct)

Example:

type ServerConfig struct {
    Host string `value:"${host:=localhost}"`
    Port int    `value:"${port:=8080}"`
}

Supported Types

The following types are supported out of the box:

  1. **Primitives**: string, all integer/float types, bool (automatic string conversion)
  2. **Time types**: time.Time, time.Duration (built-in converters)
  3. **Structs**: recursive binding of nested struct fields
  4. **Slices**: two input formats: - Indexed properties: `endpoints[0]=a`, `endpoints[1]=b` - Comma-separated: `endpoints=a,b,c`
  5. **Maps**: key-value binding from dot-notation subkeys: `users.alice=Alice`, `users.bob=Bob`
  6. **Custom types**: register custom converters with RegisterConverter

Value Resolution

All property values undergo variable resolution before binding. Any `${key}` occurrences in the string are replaced with their resolved values from the configuration store. Resolution is recursive and handles nested placeholders.

Example:

host=localhost
port=8080
url=http://${host}:${port}/api

After resolution, `url` becomes `http://localhost:8080/api`

Validation

Add validation to any field using the `expr` tag. The expression has access to:

  • `$` - the current field's value
  • All registered custom validation functions

Examples:

type Config struct {
    Port int    `value:"${port}" expr:"$ > 0 && $ < 65536"`
    Email string `value:"${email}" expr:"contains($, '@')"`
    MinLen string `value:"${data}" expr:"len($) > 3"`
}

Register custom validation functions:

// Register a validator that checks a time is in the future
conf.RegisterValidateFunc("future", func(t time.Time) bool {
    return t.After(time.Now())
})

// Use it in validation:
type Event struct {
    StartTime time.Time `value:"${start-time}" expr:"future($)"`
}

For more expression syntax see: https://github.com/expr-lang/expr

Loading Configuration

conf.Load accepts a source URI in the format:

[optional:]<provider>:<location>

Examples:

// Load from YAML file (auto-detects format by extension)
props, err := conf.Load("config.yaml")

// Explicit file provider
props, err = conf.Load("file:config.yaml")

// Optional file - does not error if file not found
props, err = conf.Load("optional:file:config.yaml")

Register custom providers (e.g., etcd, Consul, environment variables) with RegisterProvider to support additional configuration sources.

Supported File Formats

Built-in readers are registered for these extensions:

  • JSON (.json)
  • Java Properties (.properties)
  • YAML (.yaml, .yml)
  • TOML (.toml, .tml)

Register custom readers with RegisterReader for additional formats.

Quick Start

package main

import (
    "fmt"
    "log"

    "github.com/go-spring/spring-core/conf"
)

type Config struct {
    Host string `value:"${host:=localhost}"`
    Port int    `value:"${port:=8080}"`
}

func main() {
    // Load configuration from file
    props, err := conf.Load("config.yaml")
    if err != nil {
        log.Fatal(err)
    }

    // Bind to struct (uses ${ROOT} by default - binds all keys from root)
    var cfg Config
    if err := conf.Bind(props, &cfg); err != nil {
        log.Fatal(err)
    }

    // Use cfg.Host and cfg.Port
    fmt.Printf("Server starting on %s:%d\n", cfg.Host, cfg.Port)
}

Bind can also bind only under a specific key prefix:

var cfg AppConfig
conf.Bind(props, &cfg, "${app}") // all fields look for keys under app.*

Extension Points

All extensions must be registered during init():

  • RegisterProvider - add new configuration source providers
  • RegisterReader - add support for additional file formats
  • RegisterConverter - add type conversion for custom types
  • RegisterValidateFunc - add custom validation functions

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Bind added in v1.3.0

func Bind(p flatten.Storage, i any, tag ...string) error

Bind maps property values from storage into the target object. The target must be a pointer to a struct or a reflect.Value. An optional tag specifies the root property key using ${key} syntax. Supports default values: ${key:=default}. If no tag is provided, uses ${ROOT} (binds from the root). Supports binding to structs, maps, slices, and primitive types.

func BindValue

func BindValue(p flatten.Storage, v reflect.Value, t reflect.Type, param BindParam, filter Filter) (RetErr error)

BindValue attempts to bind a property value from the property source `p` into the given reflect.Value `v`, based on metadata in `param`.

Supported binding targets:

  • Primitive types (string, int, float, bool, etc.).
  • Structs (recursively bound field by field).
  • Maps (bound by iterating subkeys).
  • Slices (bound by either indexed keys or split strings).

Errors:

  • Validation failure if "expr" tag evaluation fails.
  • Type mismatch (e.g., array instead of slice).
  • Property not exist without default value.
  • Type conversion errors during parsing.
  • Custom converter function errors.

func Load

func Load(source string) (*flatten.Properties, error)

Load creates a Properties instance from a configuration source. The source format is [optional:]<provider>:<path> or just <path>. Returns an error if the file type is not supported or parsing fails.

func RegisterConverter

func RegisterConverter[T any](fn Converter[T])

RegisterConverter registers a Converter for a type T, such as time.Time, time.Duration, or other user-defined types. Must be called in init functions only.

func RegisterProvider added in v1.3.0

func RegisterProvider(name string, p provider.Provider)

RegisterProvider registers a Provider for a specific configuration source. For example, a file provider reads local files, an etcd provider fetches remote config. Must be called in init functions only.

func RegisterReader

func RegisterReader(r reader.Reader, ext ...string)

RegisterReader registers its Reader for some kind of file extension. For example, a YAML reader parses .yaml files into nested maps. Must be called in init functions only.

func RegisterValidateFunc added in v1.2.0

func RegisterValidateFunc[T any](name string, fn ValidateFunc[T])

RegisterValidateFunc registers a validation function with a specific name. Must be called in init functions only.

func Resolve added in v1.3.0

func Resolve(p flatten.Storage, s string) (string, error)

Resolve expands property references of the form ${key} inside a string, recursively resolving nested expressions.

Supported features: - Nested references: e.g. "${outer${inner}}" - Default values: "${key:=fallback}" - Arbitrary string concatenation around references.

Example:

Storage:
  "host" = "localhost"
  "port" = "8080"
Input:
  "http://${host}:${port}"
Output:
  "http://localhost:8080"

Errors: - Returns invalid syntax if braces are unbalanced.

Types

type BindParam

type BindParam struct {
	Key      string            // full property key
	Path     string            // full property path
	Tag      ParsedTag         // parsed tag
	Validate reflect.StructTag // original struct field tag for validation
}

BindParam holds metadata needed to bind a single configuration value to a Go struct field, slice element, or map entry.

func (*BindParam) BindTag

func (param *BindParam) BindTag(tag string, validate reflect.StructTag) error

BindTag parses the tag string, stores the ParsedTag in BindParam, and resolves nested key expansion.

Special cases: - "${:=default}" -> Key is empty, only default is set. - "${ROOT}" -> explicitly resets Key to an empty string.

If a BindParam already has a Key, new keys are appended hierarchically, e.g. parent Key="db", tag="${host}" -> final Key="db.host".

Errors: - Returns invalid syntax if the tag string is malformed or empty without default.

type Converter

type Converter[T any] func(string) (T, error)

Converter converts a string to a target type T.

type Filter added in v1.1.2

type Filter interface {
	Do(i any, param BindParam) (bool, error)
}

Filter defines an interface for filtering configuration fields during binding. Dynamic configuration fields have the same syntax, they need to be filtered out.

type ParsedTag

type ParsedTag struct {
	Key    string // short property key
	Def    string // default value string
	HasDef bool   // indicates whether a default value exists
}

ParsedTag represents a parsed configuration tag that encodes metadata for binding configuration values from property sources.

A tag string generally follows the pattern:

${key:=default}

- "key": the property key used to look up a value. - "default": optional fallback value if the key does not exist.

Examples:

"${db.host:=localhost}"       -> key=db.host, default=localhost
"${ports:=8080,9090}"         -> key=ports, default=8080,9090
"${:=foo}"                    -> empty key, only default value "foo"

The parsing logic is strict; malformed tags will result in invalid syntax.

func ParseTag

func ParseTag(tag string) (ret ParsedTag, err error)

ParseTag parses a tag string into a ParsedTag struct.

func (ParsedTag) String added in v1.2.0

func (tag ParsedTag) String() string

type ValidateFunc added in v1.2.0

type ValidateFunc[T any] func(T) bool

ValidateFunc defines a type for validation functions.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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