Initial QSfera import
This commit is contained in:
+559
@@ -0,0 +1,559 @@
|
||||
# Config
|
||||
|
||||

|
||||
[](https://app.codacy.com/gh/gookit/config?utm_source=github.com&utm_medium=referral&utm_content=gookit/config&utm_campaign=Badge_Grade_Settings)
|
||||
[](https://travis-ci.org/gookit/config)
|
||||
[](https://github.com/gookit/config/actions)
|
||||
[](https://coveralls.io/github/gookit/config?branch=master)
|
||||
[](https://goreportcard.com/report/github.com/gookit/config)
|
||||
[](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 ENV
|
||||
|
||||
Support load ENV vars to config data.
|
||||
|
||||
- Support set value to sub key in map.
|
||||
- eg: `{"DB_USERNAME": "db.username"}` value will set to `username` in `db`
|
||||
|
||||
```go
|
||||
// os env: APP_NAME=config APP_DEBUG=true DB_USERNAME=someone
|
||||
|
||||
// load ENV info
|
||||
config.LoadOSEnvs(map[string]string{"APP_NAME": "app_name", "APP_DEBUG": "app_debug", "DB_USERNAME": "db.username"})
|
||||
|
||||
// read
|
||||
config.Bool("app_debug") // true
|
||||
config.String("app_name") // "config"
|
||||
```
|
||||
|
||||
## Load from flags
|
||||
|
||||
Support simple CLI flags parameter parsing, load to config data.
|
||||
|
||||
- define format: `name:type:desc` OR `name:type` OR `name:desc` (type, desc is optional)
|
||||
- `type` can set `flag` type. allow: `bool`, `int`, `string`(default)
|
||||
- `desc` can set `flag` description
|
||||
- `name` can be in key path format.
|
||||
- eg: `db.username`, input: `--db.username=someone` values will be mapped to `username` of the `db` configuration
|
||||
|
||||
```go
|
||||
// 'debug' flag is bool type
|
||||
config.LoadFlags([]string{"env", "debug:bool"})
|
||||
// can with flag desc message
|
||||
config.LoadFlags([]string{"env:set the run env"})
|
||||
config.LoadFlags([]string{"debug:bool:set debug mode"})
|
||||
// can set value to map key. eg: myapp --map1.sub-key=val
|
||||
config.LoadFlags([]string{"map1.sub-key"})
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
```go
|
||||
// flags like: --name inhere --env dev --age 99 --debug --map1.sub-key=val
|
||||
|
||||
// load flag info
|
||||
keys := []string{
|
||||
"name",
|
||||
"env:set the run env",
|
||||
"age:int",
|
||||
"debug:bool:set debug mode",
|
||||
"map1.sub-key",
|
||||
}
|
||||
err := config.LoadFlags(keys)
|
||||
|
||||
// read
|
||||
config.String("name") // "inhere"
|
||||
config.String("env") // "dev"
|
||||
config.Int("age") // 99
|
||||
config.Bool("debug") // true
|
||||
config.Get("map1") // map[string]any{"sub-key":"val"}
|
||||
```
|
||||
|
||||
## 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
|
||||
}
|
||||
```
|
||||
|
||||
> **TIP**: please visit https://pkg.go.dev/github.com/gookit/config/v2#Options to see the latest options information
|
||||
|
||||
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`, and support parse fields in sub struct.
|
||||
|
||||
> **NOTE**⚠️ If you want to parse a sub-struct, you need to set the `default:""` flag on the parent struct,
|
||||
> otherwise the fields of the sub-struct will not be resolved.
|
||||
|
||||
```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**
|
||||
Reference in New Issue
Block a user