# Config

**config** is a simple golang library and designed to read configurations from JSON, Yaml files, environment variables and command line. **config** depends on [go-yaml](https://github.com/go-yaml/yaml) to analyze Yaml file and uses built-in golang library to handle JSON file.

## Installation

1. Install [Yaml](https://github.com/go-yaml/yaml) library first:

```sh
go get gopkg.in/yaml.v3
```

1. Install **config** library:

```sh
git@git.mousesoft.ru:ms/config.git
```

## Usage

### I. Define configuration name in structure tags

Like JSON, Yaml, **config** uses tags to define configurations:

| Tag       | Example                                 | Function                                                        |
| --------- | --------------------------------------- | --------------------------------------------------------------- |
| json      | Host string `json:"host"`               | Maps `Host` to a JSON field: **host**                           |
| yaml      | Host string `yaml:"host"`               | Maps `Host` to a Yaml field: **host**                           |
| env       | Host string `env:"HOST"`                | Maps `Host` to a Environment variable: **HOST**                 |
| cli       | Host string `cli:"host database host"`  | Maps `Host` to a command line argument: **-host** or **--host** |
| default   | Port int `default:"8080"`               | Defines the port with default value: **8080**                   |
| separator | Path string `json:"path" separator:";"` | Separator is used to split string to a slice                    |
| usage     | Usage string `usage:"host address"`     | Usage description                                               |

#### 1. Data types

 **config** supports the following golang data types:

* bool
* string
* int8, int16, int, int32, int64
* uint8, uint16, uint, uint32, uint64
* float32, float64
* time.Duration
* slice type. e.g: []string, []int ...
  
#### 2. Defines **default** values

Using **default** keyword in structure tags to define default value:

```golang
type Log struct {
    Path  string `default:"/var/logs"`
    Level string `default:"debug"`
}
```

#### 3. Defines configuration name for JSON

Like parsing JSON object, using **json** keyword to define configuration name:

```golang
  type Database struct {
    Host     string `json:"host"`
    Port     int    `json:"port"`
    Username string `default:"admin" json:"username"`
    Password string `default:"admin" json:"password"`
    Log      Log    `json:"log"`
  }
```

Corresponding JSON file:

```json
 {
   "host": "test.db.hostname",
   "port": 8080,
   "username": "admin",
   "password": "admin",
   "log": {
     "path": "/var/logs/db",
     "level": "debug"
   }
 }
 ```

#### 4. Defines configuration name for Yaml

Like parsing Yaml object, using **yaml** keyword to define configuration name

```golang
  type Database struct {
    Host     string `yaml:"host"`
    Port     int    `yaml:"port"`
    Username string `default:"admin" yaml:"username"`
    Password string `default:"admin" yaml:"password"`
    Log      Log    `yaml:"log"`
  }
```

Corresponding Yaml file:

```yaml
  host: test.db.hostname
  port: 8080
  username: admin
  password: admin
  log:
    path: /var/logs/db
    level: debug
 ```

#### 5. Defines configuration name for Environment variable

Using **env** keyword to define configuration name

```golang
  type Database struct {
    Host     string `env:"DB_HOST"`
    Port     int    `env:"DB_PORT"`
    Username string `default:"admin" env:"DB_USER"`
    Password string `default:"admin" env:"DB_PASSWORD"`
    Log      Log    `env:"DB_LOG_"`
  }
```

Corresponding Environment variables:

```shell
 export DB_HOST=test.db.hostname
 export DB_PORT=8080
 export DB_USER=admin
 export DB_PASSWORD=admin
 export DB_LOG_PATH=/var/logs/db
 export DB_LOG_LEVEL=debug
```

Since the `Log` is a structure and nested in `Database` structure, the tag of `Log`
and tags of its structure members will be combined to be an unique environment variable,
for example: `Path` will be mapped to environment var: `DB_LOG_PATH`.
But if the `Log` has no tag definition, only tags of its structure members will
be used, that means the `Path` will be mapped to `PATH`.

#### 6. Defines configuration name for Command line

Using **cli** keyword to define configuration name:

```golang
  type Database struct {
    Host     string `cli:"host" usage:"database host name"`
    Port     int    `cli:"port" usage:"database port"`
    Username string `cli:"username" default:"admin" usage:"database username"`
    Password string `cli:"password" default:"admin" usage:"database password"`
    Log      Log    `cli:"log" usage:"database log configurations"`
  }
```

For **cli** definition, the string is command line argument, and the **usage**
tag are the command line usage and will be outputted when printing usage.

Corresponding command line:

```shell
  ./main --host test.db.hostname --port 8080 --username admin --password admin --log-path /var/logs/db --log-level debug
```

or

```shell
  ./main --host=test.db.hostname --port=8080 --username=admin --password=admin --log-path=/var/logs/db --log-level=debug
```

#### 7. Defines configuration name as a slice type

Using **separator** to split string as a slice:

```golang
  type Log struct {
    Levels []string `env:"LEVELS" cli:"levels" separator:";" usage:"log levels"`
  }
```

If the separator is not given, its default is **:**, The separator only works on
**env** and **cli** tags.

```golang
  logConfig := Log{}
  // export LEVELS=debug;error;info
  config.ParseEnv(&logConfig)
  // logConfig[0] == debug
  // logConfig[1] == error
  // logConfig[2] == info
```

### II. Parses configurations

#### 1. Parses default values

When default values are defined in tags, calls `config.ParseDefault(interface{})`
to assign them to given structure instance **BEFORE** parsing any other configuration
types:

```golang
  logConfig := Log{}
  config.ParseDefault(&logConfig)
```

>Note: Other parsing functions don't set structure instance with default values
whatever if the configuration value is provided or not

#### 2. Parses from Environment variables

```golang
  dbConfig := Database{}
  config.ParseEnv(&dbConfig)
```

#### 3. Parses from Command line

```golang
  dbConfig := Database{}
  config.ParseCli(&dbConfig)
```

#### 4. Parses from default configuration files

Calls **ParseConfigFile(interface{}, string)** to parse given configuration file:

```golang
  dbConfig := Database{}
  config.ParseConfigFile(&dbConfig, "config.json")
```

If the configuration file is not given, the default configuration files:
**config.json** and **config.yaml** will be located under the same folder with
fixed searching order.

The **config.json** will always be located first, if it doesn't exist, then checks\
**config.yaml**. If all of them are not found, parsing will fail.

```golang
  dbConfig := Database{}
  config.ParseConfigFile(&dbConfig, "")
```

#### 4. Parses from configuration file specified by command line

Calls **ParseConfig(interface{}, string)** to parse the configuration file given
by command line. The second parameter is a command line argument which is used
to specify config file:

```golang
  dbConfig := Database{}
  config.ParseConfig(&dbConfig, "c")
```

Run application like:

```shell
  ./main -c config.json
```

**ParseConfig()** will analyze command line arguments and get configure file:
**config.json** from argument **-c**

### III. Multi-Configurations

You can define all supported configuration tags in a structure and call corresponding
functions in your desired order to parse.

Examples:

```golang
  type Log struct {
    Path   string `cli:"path"   default:"/var/logs"   env:"PATH"   json:"path"   usage:"log path"   yaml:"path"`
    Levels string `cli:"levels" default:"debug;error" env:"LEVELS" json:"levels" usage:"log levels" yaml:"levels"`
  }
  
  type Database struct {
    Host     string `cli:"host" env:"DB_HOST" json:"host" usage:"database host name" yaml:"host"`
    Port     int    `cli:"port" env:"DB_PORT" json:"port" usage:"database port"      yaml:"port"`
    Username string `cli:"username" default:"admin" env:"DB_USER"   json:"user"   usage:"database username" yaml" user"`
    Password string `cli:"password" default:"admin" env:"DB_PASSWD" json:"passwd" usage:"database password" yaml:"passwd"`
    Log      Log    `cli:"log" env:"DB_LOG_" json:"log" usage:"database log configurations" yaml:"log"`
  }
```

Then, you can parse as below:

```golang
dbConfig := Database{}
 
// parse default values
if err := config.ParseDefault(&dbConfig); err != nil {
  // error handling
}

cmd := config.NewCLI("")
if err := cmd.Init(&dbConfig); err != nil {
  // error handling
}

// capture cli flags
args := cmd.Capture(os.Args)

// parse cli flags of application, urfave for example
flagSet.Parse(args)

// parse captured cli flags of config
if err := cmd.Parse(cmd.Args); err != nil {
  // error handling
}

// parse configuration file from command line
err := config.ParseConfig(&dbConfig, "c")
 
// parse default configurations
if err != nil {
  err = config.ParseConfigFile(&dbConfig), "")
}
 
// parse environment variables
if err != nil {
  err = config.ParseEnv(&dbConfig)
}
 
// parse command line
if err != nil {
  err = config.ParseCli(&dbConfig)
}
 
// check if all required configurations are set
...
```

You don't need to call all of them. Just invokes parsing function that your need.

## License

This project is licensed under the Apache License Version 2.0.