conf

package
v1.2.5 Latest Latest
Warning

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

Go to latest
Published: Oct 1, 2025 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package conf provides a configuration binding framework with hierarchical resolution, type-safe mapping, and validation capabilities.

Core Concepts:

The framework enables mapping configuration data from multiple sources into Go structures with:

- Hierarchical property resolution using ${key} syntax - Type-safe binding with automatic conversions - Expression-based validation - Extensible architecture via pluggable components

Tag Syntax:

Struct tags use the following format:

value:"${key:=default}>>splitter"

Key features: - Nested keys (e.g., service.endpoint) - Dynamic defaults (e.g., ${DB_HOST:=localhost:${DB_PORT:=3306}}) - Splitter chaining (e.g., >>json for JSON parsing)

Data Binding:

Supports binding to various types with automatic conversion:

1. Primitives: Uses strconv for basic type conversions 2. Complex Types:

  • Slices: From indexed properties or custom splitters
  • Maps: Via subkey expansion
  • Structs: Recursive binding of nested structures

3. Custom Types: Register converters using RegisterConverter

Validation System:

  1. Expression validation using expr tag: type Config struct { Port int `expr:"$ > 0 && $ < 65535"` }

  2. Custom validators: RegisterValidateFunc("futureDate", func(t time.Time) bool { return t.After(time.Now()) })

File Support:

Built-in readers handle: - JSON (.json) - Properties (.properties) - YAML (.yaml/.yml) - TOML (.toml/.tml)

Register custom readers with RegisterReader.

Property Resolution:

- Recursive ${} substitution - Type-aware defaults - Chained defaults (${A:=${B:=C}})

Extension Points:

1. RegisterSplitter: Add custom string splitters 2. RegisterConverter: Add type converters 3. RegisterReader: Support new file formats 4. RegisterValidateFunc: Add custom validators

Examples:

Basic binding:

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

Nested structure:

type AppConfig struct {
    DB      Database `value:"${db}"`
    Timeout string   `value:"${timeout:=5s}"`
}

Slice binding:

type Config struct {
    Endpoints []string `value:"${endpoints}"`       // Indexed properties
    Features  []string `value:"${features}>>split"` // Custom splitter
}

Validation:

type UserConfig struct {
    Age     int       `value:"${age}" expr:"$ >= 18"`
    Email   string    `value:"${email}" expr:"contains($, '@')"`
    Expires time.Time `value:"${expires}" expr:"futureDate($)"`
}

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNotExist      = util.FormatError(nil, "not exist")
	ErrInvalidSyntax = util.FormatError(nil, "invalid syntax")
)

Functions

func BindValue

func BindValue(p Properties, 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: - Returns ErrNotExist if the property is missing without a default. - Returns type conversion errors if parsing fails. - Returns wrapped errors with context (path, type).

func RegisterConverter

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

RegisterConverter registers a Converter for a non-primitive type such as time.Time, time.Duration, or other user-defined value types.

func RegisterReader

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

RegisterReader registers its Reader for some kind of file extension.

func RegisterSplitter

func RegisterSplitter(name string, fn Splitter)

RegisterSplitter registers a Splitter with a given name.

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. The function can then be used in validation expressions.

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: - ErrInvalidSyntax 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.

type MutableProperties added in v1.2.0

type MutableProperties struct {
	*barky.Storage
}

MutableProperties stores the data with map[string]string and the keys are case-sensitive, you can get one of them by its key, or bind some of them to a value.

There are too many formats of configuration files, and too many conflicts between them. Each format of configuration file provides its special characteristics, but usually they are not all necessary, and complementary. For example, `conf` disabled Java properties' expansion when reading file, but also provides similar function when getting or binding properties.

A good rule of thumb is that treating application configuration as a tree, but not all formats of configuration files designed as a tree or not ideal, for instance Java properties isn't strictly verified. Although configuration can store as a tree, but it costs more CPU time when getting properties because it reads property node by node. So `conf` uses a tree to strictly verify and a flat map to store.

func Load

func Load(file string) (*MutableProperties, error)

Load creates a MutableProperties instance from a configuration file. Returns an error if the file type is not supported or parsing fails.

func Map

func Map(data map[string]any) *MutableProperties

Map creates a MutableProperties instance directly from a map.

func New

func New() *MutableProperties

New creates a new empty MutableProperties instance.

func (*MutableProperties) Bind added in v1.2.0

func (p *MutableProperties) Bind(i any, tag ...string) error

Bind maps property values into the provided target object. Supported targets: primitive values, maps, slices, and structs. Struct binding uses the `value` tag in the form:

value:"${key:=default}>>splitter"

- key: property key - default: default value if key is missing - splitter: registered splitter name for splitting into []string

func (*MutableProperties) CopyTo added in v1.2.0

func (p *MutableProperties) CopyTo(out *MutableProperties) error

CopyTo copies all properties into another MutableProperties instance, overriding values if keys already exist.

func (*MutableProperties) Resolve added in v1.2.0

func (p *MutableProperties) Resolve(s string) (string, error)

Resolve resolves placeholders in a string, replacing references like ${key:=default} with their actual values from the properties.

type ParsedTag

type ParsedTag struct {
	Key      string // short property key
	Def      string // default value string
	HasDef   bool   // indicates whether a default value exists
	Splitter string // optional splitter function name for slice parsing
}

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}>>splitter

- "key": the property key used to look up a value. - "default": optional fallback value if the key does not exist. - "splitter": optional custom function name to split strings into slices.

Examples:

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

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

func ParseTag

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

ParseTag parses a tag string into a ParsedTag struct.

Supported syntax: `${key:=default}>>splitter`

- The `${...}` block is mandatory. - ":=" introduces an optional default value. - ">>splitter" is optional and specifies a custom splitter.

Example parses:

"${foo}"               -> Key="foo"
"${foo:=bar}"          -> Key="foo", HasDef=true, Def="bar"
"${foo:=bar}>>csv"     -> Key="foo", HasDef=true, Def="bar", Splitter="csv"
"${:=fallback}"        -> Key="", HasDef=true, Def="fallback"

Errors:

  • Returns ErrInvalidSyntax if the string does not follow the pattern.

func (ParsedTag) String added in v1.2.0

func (tag ParsedTag) String() string

type Properties

type Properties interface {
	// Data returns all key-value pairs as a flat map.
	Data() map[string]string
	// Keys returns all keys.
	Keys() []string
	// SubKeys returns the sorted sub-keys of a given key.
	SubKeys(key string) ([]string, error)
	// Has checks whether a key exists.
	Has(key string) bool
	// Get returns the value for a given key, with an optional default.
	Get(key string, def ...string) string
	// Resolve resolves placeholders inside a string (e.g. ${key:=default}).
	Resolve(s string) (string, error)
	// Bind binds property values into a target object (struct, map, slice, or primitive).
	Bind(i any, tag ...string) error
	// CopyTo copies properties into another instance, overriding existing values.
	CopyTo(out *MutableProperties) error
}

Properties defines the read-only interface for accessing configuration data.

type Reader

type Reader func(b []byte) (map[string]any, error)

Reader parses raw bytes into a nested map[string]any.

type Splitter

type Splitter func(string) ([]string, error)

Splitter splits a string into a slice of strings using custom logic.

type ValidateFunc added in v1.2.0

type ValidateFunc[T any] func(T) bool

ValidateFunc defines a type for validation functions, which accept a value of type T and return a boolean result.

Directories

Path Synopsis
reader

Jump to

Keyboard shortcuts

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