Skip to content
Cascade and integrate config struct from various setting files and environment.
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.circleci
fixtures
.gitignore
LICENSE
README.md
go.mod
go.sum
option.go
twist.go
twist_test.go

README.md

CircleCI GoDoc

twist

Cascade and integrate config struct from various setting files, envrironments, and command-line arguments

Supporting configuration types:

  • toml file
  • yaml file
  • json file
  • ini file
  • environment variables
  • command-line arguments
  • default values

Installation

$ go get -u github.com/ysugimoto/twist

Or install via package manager which you're using like dep, go mod, etc...

Getting Started

Define your configuration strcut and call Mix() function with cascading configurations.

Here is toml file example:

# /path/to/setting.toml

host = "localhost"
port = 8000

[service]
name = "My Service"
description = "This is my favorite service"

Then this package can use as following:

package main

import (
  "log"

  "github.com/ysugimoto/twist"
)

type MyConfig struct {
  Host string `toml:"host"`
  Port int    `toml:"port"`

  Service struct{
    Name        string `toml:"name"`
    Description string `toml:"description"`
  } `toml:"service"` // <- need this
}

func main() {
  var config MyConfig
  if err := twist.Mix(&config, twist.WithToml("/path/to/setting.toml")); err != nil {
    log.Fatal(err)
  }
  log.Println(config.Host)                // => localhost
  log.Println(config.Port)                // => 8000
  log.Println(config.Service.Name)        // => My Service
  log.Println(config.Service.Description) // => This is my favorite service
}

Merging configuration from various kind of files and defaults

This package can support kinds of config files (eg. yaml and json and env). To merge from some configurations, call Mix() with some of WithXXX options and make sure put tag in your struct field which you want to assign:

package main

import (
  "log"
  "os"

  "github.com/ysugimoto/twist"
)

type MyConfig struct {
  Host string `toml:"host"` // will be used from toml file
  Port int    `yaml:"port"` // will be used from yaml file

  Service struct{
    Name        string `default:"My Service"`          // set as default
    Description string `default:"My Favorite Service"` // set as default
  } `toml:"service" yaml:"service"` // <- need if you want to assign from multiple files

  Secret string `env:"SECRET"`      // will be used from envrironment variable
}

func main() {
  var config MyConfig
  if err := twist.Mix(
    &config,
    twist.WithToml("/path/to/setting.toml"),
    twist.WithToml("/path/to/setting.yaml"),
    twist.WithEnv(),
  ); err != nil {
    log.Fatal(err)
  }
  log.Println(config.Host)                // => host in toml file
  log.Println(config.Port)                // => port in yaml file 
  log.Println(config.Service.Name)        // => My Service as default
  log.Println(config.Service.Description) // => My Favorite Service as default
  log.Println(config.Secret)              // => value of os.Getenv("SECRET")
}

Of course you'll confuse when cascading from different file types, so usually you can use from same file of partial configurations and integrate it, and secret values (like accessToken, etc) should be assigned from envrironment variables:

# /path/to/server.json
{
  "host": "localhost",
  "port": 8000
}

# /path/to/service.json
{
  "service": {
    "name": "My Service",
    "description": "My Favorite Service"
  }
}

Then cascading is:

package main

import (
  "log"

  "github.com/ysugimoto/twist"
)

type MyConfig struct {
  Host string `json:"host"`
  Port int    `json:"port"`

  Service struct{
    Name        string `json:"name"`
    Description string `json:"description"`
  } `json:"service"`

  Secret  string `env:"SECRET"`    // will be used from envrironment variable
  Verbose string `cli:"v,verbose"` // will be used from command-line arguments
}

func main() {
  var config MyConfig
  if err := twist.Mix(
    &config,
    twist.WithToml("/path/to/server.json"),  // will set only Host and Port
    twist.WithToml("/path/to/service.json"), // will set only Service.Name and Service.Description
    twist.WithEnv(),
    twist.WithCli(os.Args[1:]),
  ); err != nil {
    log.Fatal(err)
  }
  log.Println(config.Host)                // => host value in server.json
  log.Println(config.Port)                // => port value in server.json
  log.Println(config.Service.Name)        // => name value in service.json
  log.Println(config.Service.Description) // => description value in service.json
  log.Println(config.Secret)              // => value of os.Getenv("SECRET")
}

Correspond Struct Tags

type Config struct {
  TomlValue    string `toml:"value"`     // for toml mapping
  YamlValue    string `yaml:"value"`     // for yaml mapping
  JsonValue    string `json:"value"`     // for json mapping
  IniValue     string `ini:"value"`      // for ini mapping
  EnvValue     string `env:"ENV_NAME"`   // for env mapping
  CliValue     string `cli:"short,long"` // for cli mapping
  DefaultValue string `default:"value"`  // set as default value
}

toml, yaml, json and ini are used following packages:

  • github.com/BurntSushi/toml
  • github.com/go-yaml/yaml
  • github.com/go-ini/ini
  • encoding/json

env and cli is package defined.

Author

Yoshiaki Sugimoto

License

MIT

PR is welcome :-)

You can’t perform that action at this time.