cleanenv

package module
v0.0.0-...-c3a496f Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2022 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package cleanenv gives you a single tool to read application configuration from several sources with ease.

Features

- read from several file formats (YAML, JSON, TOML, ENV, EDN) and parse into the internal structure;

- read environment variables into the internal structure;

- output environment variable list with descriptions into help output;

- custom variable readers (e.g. if you want to read from remote config server, etc).

Usage

You can just prepare the config structure and fill it from the config file and environment variables.

type Config struct {
	Port string `yaml:"port" env:"PORT" env-default:"8080"`
	Host string `yaml:"host" env:"HOST" env-default:"localhost"`
}

var cfg Config

ReadConfig("config.yml", &cfg)

Help output

You can list all of your environment variables by means of help output:

type ConfigServer struct {
	Port     string `env:"PORT" env-description:"server port"`
	Host     string `env:"HOST" env-description:"server host"`
}

var cfg ConfigRemote

help, err := cleanenv.GetDescription(&cfg, nil)
if err != nil {
	...
}

// setup help output
f := flag.NewFlagSet("Example app", 1)
fu := f.Usage
f.Usage = func() {
	fu()
	envHelp, _ := cleanenv.GetDescription(&cfg, nil)
	fmt.Fprintln(f.Output())
	fmt.Fprintln(f.Output(), envHelp)
}

f.Parse(os.Args[1:])

Then run go run main.go -h and the output will include:

Environment variables:
	PORT  server port
	HOST  server host

For more detailed information check examples and example tests.

Example (Setter)

Example_setter uses type with a custom setter to parse environment variable data

package main

import (
	"fmt"
	"os"

	"github.com/ilyakaznacheev/cleanenv"
)

// MyField is an example type with a custom setter
type MyField string

func (f *MyField) SetValue(s string) error {
	if s == "" {
		return fmt.Errorf("field value can't be empty")
	}
	*f = MyField("my field is: " + s)
	return nil
}

func (f MyField) String() string {
	return string(f)
}

func main() {
	type config struct {
		Default string  `env:"ONE"`
		Custom  MyField `env:"TWO"`
	}

	var cfg config

	os.Setenv("ONE", "test1")
	os.Setenv("TWO", "test2")

	cleanenv.ReadEnv(&cfg)
	fmt.Printf("%+v\n", cfg)
}
Output:

{Default:test1 Custom:my field is: test2}
Example (Updater)

Example_updater uses a type with a custom updater

package main

import (
	"fmt"
	"os"

	"github.com/ilyakaznacheev/cleanenv"
)

// ConfigUpdate is a type with a custom updater
type ConfigUpdate struct {
	Default string `env:"DEFAULT"`
	Custom  string
}

func (c *ConfigUpdate) Update() error {
	c.Custom = "custom"
	return nil
}

func main() {
	var cfg ConfigUpdate

	os.Setenv("DEFAULT", "default")

	cleanenv.ReadEnv(&cfg)
	fmt.Printf("%+v\n", cfg)
}
Output:

{Default:default Custom:custom}

Index

Examples

Constants

View Source
const (
	// Name of the environment variable or a list of names
	TagEnv = "env"

	// Value parsing layout (for types like time.Time)
	TagEnvLayout = "env-layout"

	// Default value
	TagEnvDefault = "env-default"

	// Custom list and map separator
	TagEnvSeparator = "env-separator"

	// Environment variable description
	TagEnvDescription = "env-description"

	// Flag to mark a field as updatable
	TagEnvUpd = "env-upd"

	// Flag to mark a field as required
	TagEnvRequired = "env-required"

	// Flag to specify prefix for structure fields
	TagEnvPrefix = "env-prefix"
)

Supported tags

View Source
const (
	// DefaultSeparator is a default list and map separator character
	DefaultSeparator = ","
)

Variables

This section is empty.

Functions

func FUsage

func FUsage(w io.Writer, cfg interface{}, headerText *string, usageFuncs ...func()) func()

