Home

Awesome

yamagiconf

<a href="https://pkg.go.dev/github.com/romshark/yamagiconf"> <img src="https://godoc.org/github.com/romshark/yamagiconf?status.svg" alt="GoDoc"> </a> <a href="https://goreportcard.com/report/github.com/romshark/yamagiconf"> <img src="https://goreportcard.com/badge/github.com/romshark/yamagiconf" alt="GoReportCard"> </a> <a href='https://coveralls.io/github/romshark/yamagiconf?branch=main'> <img src='https://coveralls.io/repos/github/romshark/yamagiconf/badge.svg?branch=main' alt='Coverage Status' /> </a>

yamagiconf

The heavily opinionated YAML Magic Configuration framework for Go keeps your configs simple and consistent by being more restrictive than your regular YAML parser 🚷 allowing only a subset of YAML and enforcing some restrictions to the target Go type.

If you hate YAML, and you're afraid of YAML documents from hell, and you can't stand complex, unexplorable and unintuitive configurations then yamagiconf is for you!

🪄 It's magic because it uses reflect to find recursively all values of types that implement interface { Validate() error } and calls them reporting an error annotated with line and column in the YAML file if necessary.

(anti-)Features

Example

https://go.dev/play/p/PjV0aG7uIUH

list:
  - foo: valid
    bar: valid
  - foo: valid
    bar: valid
map:
  valid: valid
secret: 'this will be overwritten from env var SECRET'
required: 'this must not be empty'
package main

import (
	"fmt"

	"github.com/romshark/yamagiconf"
)

type Config struct {
	List []Struct                            `yaml:"list"`
	Map  map[ValidatedString]ValidatedString `yaml:"map"`

	// Secret will be overwritten if env var SECRET is set.
	Secret string `yaml:"secret" env:"SECRET"`

	// See https://github.com/go-playground/validator
	// for all available validation tags
	Required string `yaml:"required" validate:"required"`
}

type Struct struct {
	Foo string          `yaml:"foo"`
	Bar ValidatedString `yaml:"bar"`
}

// Validate will automatically be called by yamagiconf
func (v *Struct) Validate() error {
	if v.Foo == "invalid" {
		return fmt.Errorf("invalid foo")
	}
	if v.Bar == "invalid" {
		return fmt.Errorf("invalid bar")
	}
	return nil
}

type ValidatedString string

// Validate will automatically be called by yamagiconf
func (v ValidatedString) Validate() error {
	if v == "invalid" {
		return fmt.Errorf("string is invalid")
	}
	return nil
}

func main() {
	var c Config
	if err := yamagiconf.LoadFile("./config.yaml", &c); err != nil {
		fmt.Println("Whoops, something is wrong with your config!", err)
	}
	fmt.Printf("%#v\n", c)
}

FAQ

Why are empty array items forbidden?

Consider the following YAML array:

array:
  - 
  - ''
  - ""
  - x

Even though this YAML array works as expect with a Go array: [4]string{"", "", "", "x"}, parsing the same YAML into a Go slice will result in the empty item being ommited: []string{"", "", "x"} which is counterintuitive. Therefore, yamagiconf forbids empty array items in general to keep behavior consistent and intuitive independent of the Go target type.