config

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Oct 20, 2019 License: Apache-2.0 Imports: 12 Imported by: 2

README

config - Go configurations using structs

codecov Build Status GoDoc

Quick Start

package main

import (
    "context"
    "fmt"

    "github.com/stackopsd/config"
)

// Define a struct that includes all the configuration values you need.
type Settings struct {
    DBUser string
    DBPassword string
    UseTLS bool
}

func main() {

    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()

    // Create an instance of that configuration struct.
    var settings Settings

    loader := config.NewFileLoader("/path/to/some/config.json_or_yaml")
    mapper := &config.StructMapper{}

    if err := mapper.Map(ctx, loader, &settings); err != nil {
        panic(err)
    }

    fmt.Println(settings.DBUser, settings.DBPassword, settings.UseTLS)
}
# An example YAML file that would work with the above.
db_user: "myUser"
db_password: "${DB_PWD}" # ENV interpolation is done automatically
use_tls: true

Configuration Value Names

The expectation is that all structs being loaded will conform to the Effective Go suggestion of using MixedCase for field names. The system will automatically convert field names into a human readable snake_case format. The system attempts to chunk names in an intuitive way. Here are some examples conversions:

-   Value => value
-   SomeValue => some_value
-   DNSResolver => dns_resolver
-   HTTPServerAddress => http_server_address
-   HTTP2Enabled => http2_enabled
-   HTTPV1Enabled => httpv1_enabled
-   Http2ServerAddress => http2_server_address

Generally, the chosen name should be easily guessable. The actual algorithm is based on several aspects of a string. Word detection is driven by the use of upper, lower, and digit characters. The following table defines the word selection:

  • Given l=lower case, U=upper case, 0=digit
  • UU => continue word
  • lU => complete word ending in l and start word beginning with U
  • Ul =>
    • if word has more than one letter then
      • complete word ending before U and start new word beginning with Ul
    • else
      • continue word
  • ll => continue word
  • l0 => continue word
  • U0 => continue word
  • 00 => continue word
  • 0l => continue word
  • 0U => complete word ending in 0 and start word beginning with U

Required VS Optional Settings

Everything is required by default. You must use pointer types anywhere a value is optional. For example:

type Settings struct {
    Required string
    Optional *string
}

Using a pointer type causes the system to attempt to look up the value before setting it. If no value is found for the setting then the pointer is left as nil. If there is a value provided from the loader then the pointer value will be set to a non-zero value.

Nested Structs

Structs may be nested either by embedding or by having a struct field in the main configuration struct:

type DatabaseSettings struct {
    User string
    Password string
}

type RuntimeSettings struct {
    Address string
    Port int
}

type Settings struct {
    RuntimeSettings
    DB DatabaseSettings
}

Embedding works as it would in Go and exposes all of the attributes contained in the embedded struct through the parent. This means that address and port would not need prefixes to load. In contrast, defining struct fields will cause any loader to search for a subtree. For example, here is a YAML file that satisfies this configuration:

address: localhost
port: 8080
db:
    user: "myUser"
    password: "${DB_PWD}"
Recursive Definitions

This library supports recursive configuration definitions by using a pointer type. For example:

type Settings struct {
    Value string
    More *Settings
}

This follows the exact same semantics as other optional fields.

License

Copyright 2019 Kevin Conway

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CanonicalName

func CanonicalName(name string) string

CanonicalName converts a given field name into the canonical search path that will be used to look up a value in a Loader. The process of formatting a name is intended to match expectations of where a person might place an underscore if converting a word from CamelCase to snake_case. All canonical names are snake_case in order to maximize the human readability of the names in a configuration file. The following are example names and their conversions:

Value => value
SomeValue => some_value
DNSResolver => dns_resolver
HTTPServerAddress => http_server_address
HTTP2Enabled => http2_enabled
HTTPV1Enabled => httpv1_enabled
Http2ServerAddress => http2_server_address

The intended use case is converting Go struct field names into a friendly format. The expectation is that the field names are written using CamelCase in accordance with https://golang.org/doc/effective_go.html#mixed-caps.

The actual algorithm is based on several aspects of a string. Word detection is driven by the use of upper, lower, and digit characters. The following table defines the word selection:

Given l=lower case, U=upper case, 0=digit
UU => continue word
lU => complete word ending in l and start word beginning with U
Ul => if word has more than one letter then
		complete word ending before U and start new word beginning with Ul
	  else
		continue word
ll => continue word
l0 => continue word
U0 => continue word
00 => continue word
0l => continue word
0U => complete word ending in 0 and start word beginning with U

Types

type Loader

type Loader interface {
	// Load accepts a hierarchical path to a configuration value and returns the
	// raw form. The output must be interpreted by the caller in some way to
	// make it meaningful. The boolean value indicates whether or not the value
	// was found in the loader or if some default/nil value is returned.
	// Additionally, the error value can be non-nil if there was an exception
	// fetching data.
	Load(ctx context.Context, path ...string) (interface{}, bool, error)
}

Loader is any data source from which configuration values might be drawn.

type MapLoader

type MapLoader struct {
	Map map[string]interface{}
}

MapLoader is a Loader implementation backed by a static, in-memory map of arbitrary values. This may be re-used anywhere a source is static and can reasonably be converted into a tree structure of map[string]interface{}.

func NewFileLoader

func NewFileLoader(path string) (*MapLoader, error)

NewFileLoader reads in the given file and parsing it with multiple encodings to find one that works.

func NewJSONLoader

func NewJSONLoader(b []byte) (*MapLoader, error)

NewJSONLoader generates a config source from a JSON string.

func NewMapLoader

func NewMapLoader(m map[string]interface{}) *MapLoader

NewMapLoader is the recommended way to create a MapSource instance. While they can be created with any map[string]interface{}, this constructor ensures that all keys of the map have unicode normalization applied.

func NewYAMLLoader

func NewYAMLLoader(b []byte) (*MapLoader, error)

NewYAMLLoader generates a config source from a YAML string.

func (*MapLoader) Load

func (s *MapLoader) Load(_ context.Context, path ...string) (interface{}, bool, error)

Load traverses a configuration map until it finds the requested element or reaches a dead end.

type Mapper

type Mapper interface {
	Map(ctx context.Context, src Loader, dst interface{}) error
}

Mapper uses a Source to populate an arbitrary configuration struct.

type PrefixedLoader

type PrefixedLoader struct {
	Loader Loader
	Prefix []string
}

PrefixedLoader is a wrapper for other Loader implementaions that adds a path element to the front of every lookup.

func (*PrefixedLoader) Load

func (s *PrefixedLoader) Load(ctx context.Context, path ...string) (interface{}, bool, error)

Get a value with a prefixed path.

type Renderer

type Renderer interface {
	Render(ctx context.Context, inst interface{}) (string, error)
}

Renderer computes a string representation of a struct as it would appear in some target source. For example, implementations may present views such as JSON or YAML.

type StructMapper

type StructMapper struct{}

StructMapper implements Mapper for struct inputs.

func (*StructMapper) Map

func (m *StructMapper) Map(ctx context.Context, src Loader, dst interface{}) error

Map attempts to populate the destination struct with values from the Loader.

Directories

Path Synopsis
cmd
genconvert command

Jump to

Keyboard shortcuts

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