v0.1.3 added unknown cli flag handling, dependency update
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
// FILE: lixenwraith/config/config.go
|
|
||||||
// Package config provides thread-safe configuration management for Go applications
|
// Package config provides thread-safe configuration management for Go applications
|
||||||
// with support for multiple sources: TOML files, environment variables, command-line
|
// with support for multiple sources: TOML files, environment variables, command-line
|
||||||
// arguments, and default values with configurable precedence.
|
// arguments, and default values with configurable precedence.
|
||||||
@@ -7,6 +6,7 @@ package config
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
@@ -50,6 +50,9 @@ type Config struct {
|
|||||||
version atomic.Int64
|
version atomic.Int64
|
||||||
structCache *structCache
|
structCache *structCache
|
||||||
|
|
||||||
|
// CLI paths that matched no registered path (reset on each CLI load)
|
||||||
|
unknownCLIKeys []string
|
||||||
|
|
||||||
// File watching support
|
// File watching support
|
||||||
watcher *watcher
|
watcher *watcher
|
||||||
configFilePath string // Track loaded file path
|
configFilePath string // Track loaded file path
|
||||||
@@ -249,6 +252,7 @@ func (c *Config) SetSource(source Source, path string, value any) error {
|
|||||||
c.envData[path] = value
|
c.envData[path] = value
|
||||||
case SourceCLI:
|
case SourceCLI:
|
||||||
c.cliData[path] = value
|
c.cliData[path] = value
|
||||||
|
c.unknownCLIKeys = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
c.invalidateCache() // Invalidate cache after changes
|
c.invalidateCache() // Invalidate cache after changes
|
||||||
@@ -337,6 +341,13 @@ func (c *Config) AsStruct() (any, error) {
|
|||||||
return c.structCache.target, nil
|
return c.structCache.target, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnknownCLIKeys returns CLI paths that matched no registered config path
|
||||||
|
func (c *Config) UnknownCLIKeys() []string {
|
||||||
|
c.mutex.RLock()
|
||||||
|
defer c.mutex.RUnlock()
|
||||||
|
return slices.Clone(c.unknownCLIKeys)
|
||||||
|
}
|
||||||
|
|
||||||
// computeValue determines the current value based on precedence
|
// computeValue determines the current value based on precedence
|
||||||
func (c *Config) computeValue(item configItem) any {
|
func (c *Config) computeValue(item configItem) any {
|
||||||
// Check sources in precedence order
|
// Check sources in precedence order
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
module github.com/lixenwraith/config
|
module github.com/lixenwraith/config
|
||||||
|
|
||||||
go 1.25.4
|
go 1.26.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v1.5.0
|
github.com/BurntSushi/toml v1.6.0
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0
|
github.com/go-viper/mapstructure/v2 v2.5.0
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
|
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
||||||
|
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
|||||||
@@ -435,7 +435,7 @@ func (c *Config) loadEnv(opts LoadOptions) error {
|
|||||||
transform = defaultEnvTransform(opts.EnvPrefix)
|
transform = defaultEnvTransform(opts.EnvPrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- 1. Prepare data (Read-Lock to get paths)
|
// 1. Prepare data (Read-Lock to get paths)
|
||||||
c.mutex.RLock()
|
c.mutex.RLock()
|
||||||
paths := make([]string, 0, len(c.items))
|
paths := make([]string, 0, len(c.items))
|
||||||
for p := range c.items {
|
for p := range c.items {
|
||||||
@@ -443,7 +443,7 @@ func (c *Config) loadEnv(opts LoadOptions) error {
|
|||||||
}
|
}
|
||||||
c.mutex.RUnlock()
|
c.mutex.RUnlock()
|
||||||
|
|
||||||
// -- 2. Process env vars (No Lock)
|
// 2. Process env vars (No Lock)
|
||||||
foundEnvVars := make(map[string]string)
|
foundEnvVars := make(map[string]string)
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
if opts.EnvWhitelist != nil && !opts.EnvWhitelist[path] {
|
if opts.EnvWhitelist != nil && !opts.EnvWhitelist[path] {
|
||||||
@@ -506,6 +506,9 @@ func (c *Config) loadCLI(args []string) error {
|
|||||||
|
|
||||||
c.cliData = flattenedCLI
|
c.cliData = flattenedCLI
|
||||||
|
|
||||||
|
// Reset per load; reloads must not accumulate duplicates
|
||||||
|
c.unknownCLIKeys = c.unknownCLIKeys[:0]
|
||||||
|
|
||||||
for path, value := range flattenedCLI {
|
for path, value := range flattenedCLI {
|
||||||
if item, exists := c.items[path]; exists {
|
if item, exists := c.items[path]; exists {
|
||||||
if item.values == nil {
|
if item.values == nil {
|
||||||
@@ -514,6 +517,9 @@ func (c *Config) loadCLI(args []string) error {
|
|||||||
item.values[SourceCLI] = value
|
item.values[SourceCLI] = value
|
||||||
item.currentValue = c.computeValue(item)
|
item.currentValue = c.computeValue(item)
|
||||||
c.items[path] = item
|
c.items[path] = item
|
||||||
|
} else {
|
||||||
|
// Record CLI keys that match no registered path
|
||||||
|
c.unknownCLIKeys = append(c.unknownCLIKeys, path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user