FUsage prints configuration help into the custom output. Other usage instructions can be wrapped in and executed before this usage function

func GetDescription

func GetDescription(cfg interface{}, headerText *string) (string, error)

GetDescription returns a description of environment variables. You can provide a custom header text.

Example

ExampleGetDescription builds a description text from structure tags

package main

import (
	"fmt"

	"github.com/ilyakaznacheev/cleanenv"
)

func main() {
	type config struct {
		One   int64   `env:"ONE" env-description:"first parameter"`
		Two   float64 `env:"TWO" env-description:"second parameter"`
		Three string  `env:"THREE" env-description:"third parameter"`
	}

	var cfg config

	text, err := cleanenv.GetDescription(&cfg, nil)
	if err != nil {
		panic(err)
	}

	fmt.Println(text)
}
Output:

Environment variables:
  ONE int64
    	first parameter
  TWO float64
    	second parameter
  THREE string
    	third parameter
Example (CustomHeaderText)

ExampleGetDescription_customHeaderText builds a description text from structure tags with custom header string

package main

import (
	"fmt"

	"github.com/ilyakaznacheev/cleanenv"
)

func main() {
	type config struct {
		One   int64   `env:"ONE" env-description:"first parameter"`
		Two   float64 `env:"TWO" env-description:"second parameter"`
		Three string  `env:"THREE" env-description:"third parameter"`
	}

	var cfg config

	header := "Custom header text:"

	text, err := cleanenv.GetDescription(&cfg, &header)
	if err != nil {
		panic(err)
	}

	fmt.Println(text)
}
Output:

Custom header text:
  ONE int64
    	first parameter
  TWO float64
    	second parameter
  THREE string
    	third parameter
Example (Defaults)

ExampleGetDescription_defaults builds a description text from structure tags with description of default values

package main

import (
	"fmt"

	"github.com/ilyakaznacheev/cleanenv"
)

func main() {
	type config struct {
		One   int64   `env:"ONE" env-description:"first parameter" env-default:"1"`
		Two   float64 `env:"TWO" env-description:"second parameter" env-default:"2.2"`
		Three string  `env:"THREE" env-description:"third parameter" env-default:"test"`
	}

	var cfg config

	text, err := cleanenv.GetDescription(&cfg, nil)
	if err != nil {
		panic(err)
	}

	fmt.Println(text)
}
Output:

Environment variables:
  ONE int64
    	first parameter (default "1")
  TWO float64
    	second parameter (default "2.2")
  THREE string
    	third parameter (default "test")
Example (VariableList)

ExampleGetDescription_variableList builds a description text from structure tags with description of alternative variables

package main

import (
	"fmt"

	"github.com/ilyakaznacheev/cleanenv"
)

func main() {
	type config struct {
		FirstVar int64 `env:"ONE,TWO,THREE" env-description:"first found parameter"`
	}

	var cfg config

	text, err := cleanenv.GetDescription(&cfg, nil)
	if err != nil {
		panic(err)
	}

	fmt.Println(text)
}
Output:

Environment variables:
  ONE int64
    	first found parameter
  TWO int64 (alternative to ONE)
    	first found parameter
  THREE int64 (alternative to ONE)
    	first found parameter

func ReadConfig

func ReadConfig(path string, cfg interface{}) error

ReadConfig reads configuration file and parses it depending on tags in structure provided. Then it reads and parses

Example:

type ConfigDatabase struct {
	Port     string `yaml:"port" env:"PORT" env-default:"5432"`
	Host     string `yaml:"host" env:"HOST" env-default:"localhost"`
	Name     string `yaml:"name" env:"NAME" env-default:"postgres"`
	User     string `yaml:"user" env:"USER" env-default:"user"`
	Password string `yaml:"password" env:"PASSWORD"`
}

var cfg ConfigDatabase

err := cleanenv.ReadConfig("config.yml", &cfg)
if err != nil {
    ...
}

