Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/gookit/config

📝 Go configuration manage(load,get,set,export). support JSON, YAML, TOML, Properties, INI, HCL, ENV and Flags. Multi file load, data override merge, parse ENV var. Go应用配置加载管理,支持多种格式,多文件加载,远程文件加载,支持数据合并,解析环境变量名
https://github.com/gookit/config

config config-management configuration configuration-management flags goconfig gookit hcl ini json properties toml yaml

Last synced: 10 days ago
JSON representation

📝 Go configuration manage(load,get,set,export). support JSON, YAML, TOML, Properties, INI, HCL, ENV and Flags. Multi file load, data override merge, parse ENV var. Go应用配置加载管理,支持多种格式,多文件加载,远程文件加载,支持数据合并,解析环境变量名

Awesome Lists containing this project

README

        

# Config

![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/gookit/config?style=flat-square)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/1e0f0ca096d94ffdab375234ec4167ee)](https://app.codacy.com/gh/gookit/config?utm_source=github.com&utm_medium=referral&utm_content=gookit/config&utm_campaign=Badge_Grade_Settings)
[![Build Status](https://travis-ci.org/gookit/config.svg?branch=master)](https://travis-ci.org/gookit/config)
[![Actions Status](https://github.com/gookit/config/workflows/Unit-Tests/badge.svg)](https://github.com/gookit/config/actions)
[![Coverage Status](https://coveralls.io/repos/github/gookit/config/badge.svg?branch=master)](https://coveralls.io/github/gookit/config?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/gookit/config)](https://goreportcard.com/report/github.com/gookit/config)
[![Go Reference](https://pkg.go.dev/badge/github.com/gookit/config/v2.svg)](https://pkg.go.dev/github.com/gookit/config/v2)

`config` - Simple, full-featured Go application configuration management tool library.

> **[中文说明](README.zh-CN.md)**

## Features

- Support multi format: `JSON`(default), `JSON5`, `INI`, `Properties`, `YAML`, `TOML`, `HCL`, `ENV`, `Flags`
- `JSON` content support comments. will auto clear comments
- Other drivers are used on demand, not used will not be loaded into the application.
- Possibility to add custom driver for your specific format
- Support multi-file and multi-data loading
- Support for loading configuration from system ENV
- Support for loading configuration data from remote URLs
- Support for setting configuration data from command line(`flags`)
- Support listen and fire events on config data changed.
- allow events: `set.value`, `set.data`, `load.data`, `clean.data`, `reload.data`
- Support data overlay and merge, automatically load by key when loading multiple copies of data
- Support for binding all or part of the configuration data to the structure
- Support init default value by struct tag `default:"def_value"`
- Support init default value from ENV `default:"${APP_ENV | dev}"`
- Support get sub value by key-path, like `map.key` `arr.2`
- Support parse ENV name and allow with default value. like `envKey: ${SHELL|/bin/bash}` -> `envKey: /bin/zsh`
- Generic API: `Get` `Int` `Uint` `Int64` `Float` `String` `Bool` `Ints` `IntMap` `Strings` `StringMap` ...
- Complete unit test(code coverage > 95%)

## Only use INI

If you just want to use INI for simple config management, recommended use [gookit/ini](https://github.com/gookit/ini)

### Load dotenv file

On `gookit/ini`: Provide a sub-package `dotenv` that supports importing data from files (eg `.env`) to ENV

```shell
go get github.com/gookit/ini/v2/dotenv
```

## GoDoc

- [godoc for github](https://pkg.go.dev/github.com/gookit/config)

## Install

```bash
go get github.com/gookit/config/v2
```

## Usage

Here using the yaml format as an example(`testdata/yml_other.yml`):

```yaml
name: app2
debug: false
baseKey: value2
shell: ${SHELL}
envKey1: ${NotExist|defValue}

map1:
key: val2
key2: val20

arr1:
- val1
- val21
```

### Load data

> examples code please see [_examples/yaml.go](_examples/yaml.go):

```go
package main

import (
"github.com/gookit/config/v2"
"github.com/gookit/config/v2/yaml"
)

// go run ./examples/yaml.go
func main() {
// config.ParseEnv: will parse env var in string value. eg: shell: ${SHELL}
config.WithOptions(config.ParseEnv)

// add driver for support yaml content
config.AddDriver(yaml.Driver)

err := config.LoadFiles("testdata/yml_base.yml")
if err != nil {
panic(err)
}

// load more files
err = config.LoadFiles("testdata/yml_other.yml")
// can also load multi at once
// err := config.LoadFiles("testdata/yml_base.yml", "testdata/yml_other.yml")
if err != nil {
panic(err)
}

// fmt.Printf("config data: \n %#v\n", config.Data())
}
```
**Usage tips**:

- More extra options can be added using `WithOptions()`. For example: `ParseEnv`, `ParseDefault`
- You can use `AddDriver()` to add the required format driver (`json` is loaded by default, no need to add)
- The configuration data can then be loaded using `LoadFiles()` `LoadStrings()` etc.
- You can pass in multiple files or call multiple times
- Data loaded multiple times will be automatically merged by key

## Bind Structure

> Note: The default binding mapping tag of a structure is `mapstructure`, which can be changed by setting the decoder's option `options.DecoderConfig.TagName`

```go
type User struct {
Age int `mapstructure:"age"`
Key string `mapstructure:"key"`
UserName string `mapstructure:"user_name"`
Tags []int `mapstructure:"tags"`
}

user := User{}
err = config.BindStruct("user", &user)

fmt.Println(user.UserName) // inhere
```

**Change struct tag name**

```go
config.WithOptions(func(opt *Options) {
options.DecoderConfig.TagName = "config"
})

// use custom tag name.
type User struct {
Age int `config:"age"`
Key string `config:"key"`
UserName string `config:"user_name"`
Tags []int `config:"tags"`
}

user := User{}
err = config.Decode(&user)
```

**Can bind all config data to a struct**:

```go
config.Decode(&myConf)
// can also
config.BindStruct("", &myConf)
```

> `config.MapOnExists` like `BindStruct`,but map binding only if key exists

### Direct read data

- Get integer

```go
age := config.Int("age")
fmt.Print(age) // 100
```

- Get bool

```go
val := config.Bool("debug")
fmt.Print(val) // true
```

- Get string

```go
name := config.String("name")
fmt.Print(name) // inhere
```

- Get strings(slice)

```go
arr1 := config.Strings("arr1")
fmt.Printf("%#v", arr1) // []string{"val1", "val21"}
```

- Get string map

```go
val := config.StringMap("map1")
fmt.Printf("%#v",val) // map[string]string{"key":"val2", "key2":"val20"}
```

- Value contains ENV var

```go
value := config.String("shell")
fmt.Print(value) // "/bin/zsh"
```

- Get value by key path

```go
// from array
value := config.String("arr1.0")
fmt.Print(value) // "val1"

// from map
value := config.String("map1.key")
fmt.Print(value) // "val2"
```

- Setting new value

```go
// set value
config.Set("name", "new name")
name = config.String("name")
fmt.Print(name) // "new name"
```

## Load from flags

> Support simple flags parameter parsing, loading

```go
// flags like: --name inhere --env dev --age 99 --debug

// load flag info
keys := []string{"name", "env", "age:int" "debug:bool"}
err := config.LoadFlags(keys)

// read
config.String("name") // "inhere"
config.String("env") // "dev"
config.Int("age") // 99
config.Bool("debug") // true
```

## Load from ENV

```go
// os env: APP_NAME=config APP_DEBUG=true
// load ENV info
config.LoadOSEnvs(map[string]string{"APP_NAME": "app_name", "APP_DEBUG": "app_debug"})

// read
config.Bool("app_debug") // true
config.String("app_name") // "config"
```

## New config instance

You can create custom config instance

```go
// create new instance, will auto register JSON driver
myConf := config.New("my-conf")

// create empty instance
myConf := config.NewEmpty("my-conf")

// create and with some options
myConf := config.NewWithOptions("my-conf", config.ParseEnv, config.ReadOnly)
```

## Listen config change

Now, you can add a hook func for listen config data change. then, you can do something like: write data to file

**Add hook func on create config**:

```go
hookFn := func(event string, c *Config) {
fmt.Println("fire the:", event)
}

c := NewWithOptions("test", config.WithHookFunc(hookFn))
// for global config
config.WithOptions(config.WithHookFunc(hookFn))
```

After that, when calling `LoadXXX, Set, SetData, ClearData` methods, it will output:

```text
fire the: load.data
fire the: set.value
fire the: set.data
fire the: clean.data
```

### Watch loaded config files

To listen for changes to loaded config files, and reload the config when it changes, you need to use the https://github.com/fsnotify/fsnotify library.
For usage, please refer to the example [./_example/watch_file.go](_examples/watch_file.go)

Also, you need to listen to the `reload.data` event:

```go
config.WithOptions(config.WithHookFunc(func(event string, c *config.Config) {
if event == config.OnReloadData {
fmt.Println("config reloaded, you can do something ....")
}
}))
```

When the configuration changes, you can do related things, for example: rebind the configuration to your struct.

## Dump config data

> Can use `config.DumpTo()` export the configuration data to the specified `writer`, such as: buffer,file

**Dump to JSON file**

```go
buf := new(bytes.Buffer)

_, err := config.DumpTo(buf, config.JSON)
ioutil.WriteFile("my-config.json", buf.Bytes(), 0755)
```

**Dump pretty JSON**

You can set the default var `JSONMarshalIndent` or custom a new JSON driver.

```go
config.JSONMarshalIndent = " "
```

**Dump to YAML file**

```go
_, err := config.DumpTo(buf, config.YAML)
ioutil.WriteFile("my-config.yaml", buf.Bytes(), 0755)
```

## Available options

```go
// Options config options
type Options struct {
// parse env in string value. like: "${EnvName}" "${EnvName|default}"
ParseEnv bool
// ParseTime parses a duration string to time.Duration
// eg: 10s, 2m
ParseTime bool
// config is readonly. default is False
Readonly bool
// enable config data cache. default is False
EnableCache bool
// parse key, allow find value by key path. default is True eg: 'key.sub' will find `map[key]sub`
ParseKey bool
// the delimiter char for split key path, if `FindByPath=true`. default is '.'
Delimiter byte
// default write format
DumpFormat string
// default input format
ReadFormat string
// DecoderConfig setting for binding data to struct
DecoderConfig *mapstructure.DecoderConfig
// HookFunc on data changed.
HookFunc HookFunc
// ParseDefault tag on binding data to struct. tag: default
ParseDefault bool
}
```

Examples for set options:

```go
config.WithOptions(config.WithTagName("mytag"))
config.WithOptions(func(opt *Options) {
opt.SetTagNames("config")
})
```

### Options: Parse default

Support parse default value by struct tag `default`

```go
// add option: config.ParseDefault
c := config.New("test").WithOptions(config.ParseDefault)

// only set name
c.SetData(map[string]any{
"name": "inhere",
})

// age load from default tag
type User struct {
Age int `default:"30"`
Name string
Tags []int
}

user := &User{}
goutil.MustOk(c.Decode(user))
dump.Println(user)
```

**Output**:

```shell
&config_test.User {
Age: int(30),
Name: string("inhere"), #len=6
Tags: []int [ #len=0
],
},
```

## API Methods Refer

### Load Config

- `LoadOSEnvs(nameToKeyMap map[string]string)` Load data from os ENV
- `LoadData(dataSource ...any) (err error)` Load from struts or maps
- `LoadFlags(keys []string) (err error)` Load from CLI flags
- `LoadExists(sourceFiles ...string) (err error)`
- `LoadFiles(sourceFiles ...string) (err error)`
- `LoadFromDir(dirPath, format string) (err error)` Load custom format files from the given directory, the file name will be used as the key
- `LoadRemote(format, url string) (err error)`
- `LoadSources(format string, src []byte, more ...[]byte) (err error)`
- `LoadStrings(format string, str string, more ...string) (err error)`
- `LoadFilesByFormat(format string, sourceFiles ...string) (err error)`
- `LoadExistsByFormat(format string, sourceFiles ...string) error`

### Getting Values

- `Bool(key string, defVal ...bool) bool`
- `Int(key string, defVal ...int) int`
- `Uint(key string, defVal ...uint) uint`
- `Int64(key string, defVal ...int64) int64`
- `Ints(key string) (arr []int)`
- `IntMap(key string) (mp map[string]int)`
- `Float(key string, defVal ...float64) float64`
- `String(key string, defVal ...string) string`
- `Strings(key string) (arr []string)`
- `SubDataMap(key string) maputi.Data`
- `StringMap(key string) (mp map[string]string)`
- `Get(key string, findByPath ...bool) (value any)`

**Mapping data to struct:**

- `Decode(dst any) error`
- `BindStruct(key string, dst any) error`
- `MapOnExists(key string, dst any) error`

### Setting Values

- `Set(key string, val any, setByPath ...bool) (err error)`

### Useful Methods

- `Getenv(name string, defVal ...string) (val string)`
- `AddDriver(driver Driver)`
- `Data() map[string]any`
- `SetData(data map[string]any)` set data to override the Config.Data
- `Exists(key string, findByPath ...bool) bool`
- `DumpTo(out io.Writer, format string) (n int64, err error)`

## Run Tests

```bash
go test -cover
// contains all sub-folder
go test -cover ./...
```

## Projects using config

Check out these projects, which use https://github.com/gookit/config :

- https://github.com/JanDeDobbeleer/oh-my-posh A prompt theme engine for any shell.
- [+ See More](https://pkg.go.dev/github.com/gookit/config?tab=importedby)

## Gookit packages

- [gookit/ini](https://github.com/gookit/ini) Go config management, use INI files
- [gookit/rux](https://github.com/gookit/rux) Simple and fast request router for golang HTTP
- [gookit/gcli](https://github.com/gookit/gcli) build CLI application, tool library, running CLI commands
- [gookit/event](https://github.com/gookit/event) Lightweight event manager and dispatcher implements by Go
- [gookit/cache](https://github.com/gookit/cache) Generic cache use and cache manager for golang. support File, Memory, Redis, Memcached.
- [gookit/config](https://github.com/gookit/config) Go config management. support JSON, YAML, TOML, INI, HCL, ENV and Flags
- [gookit/color](https://github.com/gookit/color) A command-line color library with true color support, universal API methods and Windows support
- [gookit/filter](https://github.com/gookit/filter) Provide filtering, sanitizing, and conversion of golang data
- [gookit/validate](https://github.com/gookit/validate) Use for data validation and filtering. support Map, Struct, Form data
- [gookit/goutil](https://github.com/gookit/goutil) Some utils for the Go: string, array/slice, map, format, cli, env, filesystem, test and more
- More, please see https://github.com/gookit

## See also

- Ini parser [gookit/ini/parser](https://github.com/gookit/ini/tree/master/parser)
- Properties parser [gookit/properties](https://github.com/gookit/properties)
- Json5 parser
- [yosuke-furukawa/json5](https://github.com/yosuke-furukawa/json5)
- [titanous/json5](https://github.com/titanous/json5)
- Json parser
- [goccy/go-json](https://github.com/goccy/go-json)
- [json-iterator/go](https://github.com/json-iterator/go)
- Yaml parser
- [goccy/go-yaml](https://github.com/goccy/go-yaml)
- [go-yaml/yaml](https://github.com/go-yaml/yaml)
- Toml parser [go toml](https://github.com/BurntSushi/toml)
- Data merge [mergo](https://github.com/imdario/mergo)
- Map structure [mapstructure](https://github.com/mitchellh/mapstructure)

## License

**MIT**