fig

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 23, 2020 License: Apache-2.0 Imports: 12 Imported by: 78

README

fig

godoc build status semver tag go report card coverage status license

fig

fig loads your config file into a struct with additional support for marking fields as required and setting defaults.

Why fig?

  • Define your config, validations and defaults all in a single struct
  • Full support fortime.Time & time.Duration
  • Only 3 external dependencies
  • Tiny API
  • Decoders for .yaml, .json and .toml files

Getting Started

$ go get -d github.com/kkyr/fig

Define your config file:

# config.yaml

build: "2020-01-09T12:30:00Z"

server:
    ports:
      - 8080
    cleanup: 1h

logger:
    level: "warn"
    trace: true

Define your struct along with any required and default fields:

package main

import (
  "fmt"

  "github.com/kkyr/fig"
)

type Config struct {
  Build  time.Time `fig:"build,required"`
  Server struct {
    Host    string        `fig:"host,default=127.0.0.1"`
    Ports   []int         `fig:"ports,default=[80,443]"`
    Cleanup time.Duration `fig:"cleanup,default=30m"`
  }
  Logger struct {
    Level string `fig:"level,default=info"`
    Trace bool   `fig:"trace"`
  }
}

func main() {
  var cfg Config
  err := fig.Load(&cfg)
  // handle your err
  
  fmt.Printf("%+v\n", cfg)
  // Output: {Build:2019-12-25 00:00:00 +0000 UTC Server:{Host:127.0.0.1 Ports:[8080] Cleanup:1h0m0s} Logger:{Level:warn Trace:true}}
}

If a field is not loaded from the config file and is marked as required then an error is returned. If a default value is defined instead then that value is used to populate the field.

Fig searches for a file named config.yaml in the directory it is run from. Change the lookup behaviour by passing additional parameters to Load():

fig.Load(&cfg,
  fig.File("settings.json"),
  fig.Dirs(".", "/etc/myapp", "/home/user/myapp"),
) // searches for ./settings.json, /etc/myapp/settings.json, /home/user/myapp/settings.json

Usage

See godoc for detailed usage documentation.

Contributing

PRs are welcome! Please ensure you add relevant tests & documentation prior to making one.

Documentation

Overview

Package fig loads configuration files into Go structs with extra juice for validating fields and setting defaults.

Config files may be defined in in yaml, json or toml format.

Example

Define your configuration file in the root of your project:

# config.yaml

build: "2020-01-09T12:30:00Z"

server:
  ports:
    - 8080
  cleanup: 1h

logger:
  level: "warn"
  trace: true

Define your struct and load it:

package main

import (
  "fmt"

  "github.com/kkyr/fig"
)

 type Config struct {
   Build  time.Time `fig:"build,required"`
   Server struct {
     Host    string        `fig:"host,default=127.0.0.1"`
     Ports   []int         `fig:"ports,default=[80,443]"`
     Cleanup time.Duration `fig:"cleanup,default=30m"`
   }
   Logger struct {
     Level string `fig:"level,default=info"`
     Trace bool   `fig:"trace"`
   }
 }

func main() {
  var cfg Config
  _ = fig.Load(&cfg)

  fmt.Printf("%+v\n", cfg)
  // Output: {Build:2019-12-25 00:00:00 +0000 UTC Server:{Host:127.0.0.1 Ports:[8080] Cleanup:1h0m0s} Logger:{Level:warn Trace:true}}
}

By default fig searches for a file named `config.yaml` in the directory it is run from. It can be configured to look elsewhere.

Configuration

Pass options as additional parameters to `Load()` to configure fig's behaviour.

File

Change the file and directories fig searches in with `File()`.

fig.Load(&cfg,
  fig.File("settings.json"),
  fig.Dirs(".", "home/user/myapp", "/opt/myapp"),
)

Fig searches for the file in dirs sequentially and uses the first matching file.

The decoder (yaml/json/toml) used is picked based on the file's extension.

Tag

The name of the struct tag that fig uses can be changed with `Tag()`.

type Config struct {
  Host  string `config:"host,required"`
  Level string `config:"level,default=info"`
}

var cfg Config
fig.Load(&cfg, fig.Tag("config"))

By default fig uses the tag name `fig`.

Time

Change the layout fig uses to parse times using `TimeLayout()`.

type Config struct {
  Date time.Time `fig:"date,default=12-25-2019"`
}

var cfg Config
fig.Load(&cfg, fig.TimeLayout("01-02-2006"))

fmt.Printf("%+v", cfg)
// Output: {Date:2019-12-25 00:00:00 +0000 UTC}

By default fig parses time using the `RFC.3339` layout (`2006-01-02T15:04:05Z07:00`).

Validation

Fields can be validated by adding an appropriate key to the field tag. A maximum of one validation may be added to each field.

Required

A required key in the field tag causes fig to check if the field has been set after it's loaded from the config file. Required fields that are not set are returned as an error.

type Config struct {
  Host string `fig:"host,required"` // or `fig:",required"
}