func ReadEnv

func ReadEnv(cfg interface{}) error

ReadEnv reads environment variables into the structure.

Example

ExampleReadEnv reads environment variables or default values into the structure

package main

import (
	"fmt"
	"os"

	"github.com/ilyakaznacheev/cleanenv"
)

func main() {
	type config struct {
		Port     string `env:"PORT" env-default:"5432"`
		Host     string `env:"HOST" env-default:"localhost"`
		Name     string `env:"NAME" env-default:"postgres"`
		User     string `env:"USER" env-default:"user"`
		Password string `env:"PASSWORD"`
	}

	var cfg config

	os.Setenv("PORT", "5050")
	os.Setenv("NAME", "redis")
	os.Setenv("USER", "tester")
	os.Setenv("PASSWORD", "*****")

	cleanenv.ReadEnv(&cfg)
	fmt.Printf("%+v\n", cfg)

}
Output:

{Port:5050 Host:localhost Name:redis User:tester Password:*****}

func UpdateEnv

func UpdateEnv(cfg interface{}) error

UpdateEnv rereads (updates) environment variables in the structure.

Example

ExampleUpdateEnv updates variables in the configuration structure. Only variables with `env-upd:""` tag will be updated

package main

import (
	"fmt"
	"os"

	"github.com/ilyakaznacheev/cleanenv"
)

func main() {
	type config struct {
		One int64 `env:"ONE"`
		Two int64 `env:"TWO" env-upd:""`
	}

	var cfg config

	// set environment variables
	os.Setenv("ONE", "1")
	os.Setenv("TWO", "2")

	// read environment variables into the structure
	cleanenv.ReadEnv(&cfg)
	fmt.Printf("%+v\n", cfg)

	// update environment variables
	os.Setenv("ONE", "11")
	os.Setenv("TWO", "22")

	// update only updatable environment variables in the structure
	cleanenv.UpdateEnv(&cfg)
	fmt.Printf("%+v\n", cfg)

}
Output:

{One:1 Two:2}
{One:1 Two:22}

func Usage

func Usage(cfg interface{}, headerText *string, usageFuncs ...func()) func()

Usage returns a configuration usage help. Other usage instructions can be wrapped in and executed before this usage function. The default output is STDERR.

Example
package main

import (
	"flag"
	"os"

	"github.com/ilyakaznacheev/cleanenv"
)

func main() {
	os.Stderr = os.Stdout //replace STDERR with STDOUT for test

	type config struct {
		One   int64   `env:"ONE" env-description:"first parameter"`
		Two   float64 `env:"TWO" env-description:"second parameter"`
		Three string  `env:"THREE" env-description:"third parameter"`
	}

	// setup flags
	fset := flag.NewFlagSet("Example", flag.ContinueOnError)

	fset.String("p", "8080", "service port")
	fset.String("h", "localhost", "service host")

	var cfg config
	customHeader := "My sweet variables:"

	// get config usage with wrapped flag usage and custom header string
	u := cleanenv.Usage(&cfg, &customHeader, fset.Usage)

	// print usage to STDERR
	u()

}
Output:

Usage of Example:
  -h string
    	service host (default "localhost")
  -p string
    	service port (default "8080")

My sweet variables:
  ONE int64
    	first parameter
  TWO float64
    	second parameter
  THREE string
    	third parameter

Types

type Setter

type Setter interface {
	SetValue(string) error
}

Setter is an interface for a custom value setter.

To implement a custom value setter you need to add a SetValue function to your type that will receive a string raw value:

type MyField string

func (f *MyField) SetValue(s string) error {
	if s == "" {
		return fmt.Errorf("field value can't be empty")
	}
	*f = MyField("my field is: " + s)
	return nil
}

type Updater

type Updater interface {
	Update() error
}

Updater gives an ability to implement custom update function for a field or a whole structure

Directories

Path Synopsis
example

Jump to

Keyboard shortcuts

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