config

package module
v0.0.4 Latest Latest
Warning

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

Go to latest
Published: Oct 5, 2018 License: MIT Imports: 13 Imported by: 0

README

Config

I needed a lite configuration library for a personal Go project, and having read Peter Bourgon's treatise on industrial programming I thought I would have an opinionated attempt at it.

The library allows the use of command line flags, environmental variables, and / or JSON encoded text files. Flags and env vars are generated automatically from struct property names and tags. The standard library encoding/json encoder is used for JSON files. JSON sucks for configuration (comments) but I wanted to keep the import graph as small a possible initially. It would be trivial to allow toml (preferred) or yaml (please don't) encoded files.

At a very basic level the rationale was to create a library that would allow an instance of a struct to be passed to it at runtime and layered providers would supply configuration values from the environment.

Assumptions

  • The library trades startup time for ease of use which in a long running API shouldn't cause too many problems for most people, unless you're running in the cloud.
  • Flags are created in their own flagset because flag.ExitOnError is the default and I don't want that. This also means that if you have other flags specified on the command line (perhaps you want to interpret them with another library) then the flag library will stop processing and return an error. I'm assuming that if you have a configuration struct then all of the configuration is in it and you want this library to handle it.
  • This library does not panic in an of itself. However, although care has been taken not to cause a panic from the reflect library I can't guarantee I found them all.

Usage

Most common usage would simply be this:

package main

import (
    "fmt"
    "os"
    
    "github.com/ande980/config"
)

func main() {
    if err := run(); err != nil {
        fmt.Fprintf(os.Stderr, "%v\n", err)
        os.Exit(2)
    }
}

func run() error {
    cfg := &Cfg{}
    if err := config.Parse(cfg); err != nil {
        if err == flag.ErrHelp {
            return nil
        }
        return err    
    }
    // use cfg
}

The library includes two optional interfaces that can be implemented:

  1. Initer just in case you can't, for some reason, create a constructor for your type or have some theoretical requirement to set default values outside of a constructor ... or something.
  2. Validator if you want to use JSON schema or some custom business logic to ensure your configuration is valid.

Using the Validator interface would look something like this:

package main

import (
    "fmt"
    "os"
    
    "github.com/ande980/config"
)

type Cfg struct {
    Addr      string `usage:"Local address to bind to"`
    ProxyAddr string `usage:"Remote proxy address"`
}

func(c *Cfg) Validate() error {
    if c.Addr == "" {
        c.Addr = ":0"
    }

    if c.ProxyAddr == "" {
        return fmt.Errorf("validate: proxy address must be provided")
    }
    return nil
}

func main() {
    if err := run(); err != nil {
        fmt.Fprintf(os.Stderr, "%v\n", err)
        os.Exit(2)
    }
}

func run() error {
    cfg := &Cfg{}
    if err := config.Parse(cfg); err != nil {
        if err == flag.ErrHelp {
            return nil
        }
        return err    
    }
    // use cfg
}

Additional Providers

Both a toml, and a yaml, provider have been created but neither are registered by default. This is to keep the import graph small and restricted to (almost) only standard libraries. Only one file provider can be registered at once, so registering the toml provider for example will unregister the json or yaml provider if either are registered.

TODO

  • Either add in panic recovery or change reflection panics to errors
  • Add toml support
  • Add yaml support
  • More canonical flag names
  • Tests for toml and yaml providers
  • Generate default usage verbiage if none is provided

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrHelp is returned when the -h or --help flags are used.
	ErrHelp = errors.New("help requested")
	// ErrVersion is returned when the -v or --version flags are used.
	ErrVersion = errors.New("version requested")
)

Functions

func Parse

func Parse(i interface{}) (err error)

Parse acccepts a variadic number of config providers and returns an error. If a single provider returns an error then it will be return even if all other providers functioned correctly.

Types

type Initer

type Initer interface {
	Init() error
}

Initer is an optional interface that configuration structs can implement with a single method that will be called before any values are scanned into the struct. This is useful as a constructor without having a lot of initialization code at the call site.

type Provider

type Provider interface {
	Parse(interface{}) error
}

Provider describes a single function to take an arbitrary struct and infill.

type ProviderFunc

type ProviderFunc func(interface{}) error

ProviderFunc is a convenience type a la http.HandlerFunc.

func (ProviderFunc) Parse

func (p ProviderFunc) Parse(i interface{}) error

Parse allows ProviderFunc to satisfy the Provider interface

type Validator

type Validator interface {
	Validate() error
}

Validator is an optional interface that configuration structs can implement with a single method that will be called after scanning has completed. This is useful as a validation phase.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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