Documentation
¶
Index ¶
- Constants
- func Flatten(m map[string]any) map[string]string
- func JoinPath(path []Path) string
- type ConfigSource
- type LayeredStorage
- func (s *LayeredStorage) AddStorage(index int, source *PropertiesStorage, name string)
- func (s *LayeredStorage) Exists(key string) bool
- func (s *LayeredStorage) MapKeys(key string, result map[string]struct{}) bool
- func (s *LayeredStorage) SliceEntries(key string, result map[string]string) bool
- func (s *LayeredStorage) Value(key string) (string, bool)
- type Path
- type PathType
- type PrefixedStorage
- type Properties
- type PropertiesStorage
- type Storage
Constants ¶
const ( // StorageCommandLine represents configuration provided via command line. // This usually has the highest priority. StorageCommandLine = iota // StorageEnvironment represents configuration from environment variables. StorageEnvironment // StorageProfileFile represents configuration loaded from profile-specific files. // Example: application-dev.properties. StorageProfileFile // StorageAppFile represents configuration from the main application file. // Example: application.properties or application.yml. StorageAppFile // StorageDefault represents built-in default configuration values. // This layer typically has the lowest priority. StorageDefault // StorageMax is the number of supported layers. StorageMax )
Variables ¶
This section is empty.
Functions ¶
func Flatten ¶
Flatten flattens a nested map[string]any into a map[string]string.
This function is intended for data produced by encoding/json.Unmarshal, where values are limited to the following kinds:
- map[string]any
- []any
- primitive JSON types (bool, number, string, nil)
Structs, custom types, and non-string map keys are explicitly out of scope.
Flattening rules:
Nested maps are expanded using dot notation: {"a": {"b": 1}} -> "a.b" = "1"
Slices (and arrays, although arrays do not originate from json.Unmarshal) are expanded using index notation: {"a": [1, 2]} -> "a[0]" = "1", "a[1]" = "2"
Nil values (both untyped nil and typed nil) are represented as "<nil>".
Empty (zero-length but non-nil) maps are represented as "{}".
Empty (zero-length but non-nil) slices are represented as "[]".
Primitive values are converted to strings using deterministic, Go-native formatting (strconv).
The resulting map is intended for display-oriented use cases such as logging, diffing, diagnostics, or inspection. The output is not reversible and must not be treated as a lossless serialization format.
Types ¶
type ConfigSource ¶ added in v0.0.10
type ConfigSource struct {
*PropertiesStorage
Name string
}
type LayeredStorage ¶ added in v0.0.10
type LayeredStorage struct {
// contains filtered or unexported fields
}
LayeredStorage aggregates multiple configuration sources with deterministic precedence rules.
The design follows the layered configuration model used by Spring-style environments: configuration values may come from multiple sources (command line, environment variables, files, etc.), and a predictable priority order determines which value wins.
Precedence rules:
- Layers with a lower index have higher priority.
- Within the same layer, sources added later override earlier ones.
For example:
CommandLine Environment ProfileFile AppFile Default
Lookup always scans layers from highest priority to lowest.
Different data structures behave differently across layers:
Leaf values follow **override semantics**. The first value found wins.
Map properties follow **merge semantics**. Keys from all layers are combined.
Slice properties follow **override semantics**. The first layer defining the slice replaces all lower layers.
func (*LayeredStorage) AddStorage ¶ added in v0.0.10
func (s *LayeredStorage) AddStorage(index int, source *PropertiesStorage, name string)
AddStorage registers a configuration source into the specified layer.
Sources within the same layer follow an override rule: the most recently added source has higher priority.
This is implemented by inserting the new source at the beginning of the slice so that iteration always sees newer sources first.
func (*LayeredStorage) Exists ¶ added in v0.0.10
func (s *LayeredStorage) Exists(key string) bool
Exists reports whether the given key exists in any layer.
Lookup follows layer priority: once a higher-priority source reports the key exists, lower layers are not checked.
Exists considers both leaf nodes and intermediate prefixes, depending on the underlying Storage implementation.
func (*LayeredStorage) MapKeys ¶ added in v0.0.10
func (s *LayeredStorage) MapKeys(key string, result map[string]struct{}) bool
MapKeys collects the child keys of a map node across all layers.
Unlike leaf values, map structures are merged across sources. This means keys defined in different layers are combined into a single logical map.
Example:
source1:
server.port=8080
source2:
server.host=localhost
Final map:
server.port server.host
If the same key appears in multiple layers, the actual value resolution still follows the normal override rule in Value().
func (*LayeredStorage) SliceEntries ¶ added in v0.0.10
func (s *LayeredStorage) SliceEntries(key string, result map[string]string) bool
SliceEntries collects slice entries for the specified key.
Lists follow an override rule across configuration layers. Once a higher-priority source defines a slice, lower layers are ignored entirely.
Example:
source1:
my.list[0]=a
my.list[1]=b
source2:
my.list[0]=c
Result:
[c]
Not:
[c,b]
Therefore the search stops as soon as a source containing slice entries is found.
type Path ¶
type Path struct {
// Whether the element is a key or an index.
Type PathType
// Actual key or index value as a string.
// For PathTypeKey, it's the key string;
// for PathTypeIndex, it's the index number as a string.
Elem string
}
Path represents a single segment in a parsed key path. A path is composed of multiple Path elements that can be joined or split. For example, "foo.bar[0]" parses into:
[{Type: PathTypeKey, Elem: "foo"},
{Type: PathTypeKey, Elem: "bar"},
{Type: PathTypeIndex, Elem: "0"}].
func SplitPath ¶
SplitPath parses a hierarchical key string into a slice of Path objects. It supports dot-notation for maps and bracket-notation for arrays. Examples:
"foo.bar[0]" -> [{Key:"foo"}, {Key:"bar"}, {Index:"0"}]
"a[1][2]" -> [{Key:"a"}, {Index:"1"}, {Index:"2"}]
Rules:
- Keys must be non-empty strings without spaces.
- Indices must be unsigned integers (no sign, no decimal).
- Empty maps/slices are not special-cased here.
- Returns an error if the key is malformed (e.g. unbalanced brackets, unexpected characters, or empty keys if disallowed).
type PathType ¶
type PathType int8
PathType represents the type of a path element in a hierarchical key. A path element can either be a key (map field) or an index (array/slice element).
type PrefixedStorage ¶ added in v0.0.10
PrefixedStorage wraps another Storage and automatically prepends a fixed prefix to all keys.
func NewPrefixedStorage ¶ added in v0.0.10
func NewPrefixedStorage(s Storage, prefix string) *PrefixedStorage
NewPrefixedStorage creates a new PrefixedStorage instance.
func (*PrefixedStorage) Exists ¶ added in v0.0.10
func (s *PrefixedStorage) Exists(key string) bool
Exists checks existence with the configured prefix.
func (*PrefixedStorage) MapKeys ¶ added in v0.0.10
func (s *PrefixedStorage) MapKeys(key string, result map[string]struct{}) bool
MapKeys retrieves map keys with the configured prefix.
func (*PrefixedStorage) SliceEntries ¶ added in v0.0.10
func (s *PrefixedStorage) SliceEntries(key string, result map[string]string) bool
SliceEntries retrieves slice entries with the configured prefix.
type Properties ¶ added in v0.0.10
type Properties struct {
// contains filtered or unexported fields
}
Properties represents a flattened key-value storage.
func MapProperties ¶ added in v0.0.10
func MapProperties(data map[string]any) *Properties
MapProperties creates a new Properties instance from a hierarchical map by flattening it into key-value pairs.
func NewProperties ¶ added in v0.0.10
func NewProperties(data map[string]string) *Properties
NewProperties creates a new Properties instance.
func (*Properties) Data ¶ added in v0.0.10
func (s *Properties) Data() map[string]string
Data returns the underlying flattened data.
func (*Properties) Get ¶ added in v0.0.10
func (s *Properties) Get(key string) (string, bool)
Get retrieves the value of a leaf node.
func (*Properties) Set ¶ added in v0.0.10
func (s *Properties) Set(key, val string)
Set sets the value of a leaf node.
type PropertiesStorage ¶ added in v0.0.10
type PropertiesStorage struct {
*Properties
}
PropertiesStorage adapts Properties to the Storage interface.
func NewPropertiesStorage ¶ added in v0.0.10
func NewPropertiesStorage(s *Properties) *PropertiesStorage
NewPropertiesStorage creates a new PropertiesStorage instance.
func (*PropertiesStorage) Exists ¶ added in v0.0.10
func (s *PropertiesStorage) Exists(key string) bool
Exists reports whether the key exists.
A key is considered existing if:
- it exists as an exact leaf key
- it is a prefix of other keys (intermediate node)
func (*PropertiesStorage) MapKeys ¶ added in v0.0.10
func (s *PropertiesStorage) MapKeys(key string, result map[string]struct{}) bool
MapKeys collects child keys of a map node.
func (*PropertiesStorage) SliceEntries ¶ added in v0.0.10
func (s *PropertiesStorage) SliceEntries(key string, result map[string]string) bool
SliceEntries collects all entries belonging to a slice node.
The implementation only checks for the presence of key[index]. It does not enforce index continuity.
type Storage ¶
type Storage interface {
// Exists reports whether a key exists.
//
// A key is considered existing if:
// - it exists as an exact leaf key
// - it is a prefix of other keys
//
// Example:
//
// server.port=8080
//
// Exists("server") -> true
// Exists("server.port") -> true
// Exists("server.host") -> false
//
// This method is typically used by property condition logic.
Exists(key string) bool
// Value returns the value of a leaf node.
//
// Only exact key matches are returned.
Value(key string) (string, bool)
// MapKeys collects the direct child keys of a map node.
//
// Example:
//
// key = "server"
// data:
// server.host
// server.port
//
// result:
//
// {"host", "port"}
//
// Only the first map level is returned.
MapKeys(key string, result map[string]struct{}) bool
// SliceEntries collects all flattened entries belonging to a slice node.
//
// Example:
//
// key = "users"
// data:
// users[0].name
// users[1].name
//
// result will contain all matching entries.
SliceEntries(key string, result map[string]string) bool
}
Storage defines the minimal abstraction required by the bind system.
Data is stored in flattened form, for example:
server.port=8080 server.host=localhost users[0].name=tom
The implementation assumes the input data is already valid. Storage itself does not validate structural correctness.
The interface provides three capabilities used during binding:
- leaf value lookup
- map key discovery
- slice entry discovery
Exists is mainly intended for property condition checks rather than binding.