Files
jety/README.md
Tai Groot 0c0fc0320c feat(unmarshal): add Unmarshal and UnmarshalKey methods
Add struct unmarshaling support via JSON round-trip:
- Unmarshal() unmarshals full combined config into a struct
- UnmarshalKey() unmarshals a specific key (supports dot notation)
- Package-level convenience functions for default manager
- Comprehensive tests covering JSON, TOML, YAML, nested keys, scalars
- Bump Go to 1.26.2
- Fix stale Delete reference in README API table
- Add .gitignore for cover.out
2026-04-13 07:36:10 +00:00

202 lines
5.8 KiB
Markdown

# JETY
[![test](https://github.com/taigrr/jety/actions/workflows/test.yml/badge.svg)](https://github.com/taigrr/jety/actions/workflows/test.yml)
[![govulncheck](https://github.com/taigrr/jety/actions/workflows/govulncheck.yml/badge.svg)](https://github.com/taigrr/jety/actions/workflows/govulncheck.yml)
JSON, ENV, TOML, YAML
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).
## Installation
```bash
go get github.com/taigrr/jety
```
Requires Go 1.26.2 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 (Set > env > config file > 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**: Set() > environment > config file > defaults
## Nested Configuration
For nested config structures like:
```toml
[services.cloud]
var = "xyz"
timeout = "30s"
[services.cloud.auth]
client_id = "abc123"
```
Use `Sub()` to get a scoped ConfigManager for a nested section:
```go
cloud := jety.Sub("services")
if cloud != nil {
inner := cloud.Sub("cloud")
if inner != nil {
varValue := inner.GetString("var") // "xyz"
timeout := inner.GetDuration("timeout") // 30s
}
}
```
Or access nested values directly with `GetStringMap` and type assertions:
```go
services := jety.GetStringMap("services")
cloud := services["cloud"].(map[string]any)
varValue := cloud["var"].(string) // "xyz"
```
### 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 both defaults and config file values for registered keys (keys that appear in defaults or the config file).
## 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 |
| `Sub(key)` | Get scoped sub-config |
| `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]any |
| `IsSet(key)` | Check if key has a value |
| `AllKeys()` | List all known keys |
| `AllSettings()` | Get all values as a map |
| `Unmarshal(target)` | Unmarshal config to struct |
| `UnmarshalKey(key, target)` | Unmarshal a key to struct |
### Environment
| Function | Description |
| ----------------------- | --------------------------------------------------- |
| `WithEnvPrefix(prefix)` | Filter env vars by prefix (strips prefix from keys) |
| `SetEnvPrefix(prefix)` | Set prefix for env var lookups |
## Struct Unmarshaling
Unmarshal your configuration directly into Go structs:
```go
type Config struct {
Host string `json:"host"`
Port int `json:"port"`
Debug bool `json:"debug"`
}
jety.SetConfigFile("config.json")
jety.SetConfigType("json")
jety.ReadInConfig()
var cfg Config
if err := jety.Unmarshal(&cfg); err != nil {
log.Fatal(err)
}
```
For nested sections, use `UnmarshalKey`:
```go
type DatabaseConfig struct {
Host string `json:"host"`
Port int `json:"port"`
Name string `json:"name"`
}
var dbCfg DatabaseConfig
if err := jety.UnmarshalKey("database", &dbCfg); err != nil {
log.Fatal(err)
}
```
Struct tags use `json:"..."` since the unmarshaling uses JSON round-trip internally.
Dot notation works with `UnmarshalKey` for deeply nested values (e.g., `"services.api"`).
## License
See [LICENSE](LICENSE) file.