mirror of
https://github.com/taigrr/jety.git
synced 2026-04-02 03:19:03 -07:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7335ecd39c | |||
| 4c8d8960be | |||
| 6852ffbebb | |||
| 9ff1fdc5ee | |||
| 7f2320f204 | |||
| 94b97a5825 | |||
| 21ea264b79 | |||
| c4c05732f5 | |||
|
59b8a9078f
|
|||
|
550537be3b
|
|||
|
95bdc4d109
|
|||
|
64d37d936f
|
|||
|
62dd32f61b
|
|||
|
9c5923bd4e
|
|||
|
5643d4d262
|
|||
|
ee74c94359
|
|||
|
e84645ccfa
|
171
README.md
171
README.md
@@ -1,9 +1,172 @@
|
||||
# JETY
|
||||
|
||||
JSON, ENV, YAML, TOML
|
||||
JSON, ENV, TOML, YAML
|
||||
|
||||
This is a package for collapsing multiple configuration stores (env+json, env+yaml, env+toml) and writing them back to a centralized config.
|
||||
A lightweight Go configuration management library supporting JSON, ENV, TOML, and YAML formats.
|
||||
It provides viper-like `AutomaticEnv` functionality with fewer dependencies.
|
||||
Originally built to support [grlx](http://github.com/gogrlx/grlx).
|
||||
|
||||
It should behave similarly to the AutomaticEnv functionality of viper, but without some of the extra heft of the depedendencies it carries.
|
||||
## Installation
|
||||
|
||||
The inital purpose of this repo is to support the configuration requirements of [grlx](http://github.com/gogrlx/grlx), but development may continue to expand until more viper use cases and functionality are covered.
|
||||
```bash
|
||||
go get github.com/taigrr/jety
|
||||
```
|
||||
|
||||
Requires Go 1.25.5 or later.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "github.com/taigrr/jety"
|
||||
|
||||
func main() {
|
||||
// Set defaults
|
||||
jety.SetDefault("port", 8080)
|
||||
jety.SetDefault("host", "localhost")
|
||||
|
||||
// Environment variables are loaded automatically
|
||||
// e.g., PORT=9000 overrides the default
|
||||
|
||||
// Read from config file
|
||||
jety.SetConfigFile("config.toml")
|
||||
jety.SetConfigType("toml")
|
||||
if err := jety.ReadInConfig(); err != nil {
|
||||
// handle error
|
||||
}
|
||||
|
||||
// Get values (config file > env > default)
|
||||
port := jety.GetInt("port")
|
||||
host := jety.GetString("host")
|
||||
}
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- **Multiple formats**: JSON, TOML, YAML
|
||||
- **Automatic env loading**: Environment variables loaded on init
|
||||
- **Prefix filtering**: Filter env vars by prefix (e.g., `MYAPP_`)
|
||||
- **Case-insensitive keys**: Keys normalized to lowercase
|
||||
- **Type coercion**: Getters handle type conversion gracefully
|
||||
- **Thread-safe**: Safe for concurrent access
|
||||
- **Config precedence**: config file > environment > defaults
|
||||
|
||||
## Nested Configuration
|
||||
|
||||
For nested config structures like:
|
||||
|
||||
```toml
|
||||
[services.cloud]
|
||||
var = "xyz"
|
||||
timeout = "30s"
|
||||
|
||||
[services.cloud.auth]
|
||||
client_id = "abc123"
|
||||
```
|
||||
|
||||
Access nested values using `GetStringMap` and type assertions:
|
||||
|
||||
```go
|
||||
services := jety.GetStringMap("services")
|
||||
cloud := services["cloud"].(map[string]any)
|
||||
varValue := cloud["var"].(string) // "xyz"
|
||||
|
||||
// For deeper nesting
|
||||
auth := cloud["auth"].(map[string]any)
|
||||
clientID := auth["client_id"].(string) // "abc123"
|
||||
```
|
||||
|
||||
### Environment Variable Overrides
|
||||
|
||||
Environment variables use uppercase keys. For nested config, the env var name is the key in uppercase:
|
||||
|
||||
```bash
|
||||
# Override top-level key
|
||||
export PORT=9000
|
||||
|
||||
# For nested keys, use the full key name in uppercase
|
||||
export SERVICES_CLOUD_VAR=override_value
|
||||
```
|
||||
|
||||
With a prefix:
|
||||
|
||||
```go
|
||||
cm := jety.NewConfigManager().WithEnvPrefix("MYAPP_")
|
||||
```
|
||||
|
||||
```bash
|
||||
export MYAPP_PORT=9000
|
||||
export MYAPP_SERVICES_CLOUD_VAR=override_value
|
||||
```
|
||||
|
||||
**Note**: Environment variables override defaults but config files take highest precedence.
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### From v0.x to v1.x
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
1. **`WriteConfig()` now returns `error`**
|
||||
|
||||
```go
|
||||
// Before
|
||||
jety.WriteConfig()
|
||||
|
||||
// After
|
||||
if err := jety.WriteConfig(); err != nil {
|
||||
// handle error
|
||||
}
|
||||
// Or if you want to ignore the error:
|
||||
_ = jety.WriteConfig()
|
||||
```
|
||||
|
||||
2. **Go 1.25.5 minimum required**
|
||||
|
||||
Update your Go version or pin to an older jety release.
|
||||
|
||||
#### Non-Breaking Improvements
|
||||
|
||||
- Getters (`GetBool`, `GetInt`, `GetDuration`) now return zero values instead of panicking on unknown types
|
||||
- Added `int64` support in `GetInt`, `GetIntSlice`, and `GetDuration`
|
||||
- Improved env var parsing (handles values containing `=`)
|
||||
|
||||
## API
|
||||
|
||||
### Configuration
|
||||
|
||||
| Function | Description |
|
||||
| --------------------- | --------------------------------------------- |
|
||||
| `SetConfigFile(path)` | Set config file path |
|
||||
| `SetConfigDir(dir)` | Set config directory |
|
||||
| `SetConfigName(name)` | Set config file name (without extension) |
|
||||
| `SetConfigType(type)` | Set config type: `"toml"`, `"yaml"`, `"json"` |
|
||||
| `ReadInConfig()` | Read config file |
|
||||
| `WriteConfig()` | Write config to file |
|
||||
|
||||
### Values
|
||||
|
||||
| Function | Description |
|
||||
| ------------------------ | ------------------------ |
|
||||
| `Set(key, value)` | Set a value |
|
||||
| `SetDefault(key, value)` | Set a default value |
|
||||
| `Get(key)` | Get raw value |
|
||||
| `GetString(key)` | Get as string |
|
||||
| `GetInt(key)` | Get as int |
|
||||
| `GetBool(key)` | Get as bool |
|
||||
| `GetDuration(key)` | Get as time.Duration |
|
||||
| `GetStringSlice(key)` | Get as []string |
|
||||
| `GetIntSlice(key)` | Get as []int |
|
||||
| `GetStringMap(key)` | Get as map[string]string |
|
||||
|
||||
### Environment
|
||||
|
||||
| Function | Description |
|
||||
| ----------------------- | --------------------------------------------------- |
|
||||
| `WithEnvPrefix(prefix)` | Filter env vars by prefix (strips prefix from keys) |
|
||||
| `SetEnvPrefix(prefix)` | Set prefix for env var lookups |
|
||||
|
||||
## License
|
||||
|
||||
See [LICENSE](LICENSE) file.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package config
|
||||
package jety
|
||||
|
||||
import "time"
|
||||
|
||||
@@ -40,8 +40,8 @@ func Set(key string, value any) {
|
||||
defaultConfigManager.Set(key, value)
|
||||
}
|
||||
|
||||
func WriteConfig() {
|
||||
defaultConfigManager.WriteConfig()
|
||||
func WriteConfig() error {
|
||||
return defaultConfigManager.WriteConfig()
|
||||
}
|
||||
|
||||
func ConfigFileUsed() string {
|
||||
|
||||
31
doc.go
Normal file
31
doc.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Package jety provides configuration management supporting JSON, ENV, TOML, and YAML formats.
|
||||
//
|
||||
// It offers viper-like AutomaticEnv functionality with minimal dependencies, allowing
|
||||
// configuration to be loaded from files and environment variables with automatic merging.
|
||||
//
|
||||
// Configuration sources are layered with the following precedence (highest to lowest):
|
||||
// - Values set via Set() or SetString()/SetBool()
|
||||
// - Environment variables (optionally filtered by prefix)
|
||||
// - Values from config file via ReadInConfig()
|
||||
// - Default values set via SetDefault()
|
||||
//
|
||||
// Basic usage:
|
||||
//
|
||||
// jety.SetConfigFile("/etc/myapp/config.yaml")
|
||||
// jety.SetConfigType("yaml")
|
||||
// jety.SetEnvPrefix("MYAPP_")
|
||||
// jety.SetDefault("port", 8080)
|
||||
//
|
||||
// if err := jety.ReadInConfig(); err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// port := jety.GetInt("port")
|
||||
//
|
||||
// For multiple independent configurations, create separate ConfigManager instances:
|
||||
//
|
||||
// cm := jety.NewConfigManager()
|
||||
// cm.SetConfigFile("/etc/myapp/config.toml")
|
||||
// cm.SetConfigType("toml")
|
||||
// cm.ReadInConfig()
|
||||
package jety
|
||||
89
getters.go
89
getters.go
@@ -1,4 +1,4 @@
|
||||
package config
|
||||
package jety
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -12,7 +12,7 @@ func (c *ConfigManager) Get(key string) any {
|
||||
defer c.mutex.RUnlock()
|
||||
v, ok := c.combinedConfig[strings.ToLower(key)]
|
||||
if !ok {
|
||||
v, ok = c.envConfig[strings.ToLower(c.envPrefix+key)]
|
||||
v, ok = c.envConfig[strings.ToLower(key)]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
@@ -25,7 +25,7 @@ func (c *ConfigManager) GetBool(key string) bool {
|
||||
defer c.mutex.RUnlock()
|
||||
v, ok := c.combinedConfig[strings.ToLower(key)]
|
||||
if !ok {
|
||||
v, ok = c.envConfig[strings.ToLower(c.envPrefix+key)]
|
||||
v, ok = c.envConfig[strings.ToLower(key)]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
@@ -35,29 +35,19 @@ func (c *ConfigManager) GetBool(key string) bool {
|
||||
case bool:
|
||||
return val
|
||||
case string:
|
||||
if strings.ToLower(val) == "true" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return strings.EqualFold(val, "true")
|
||||
case int:
|
||||
if val == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
case float32, float64:
|
||||
if val == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return val != 0
|
||||
case float32:
|
||||
return val != 0
|
||||
case float64:
|
||||
return val != 0
|
||||
case time.Duration:
|
||||
return val > 0
|
||||
case nil:
|
||||
return false
|
||||
case time.Duration:
|
||||
if val == 0 || val < 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
default:
|
||||
return val.(bool)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +56,7 @@ func (c *ConfigManager) GetDuration(key string) time.Duration {
|
||||
defer c.mutex.RUnlock()
|
||||
v, ok := c.combinedConfig[strings.ToLower(key)]
|
||||
if !ok {
|
||||
v, ok = c.envConfig[strings.ToLower(c.envPrefix+key)]
|
||||
v, ok = c.envConfig[strings.ToLower(key)]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
@@ -83,6 +73,8 @@ func (c *ConfigManager) GetDuration(key string) time.Duration {
|
||||
return d
|
||||
case int:
|
||||
return time.Duration(val)
|
||||
case int64:
|
||||
return time.Duration(val)
|
||||
case float32:
|
||||
return time.Duration(val)
|
||||
case float64:
|
||||
@@ -90,8 +82,7 @@ func (c *ConfigManager) GetDuration(key string) time.Duration {
|
||||
case nil:
|
||||
return 0
|
||||
default:
|
||||
return val.(time.Duration)
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +91,7 @@ func (c *ConfigManager) GetString(key string) string {
|
||||
defer c.mutex.RUnlock()
|
||||
v, ok := c.combinedConfig[strings.ToLower(key)]
|
||||
if !ok {
|
||||
v, ok = c.envConfig[strings.ToLower(c.envPrefix+key)]
|
||||
v, ok = c.envConfig[strings.ToLower(key)]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
@@ -119,7 +110,7 @@ func (c *ConfigManager) GetStringMap(key string) map[string]any {
|
||||
defer c.mutex.RUnlock()
|
||||
v, ok := c.combinedConfig[strings.ToLower(key)]
|
||||
if !ok {
|
||||
v, ok = c.envConfig[strings.ToLower(c.envPrefix+key)]
|
||||
v, ok = c.envConfig[strings.ToLower(key)]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
@@ -137,7 +128,7 @@ func (c *ConfigManager) GetStringSlice(key string) []string {
|
||||
defer c.mutex.RUnlock()
|
||||
v, ok := c.combinedConfig[strings.ToLower(key)]
|
||||
if !ok {
|
||||
v, ok = c.envConfig[strings.ToLower(c.envPrefix+key)]
|
||||
v, ok = c.envConfig[strings.ToLower(key)]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
@@ -145,6 +136,17 @@ func (c *ConfigManager) GetStringSlice(key string) []string {
|
||||
switch val := v.Value.(type) {
|
||||
case []string:
|
||||
return val
|
||||
case []any:
|
||||
var ret []string
|
||||
for _, v := range val {
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
ret = append(ret, v)
|
||||
default:
|
||||
ret = append(ret, fmt.Sprintf("%v", v))
|
||||
}
|
||||
}
|
||||
return ret
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@@ -155,7 +157,7 @@ func (c *ConfigManager) GetInt(key string) int {
|
||||
defer c.mutex.RUnlock()
|
||||
v, ok := c.combinedConfig[strings.ToLower(key)]
|
||||
if !ok {
|
||||
v, ok = c.envConfig[strings.ToLower(c.envPrefix+key)]
|
||||
v, ok = c.envConfig[strings.ToLower(key)]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
@@ -163,6 +165,8 @@ func (c *ConfigManager) GetInt(key string) int {
|
||||
switch val := v.Value.(type) {
|
||||
case int:
|
||||
return val
|
||||
case int64:
|
||||
return int(val)
|
||||
case string:
|
||||
i, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
@@ -185,7 +189,7 @@ func (c *ConfigManager) GetIntSlice(key string) []int {
|
||||
defer c.mutex.RUnlock()
|
||||
v, ok := c.combinedConfig[strings.ToLower(key)]
|
||||
if !ok {
|
||||
v, ok = c.envConfig[strings.ToLower(c.envPrefix+key)]
|
||||
v, ok = c.envConfig[strings.ToLower(key)]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
@@ -193,6 +197,31 @@ func (c *ConfigManager) GetIntSlice(key string) []int {
|
||||
switch val := v.Value.(type) {
|
||||
case []int:
|
||||
return val
|
||||
case []any:
|
||||
var ret []int
|
||||
for _, v := range val {
|
||||
switch v := v.(type) {
|
||||
case int:
|
||||
ret = append(ret, v)
|
||||
case int64:
|
||||
ret = append(ret, int(v))
|
||||
case string:
|
||||
i, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, i)
|
||||
case float32:
|
||||
ret = append(ret, int(v))
|
||||
case float64:
|
||||
ret = append(ret, int(v))
|
||||
case nil:
|
||||
continue
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
return ret
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
4
go.mod
4
go.mod
@@ -1,8 +1,8 @@
|
||||
module github.com/taigrr/jety
|
||||
|
||||
go 1.21.3
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.3.2
|
||||
github.com/BurntSushi/toml v1.6.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
4
go.sum
4
go.sum
@@ -1,5 +1,5 @@
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
||||
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
||||
110
jety.go
110
jety.go
@@ -1,10 +1,11 @@
|
||||
package config
|
||||
package jety
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -31,8 +32,8 @@ type (
|
||||
configPath string
|
||||
configFileUsed string
|
||||
configType configType
|
||||
envPrefix string
|
||||
mapConfig map[string]ConfigMap
|
||||
overrideConfig map[string]ConfigMap
|
||||
fileConfig map[string]ConfigMap
|
||||
defaultConfig map[string]ConfigMap
|
||||
envConfig map[string]ConfigMap
|
||||
combinedConfig map[string]ConfigMap
|
||||
@@ -41,20 +42,26 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
var ErrConfigFileNotFound = errors.New("config File Not Found")
|
||||
var (
|
||||
ErrConfigFileNotFound = errors.New("config file not found")
|
||||
ErrConfigFileEmpty = errors.New("config file is empty")
|
||||
)
|
||||
|
||||
func NewConfigManager() *ConfigManager {
|
||||
cm := ConfigManager{}
|
||||
cm.envConfig = make(map[string]ConfigMap)
|
||||
cm.mapConfig = make(map[string]ConfigMap)
|
||||
cm.overrideConfig = make(map[string]ConfigMap)
|
||||
cm.fileConfig = make(map[string]ConfigMap)
|
||||
cm.defaultConfig = make(map[string]ConfigMap)
|
||||
cm.combinedConfig = make(map[string]ConfigMap)
|
||||
cm.envPrefix = ""
|
||||
envSet := os.Environ()
|
||||
for _, env := range envSet {
|
||||
kv := strings.Split(env, "=")
|
||||
lower := strings.ToLower(kv[0])
|
||||
cm.envConfig[lower] = ConfigMap{Key: kv[0], Value: kv[1]}
|
||||
key, value, found := strings.Cut(env, "=")
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
lower := strings.ToLower(key)
|
||||
cm.envConfig[lower] = ConfigMap{Key: key, Value: value}
|
||||
}
|
||||
return &cm
|
||||
}
|
||||
@@ -62,15 +69,16 @@ func NewConfigManager() *ConfigManager {
|
||||
func (c *ConfigManager) WithEnvPrefix(prefix string) *ConfigManager {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
c.envPrefix = prefix
|
||||
envSet := os.Environ()
|
||||
c.envConfig = make(map[string]ConfigMap)
|
||||
for _, env := range envSet {
|
||||
kv := strings.Split(env, "=")
|
||||
if strings.HasPrefix(kv[0], prefix) {
|
||||
withoutPrefix := strings.TrimPrefix(kv[0], prefix)
|
||||
key, value, found := strings.Cut(env, "=")
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
if withoutPrefix, ok := strings.CutPrefix(key, prefix); ok {
|
||||
lower := strings.ToLower(withoutPrefix)
|
||||
c.envConfig[lower] = ConfigMap{Key: withoutPrefix, Value: kv[1]}
|
||||
c.envConfig[lower] = ConfigMap{Key: withoutPrefix, Value: value}
|
||||
}
|
||||
}
|
||||
return c
|
||||
@@ -89,16 +97,22 @@ func (c *ConfigManager) UseExplicitDefaults(enable bool) {
|
||||
}
|
||||
|
||||
func (c *ConfigManager) collapse() {
|
||||
c.mutex.RLock()
|
||||
defer c.mutex.RUnlock()
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
ccm := make(map[string]ConfigMap)
|
||||
// Precedence (highest to lowest): overrides (Set) > env > file > defaults
|
||||
for k, v := range c.defaultConfig {
|
||||
ccm[k] = v
|
||||
if _, ok := c.envConfig[k]; ok {
|
||||
ccm[k] = c.envConfig[k]
|
||||
}
|
||||
for k, v := range c.fileConfig {
|
||||
ccm[k] = v
|
||||
}
|
||||
for k := range c.defaultConfig {
|
||||
if v, ok := c.envConfig[k]; ok {
|
||||
ccm[k] = v
|
||||
}
|
||||
}
|
||||
for k, v := range c.mapConfig {
|
||||
for k, v := range c.overrideConfig {
|
||||
ccm[k] = v
|
||||
}
|
||||
c.combinedConfig = ccm
|
||||
@@ -144,6 +158,8 @@ func (c *ConfigManager) WriteConfig() error {
|
||||
}
|
||||
|
||||
func (c *ConfigManager) SetConfigType(configType string) error {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
switch configType {
|
||||
case "toml":
|
||||
c.configType = ConfigTypeTOML
|
||||
@@ -158,14 +174,48 @@ func (c *ConfigManager) SetConfigType(configType string) error {
|
||||
}
|
||||
|
||||
func (c *ConfigManager) SetEnvPrefix(prefix string) {
|
||||
c.envPrefix = prefix
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
// Re-read environment variables, stripping the prefix from matching keys.
|
||||
// This mirrors WithEnvPrefix behavior so that prefixed env vars are
|
||||
// accessible by their unprefixed key name.
|
||||
envSet := os.Environ()
|
||||
c.envConfig = make(map[string]ConfigMap)
|
||||
for _, env := range envSet {
|
||||
key, value, found := strings.Cut(env, "=")
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
if withoutPrefix, ok := strings.CutPrefix(key, prefix); ok {
|
||||
lower := strings.ToLower(withoutPrefix)
|
||||
c.envConfig[lower] = ConfigMap{Key: withoutPrefix, Value: value}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConfigManager) ReadInConfig() error {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
// assume config = map[string]any
|
||||
confFileData, err := readFile(c.configFileUsed, c.configType)
|
||||
c.mutex.RLock()
|
||||
configFile := c.configFileUsed
|
||||
if configFile == "" && c.configPath != "" && c.configName != "" {
|
||||
ext := ""
|
||||
switch c.configType {
|
||||
case ConfigTypeTOML:
|
||||
ext = ".toml"
|
||||
case ConfigTypeYAML:
|
||||
ext = ".yaml"
|
||||
case ConfigTypeJSON:
|
||||
ext = ".json"
|
||||
}
|
||||
configFile = filepath.Join(c.configPath, c.configName+ext)
|
||||
}
|
||||
configType := c.configType
|
||||
c.mutex.RUnlock()
|
||||
|
||||
if configFile == "" {
|
||||
return errors.New("no config file specified: use SetConfigFile or SetConfigDir + SetConfigName")
|
||||
}
|
||||
|
||||
confFileData, err := readFile(configFile, configType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -174,12 +224,22 @@ func (c *ConfigManager) ReadInConfig() error {
|
||||
lower := strings.ToLower(k)
|
||||
conf[lower] = ConfigMap{Key: k, Value: v}
|
||||
}
|
||||
c.mapConfig = conf
|
||||
c.mutex.Lock()
|
||||
c.fileConfig = conf
|
||||
c.configFileUsed = configFile
|
||||
c.mutex.Unlock()
|
||||
c.collapse()
|
||||
return nil
|
||||
}
|
||||
|
||||
func readFile(filename string, fileType configType) (map[string]any, error) {
|
||||
fileData := make(map[string]any)
|
||||
if d, err := os.Stat(filename); os.IsNotExist(err) {
|
||||
return nil, ErrConfigFileNotFound
|
||||
} else if d.Size() == 0 {
|
||||
return nil, ErrConfigFileEmpty
|
||||
}
|
||||
|
||||
switch fileType {
|
||||
case ConfigTypeTOML:
|
||||
_, err := toml.DecodeFile(filename, &fileData)
|
||||
|
||||
1399
jety_test.go
Normal file
1399
jety_test.go
Normal file
File diff suppressed because it is too large
Load Diff
16
setters.go
16
setters.go
@@ -1,4 +1,4 @@
|
||||
package config
|
||||
package jety
|
||||
|
||||
import (
|
||||
"strings"
|
||||
@@ -8,7 +8,7 @@ func (c *ConfigManager) SetBool(key string, value bool) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
lower := strings.ToLower(key)
|
||||
c.mapConfig[lower] = ConfigMap{Key: key, Value: value}
|
||||
c.overrideConfig[lower] = ConfigMap{Key: key, Value: value}
|
||||
c.combinedConfig[lower] = ConfigMap{Key: key, Value: value}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func (c *ConfigManager) SetString(key string, value string) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
lower := strings.ToLower(key)
|
||||
c.mapConfig[lower] = ConfigMap{Key: key, Value: value}
|
||||
c.overrideConfig[lower] = ConfigMap{Key: key, Value: value}
|
||||
c.combinedConfig[lower] = ConfigMap{Key: key, Value: value}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ func (c *ConfigManager) Set(key string, value any) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
lower := strings.ToLower(key)
|
||||
c.mapConfig[lower] = ConfigMap{Key: key, Value: value}
|
||||
c.overrideConfig[lower] = ConfigMap{Key: key, Value: value}
|
||||
c.combinedConfig[lower] = ConfigMap{Key: key, Value: value}
|
||||
}
|
||||
|
||||
@@ -33,12 +33,14 @@ func (c *ConfigManager) SetDefault(key string, value any) {
|
||||
defer c.mutex.Unlock()
|
||||
lower := strings.ToLower(key)
|
||||
c.defaultConfig[lower] = ConfigMap{Key: key, Value: value}
|
||||
if _, ok := c.mapConfig[lower]; !ok {
|
||||
if _, ok := c.overrideConfig[lower]; !ok {
|
||||
if envVal, ok := c.envConfig[lower]; ok {
|
||||
c.mapConfig[lower] = envVal
|
||||
c.combinedConfig[lower] = envVal
|
||||
c.overrideConfig[lower] = ConfigMap{Key: key, Value: envVal.Value}
|
||||
c.combinedConfig[lower] = ConfigMap{Key: key, Value: envVal.Value}
|
||||
} else {
|
||||
c.combinedConfig[lower] = ConfigMap{Key: key, Value: value}
|
||||
}
|
||||
} else {
|
||||
c.combinedConfig[lower] = c.overrideConfig[lower]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user