From dd7e2e3ecbaa1f4878ee9bb8fc495c265bbc7721 Mon Sep 17 00:00:00 2001 From: Tai Groot Date: Fri, 6 Mar 2026 10:32:42 +0000 Subject: [PATCH] feat(getters): add GetFloat64 and GetInt64, fix docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add GetFloat64 and GetInt64 methods to ConfigManager and package-level API - Fix README precedence: actual order is Set > env > file > default (was incorrectly documented as file > env > default) - Fix GetStringMap return type in API table: map[string]any, not map[string]string - Bump Go to 1.26.1 - Add tests for all new getters (coverage 94.7% → 95.2%) --- README.md | 12 ++++--- default.go | 8 +++++ getters.go | 58 ++++++++++++++++++++++++++++++++++ go.mod | 2 +- jety_test.go | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 162 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 61cd9e6..93f2977 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Originally built to support [grlx](http://github.com/gogrlx/grlx). go get github.com/taigrr/jety ``` -Requires Go 1.26 or later. +Requires Go 1.26.1 or later. ## Quick Start @@ -39,7 +39,7 @@ func main() { // handle error } - // Get values (config file > env > default) + // Get values (Set > env > config file > default) port := jety.GetInt("port") host := jety.GetString("host") } @@ -53,7 +53,7 @@ func main() { - **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 +- **Config precedence**: Set() > environment > config file > defaults ## Nested Configuration @@ -103,7 +103,7 @@ export MYAPP_PORT=9000 export MYAPP_SERVICES_CLOUD_VAR=override_value ``` -**Note**: Environment variables override defaults but config files take highest precedence. +**Note**: Environment variables override both defaults and config file values for registered keys (keys that appear in defaults or the config file). ## API @@ -127,11 +127,13 @@ export MYAPP_SERVICES_CLOUD_VAR=override_value | `Get(key)` | Get raw value | | `GetString(key)` | Get as string | | `GetInt(key)` | Get as int | +| `GetInt64(key)` | Get as int64 | +| `GetFloat64(key)` | Get as float64 | | `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 | +| `GetStringMap(key)` | Get as map[string]any | | `IsSet(key)` | Check if key has a value | | `AllKeys()` | List all known keys | | `AllSettings()` | Get all values as a map | diff --git a/default.go b/default.go index 1413c99..d042f5a 100644 --- a/default.go +++ b/default.go @@ -20,6 +20,14 @@ func SetConfigName(name string) { defaultConfigManager.SetConfigName(name) } +func GetFloat64(key string) float64 { + return defaultConfigManager.GetFloat64(key) +} + +func GetInt64(key string) int64 { + return defaultConfigManager.GetInt64(key) +} + func GetInt(key string) int { return defaultConfigManager.GetInt(key) } diff --git a/getters.go b/getters.go index 9b345bd..952f611 100644 --- a/getters.go +++ b/getters.go @@ -146,6 +146,64 @@ func (c *ConfigManager) GetStringSlice(key string) []string { } } +func (c *ConfigManager) GetFloat64(key string) float64 { + c.mutex.RLock() + defer c.mutex.RUnlock() + v, ok := c.resolve(key) + if !ok { + return 0 + } + switch val := v.Value.(type) { + case float64: + return val + case float32: + return float64(val) + case int: + return float64(val) + case int64: + return float64(val) + case string: + f, err := strconv.ParseFloat(val, 64) + if err != nil { + return 0 + } + return f + case nil: + return 0 + default: + return 0 + } +} + +func (c *ConfigManager) GetInt64(key string) int64 { + c.mutex.RLock() + defer c.mutex.RUnlock() + v, ok := c.resolve(key) + if !ok { + return 0 + } + switch val := v.Value.(type) { + case int64: + return val + case int: + return int64(val) + case string: + i, err := strconv.ParseInt(val, 10, 64) + if err != nil { + return 0 + } + return i + case float32: + return int64(val) + case float64: + return int64(val) + case nil: + return 0 + default: + return 0 + } +} + func (c *ConfigManager) GetInt(key string) int { c.mutex.RLock() defer c.mutex.RUnlock() diff --git a/go.mod b/go.mod index b5623d7..5be6595 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/taigrr/jety -go 1.26.0 +go 1.26.1 require ( github.com/BurntSushi/toml v1.6.0 diff --git a/jety_test.go b/jety_test.go index fece21c..4e7edb3 100644 --- a/jety_test.go +++ b/jety_test.go @@ -120,6 +120,78 @@ func TestSetAndGetInt(t *testing.T) { } } +func TestSetAndGetInt64(t *testing.T) { + cm := NewConfigManager() + + tests := []struct { + name string + value any + want int64 + }{ + {"int64", int64(9223372036854775807), 9223372036854775807}, + {"int", 42, 42}, + {"string", "123456789012345", 123456789012345}, + {"float64", 99.9, 99}, + {"float32", float32(50.5), 50}, + {"invalid string", "not-a-number", 0}, + {"nil", nil, 0}, + {"unknown type", struct{}{}, 0}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cm.Set("key", tt.value) + got := cm.GetInt64("key") + if got != tt.want { + t.Errorf("GetInt64() = %d, want %d", got, tt.want) + } + }) + } +} + +func TestSetAndGetFloat64(t *testing.T) { + cm := NewConfigManager() + + tests := []struct { + name string + value any + want float64 + }{ + {"float64", 3.14159, 3.14159}, + {"float32", float32(2.5), 2.5}, + {"int", 42, 42.0}, + {"int64", int64(100), 100.0}, + {"string", "1.618", 1.618}, + {"invalid string", "not-a-float", 0}, + {"nil", nil, 0}, + {"unknown type", struct{}{}, 0}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cm.Set("key", tt.value) + got := cm.GetFloat64("key") + if got != tt.want { + t.Errorf("GetFloat64() = %f, want %f", got, tt.want) + } + }) + } +} + +func TestGetFloat64NotSet(t *testing.T) { + cm := NewConfigManager() + if got := cm.GetFloat64("nonexistent"); got != 0 { + t.Errorf("GetFloat64(nonexistent) = %f, want 0", got) + } +} + +func TestGetInt64NotSet(t *testing.T) { + cm := NewConfigManager() + if got := cm.GetInt64("nonexistent"); got != 0 { + t.Errorf("GetInt64(nonexistent) = %d, want 0", got) + } +} + func TestSetAndGetBool(t *testing.T) { cm := NewConfigManager() @@ -1466,3 +1538,19 @@ func TestPackageLevelGet(t *testing.T) { t.Error("Get(key) failed") } } + +func TestPackageLevelGetFloat64(t *testing.T) { + defaultConfigManager = NewConfigManager() + Set("rate", 3.14) + if got := GetFloat64("rate"); got != 3.14 { + t.Errorf("GetFloat64(rate) = %f, want 3.14", got) + } +} + +func TestPackageLevelGetInt64(t *testing.T) { + defaultConfigManager = NewConfigManager() + Set("bignum", int64(9223372036854775807)) + if got := GetInt64("bignum"); got != 9223372036854775807 { + t.Errorf("GetInt64(bignum) = %d, want 9223372036854775807", got) + } +}