Fig uses the following properties to check if a field is set:

basic types:           != to its zero value ("" for str, 0 for int, etc.)
slices, arrays:        len() > 0
pointers*, interfaces: != nil
structs:               always true (use a struct pointer to check for struct presence)
time.Time:             !time.IsZero()
time.Duration:         != 0

*non-nil pointers to non-struct types (except time.Time) are de-referenced and then checked

See example below to help understand:

type Config struct {
  A string    `fig:",required"`
  B *string   `fig:",required"`
  C int       `fig:",required"`
  D *int      `fig:",required"`
  E []float32 `fig:",required"`
  F struct{}  `fig:",required"`
  G *struct{} `fig:",required"`
  H struct {
    I interface{} `fig:",required"`
    J interface{} `fig:",required"`
  } `fig:",required"`
  K *[]bool    `fig:",required"`
  L []uint     `fig:",required"`
  M *time.Time `fig:",required"`
}

var cfg Config

// simulate loading of config file
b := ""
cfg.B = &b
cfg.H.I = 5.5
cfg.K = &[]bool{}
cfg.L = []uint{5}
m := time.Time{}
cfg.M = &m

err := fig.Load(&cfg)
fmt.Print(err)
// A: required, B: required, C: required, D: required, E: required, G: required, H.J: required, K: required, M: required

Default

A default key in the field tag causes fig to fill the field with the value specified only if the field is not set. It must be in the format default=value.

Fig attempts to parse the value based on the field's type. If parsing fails then an error is returned.

type Config struct {
  Port int `fig:"port,default=8000"` // or `fig:",default=8000"
}

A default value can be set for the following types:

all basic types*
time.Time
time.Duration
slices (of above types)

*complex not supported

Slice defaults must be enclosed in square brackets and successive values separated by a comma:

type Config struct {
  Durations []time.Duration `fig:",default=[30m,1h,90m,2h]"
}

Errors

A wrapped error `ErrFileNotFound` is returned when fig is not able to find a config file to load. This can be useful for instance to fallback to a different configuration loading mechanism.

var cfg Config
err := fig.Load(&cfg)
if errors.Is(err, fig.ErrFileNotFound) {
  // load config from elsewhere
}

Index

Constants

View Source
const (
	// DefaultFilename is the default filename of the config file that fig looks for.
	DefaultFilename = "config.yaml"
	// DefaultDir is the default directory that fig searches in for the config file.
	DefaultDir = "."
	// DefaultTag is the default struct tag name that fig uses for field metadata.
	DefaultTag = "fig"
	// DefaultTimeLayout is the default time layout that fig uses to parse times.
	DefaultTimeLayout = time.RFC3339
)

Variables

View Source
var ErrFileNotFound = fmt.Errorf("file not found")

ErrFileNotFound is returned as a wrapped error by `Load` when the config file is not found in the given search dirs.

Functions

func Load

func Load(cfg interface{}, options ...Option) error

Load reads a configuration file and loads it into the given struct. The parameter `cfg` must be a pointer to a struct.

By default fig looks for a file `config.yaml` in the current directory and uses the struct field tag `fig` for matching field names and validation. To alter this behaviour pass additional parameters as options.

A field can be marked as required by adding a `required` key in the field's struct tag. If a required field is not set by the configuration file an error is returned.

type config struct {
  Env string `fig:"env,required"` // or `fig:",required"`
}

A field can be configured with a default value by adding a `default=value` in the field's struct tag. If a field is not set by the configuration file then the default value is set.

type config struct {
  Level string `fig:"level,default=info"` // or `fig:",default=info"`
}

A single field may not be marked as both `required` and `default`.

Types

type Option

type Option func(f *fig)

Option configures how fig loads the configuration.

func Dirs

func Dirs(dirs ...string) Option

Dirs returns an option that configures the directories that fig searches to find the configuration file.

Directories are searched sequentially and the first one with a matching config file is used.

This is useful when you don't know where exactly your configuration will be during run-time:

fig.Load(&cfg, fig.Dirs(".", "/etc/myapp", "/home/user/myapp"))

If this option is not used then fig looks in the directory it is run from.

func File

func File(name string) Option

File returns an option that configures the filename that fig looks for to provide the config values.

The name must include the extension of the file. Supported file types are `yaml`, `yml`, `json` and `toml`.

fig.Load(&cfg, fig.File("config.toml"))

If this option is not used then fig looks for a file with name `config.yaml`.

func Tag

func Tag(tag string) Option

Tag returns an option that configures the tag that fig uses when searching for struct tags in fields.

fig.Load(&cfg, fig.Tag("config"))

If this option is not used then fig uses the tag `fig`.

func TimeLayout

func TimeLayout(layout string) Option

TimeLayout returns an option that conmfigures the time layout that fig uses when parsing a time in a config file or in the default tag for time.Time fields.

fig.Load(&cfg, fig.TimeLayout("2006-01-02"))

If this option is not used then fig parses times using `time.RFC3339` layout.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL