config

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Dec 16, 2018 License: GPL-3.0 Imports: 15 Imported by: 15

README

.. BADGES:

.. API documentation:
.. image:: https://godoc.org/github.com/sahib/config?status.svg
   :target: https://godoc.org/github.com/sahib/config

.. Test status via Travis:
.. image:: https://img.shields.io/travis/sahib/config/master.svg?style=flat
   :target: https://travis-ci.org/sahib/config

.. Issue tracker:
.. image:: https://img.shields.io/github/issues/sahib/config.svg?style=flat
   :target: https://github.com/sahib/config/issues

.. Release overview:
.. image:: https://img.shields.io/github/release/sahib/config.svg?style=flat
   :target: https://github.com/sahib/config/releases

.. Download count:
.. image:: https://img.shields.io/github/downloads/sahib/config/latest/total.svg
   :target: https://github.com/sahib/config/releases/latest

.. GPL tag:
.. image:: http://img.shields.io/badge/license-GPLv3-4AC51C.svg?style=flat
   :target: https://www.gnu.org/licenses/quick-guide-gplv3.html.en

``config``
==========

Go package for loading typesafe program configuration with validation and migration.

Motivation
----------

There are already multiple packages available for loading application
configuration from a file. Viper_ is just one of the more popular ones. They
mostly focus on being convinient to use and to support lots of different
configuration sources (environment variables, network sources, files...). I
found none though that focuses on loading configuration and validating the
incoming values. Some libraries supported default values, but all of them
allowed »free form« configs -- i.e. defining keys that are not available in the
program without any warning. This usually leads to a lot of user errors, wrong
configuration and/or misbehaving programs. Config libraries should be very
strict in what they accept and allow for validation and migration of the config
when the layout changes -- this is why I wrote this package. If you didn't
notice yet: The design is somewhat opinionated and not tailored to cover all
possible use cases or to be overly convinient in exchange for power.

*Note:* The API is influenced by Python's ConfigObj_, but ``specs.cfg`` is part of the program.

.. _Viper: https://github.com/spf13/viper
.. _ConfigObj: http://configobj.readthedocs.io/en/latest/configobj.html

Features
--------

**Validated:** All configuration keys that are possible to set are defined with
a normal type in a ``.go`` file as part of your application. Those values are
also taken over as defaults, if they are not explicitly overwritten. This
ensures that the program always have sane values to work with. Every key can be
associated with a validation func, which can be used to implement further
validation (i.e. allow a string key to only have certain enumeration values).

**Versioned:** Every config starts with a version of zero. If the application
owning the config needs to change the layout, it can register a migration
function to do this once an old configuration is loaded. This frees you from worrying
about breaking changes in the config.

**Typesafety**: There is no stringification of values or other surprises (like
in *ConfigObj*). Every configuration key has exactly one value, directly
defined in Go's type system.

**Change Notification and instant reloading:** The application can reload the
configuration anytime and also register a func that will be called when a
certain key changes. This allows longer running daemon processes to react
instantly on config changes, if possible.

**Built-in Documentation:** You can write down documentation for your configuration
as part of the defaults definition, including a hint if this key needs a restart of
the application to take effect.

**Support for multiple formats:** Only YAML is supported by default, since it
suffices in the vast majority of use cases. If you need to, you can define your
own ``Encoder`` and ``Decoder`` to fit your specific usecase.

**Support for sub-sections:** Sub-sections of a config can be used like a
regular config object. Tip: Define your configuration hierarchy like the
package structure of your program. That way you can pass sub-section config to
those packages and you can be sure that they can only change the keys they are
responsible for.

**Support for placeholder sections:** By using the special section name ``__many__``
you can have several sections that all follow the same layout, but are allowed to be
named differently.

**Native support for slices:** All native types (``string``, ``int``, ``float`` and ``bool``)
can be packed into lists and easily set and accessed with special API for them.

**Merging:** Several configs from several sources can be merged. This might be
useful e.g. when there are certain global defaults, that are overwritten with local
defaults which are again merged with user defined settings.

**Reset to defaults:** Any part of the config can be reset to defaults at any time.

Migrations
----------

Sometimes, you have to change the layout of your config or change default values.
Since the configs of this module have version tags, this becomes quite simple.
All you have to do is to write a migration function that transforms the old
config to a new config - Take a look at the example below.

When do you have to migrate?

- When moving or removing a key or section.
- When changing the default of an existing key.
- When adding new keys that are pre-filled by some user-defined logic.

If you're only adding new keys with constant defaults, there is no need for a migration.

Examples
--------

- `Basic example.`_

.. _`Basic example.`: https://github.com/sahib/config/blob/master/example_test.go#L51

- `Migration example.`_

.. _`Migration example.`: https://github.com/sahib/config/blob/master/example_test.go#L127

If the validation was not succesful, you can either error out directly or continue with defaults as fallback:

.. code-block:: go

    // Somewhere in your init code:
    cfg, err := config.Open(config.NewYamlDecoder(fd), defaults)
    if err != nil {
        log.Errorf("Failed to user config. Continuing with defaults.")
        cfg, err = config.Open(nil, defaults)
        if err != nil {
            // Something is really wrong. The defaults are probably wrong.
            // This is a programmer error and should be catched early though.
            log.Fatalf("Failed to load default config: %v", err)
        }
    }


LICENSE
-------

`config` is licensed under the conditions of the `GPLv3
<https://www.gnu.org/licenses/quick-guide-gplv3.html.en>`_. See the
``COPYING``. distributed along the source for details.

Author
------

Christopher <sahib_> Pahl 2018

.. _sahib: https://www.github.com/sahib

----

Originally developed as part of »brig« (https://github.com/sahib/brig)

Documentation

Overview

Package config implements a very opinionated config utility. It relies on a "default spec", i.e. a structure that defines all existing configuration keys, their types and their initial default values. This is used as fallback and source of validation. The idea is similar to python's configobj (albeit much smaller). Surprisingly I didn't find any similar library in Go.

Note that passing invalid keys to a few methods will cause a panic - on purpose. Using a wrong config key is seen as a bug and should be corrected immediately. This allows this package to skip error handling on Get() and Set() entirely.

In short: This config does a few things different than the ones I saw for Go. Instead of providing numerous possible sources and formats to save your config it simply relies on YAML out of the box. The focus is not on ultimate convinience but on:

- Providing meaningful validation and default values.

- Providing built-in documentation for all config values.

- Making it able to react on changed config values.

- Being usable from several go routines.

- In future: Provide an easy way to migrate configs.

Index

Examples

Constants

View Source
const (
	// StrictnessIgnore silently ignores any programmer error
	StrictnessIgnore = Strictness(iota)
	// StrictnessWarn will log a complaint via log.Println()
	StrictnessWarn
	// StrictnessPanic will panic when a programmer error was made.
	StrictnessPanic
)

Variables

View Source
var (
	// ErrNotVersioned is returned by Migrate() when it can't find the version tag.
	// If that happens you can still try to Open() the config normally.
	ErrNotVersioned = errors.New("config has no valid version tag")
)

Functions

func DurationValidator added in v0.2.0

func DurationValidator() func(val interface{}) error

DurationValidator asserts that the config value is a valid duration that can be parsed by time.ParseDuration.

func EnumValidator

func EnumValidator(options ...string) func(val interface{}) error

EnumValidator checks if the supplied string value is in the `options` list.

func FloatRangeValidator

func FloatRangeValidator(min, max float64) func(val interface{}) error

FloatRangeValidator checks if the supplied float value lies in the inclusive boundaries of `min` and `max`.

func IntRangeValidator

func IntRangeValidator(min, max int64) func(val interface{}) error

IntRangeValidator checks if the supplied integer value lies in the inclusive boundaries of `min` and `max`.

func ListValidator added in v0.2.0

func ListValidator(fn func(val interface{}) error) func(val interface{}) error

ListValidator takes any other validator and applies it to a list value. If `fn` is nil it only checks if the value is indeed a list.

func MigrateKeys

func MigrateKeys(oldCfg, newCfg *Config, fn func(key string, err error) error) error

MigrateKeys is a helper function to write migrations easily. It takes the old and new config and copies all keys that are compatible (i.e. same key, same type). If calls fn() on any key that exists in the new config and not in the old config (i.e. new keys). If any error occurs during set (e.g. wrong type) fn is also called. If fn returns a non-nil error this method stops and returns the error.

func ToYamlFile

func ToYamlFile(path string, cfg *Config) error

ToYamlFile saves `cfg` as YAML at a file located at `path`.

Types

type Config

type Config struct {
	// contains filtered or unexported fields
}

Config is a helper that is built around a representation defined by a Encoder/Decoder. It supports typed gets and sets, change notifications and basic validation with defaults.

func FromYamlFile

func FromYamlFile(path string, defaults DefaultMapping, strictness Strictness) (*Config, error)

FromYamlFile creates a new config from the YAML file located at `path`

func Open

func Open(dec Decoder, defaults DefaultMapping, strictness Strictness) (*Config, error)

Open creates a new config from the data in `r`. The mapping in `defaults ` tells the config which keys to expect and what type each of it should have. It is allowed to pass `nil` as decoder. In this case a config purely with default values will be created.

Example

Basic usage example:

// You either open it via existing data - or in case of the initial write,
// you just let it take over the defaults by passing nil as decoder. Open()
// is also the step where the initial validation happens.  If this
// validation fails, an error is returned.
cfg, err := Open(nil, ExampleDefaultsV0, StrictnessPanic)
if err != nil {
	log.Fatalf("Failed to open config: %v", err)
}

// Fetching keys is easy now and requires no error handling:
cfg.String("backend.name")   // -> the_good_one
cfg.Int("backend.workers")   // -> 10
cfg.Bool("ui.show_tooltips") // -> true

// You can set also set keys: This one will return an error though because,
// "the_great_one" is no valid enum value (see defaults) Note that using a
// bad key is considered a programming error and will cause a panic. This
// is on purpose and might seem radical, but should help you to catch
// errors early in the development.
cfg.SetString("backend.name", "the_great_one")

// If you'd like to print an overview over all config keys,
// you can just get a list of all default entries:
for _, key := range cfg.Keys() {
	entry := cfg.GetDefault(key)
	fmt.Printf("%s: %s (restart: %t)\n", key, entry.Docs, entry.NeedsRestart)
}

// If you have only a string (e.g. from a cmdline config set),
// you can ask the config to convert it to the right type:
// Note that you need to check if it's an valid key, otherwise cfg.Set() might panic.
alienKey, alienVal := "backend.workers", "15"
if cfg.IsValidKey(alienKey) {
	safeVal, err := cfg.Cast(alienKey, alienVal)
	if err != nil {
		log.Fatalf("Uh, oh, could not cast key to the right type: %v", err)
	}

	// safeVal is an integer now:
	cfg.Set(alienKey, safeVal)
}

// Want to know if something changed?
// Just register a callback for it. If you pass an empty string,
// you'll get callbacks for every set. The respective key is then
// passed to the callback.
cid := cfg.AddEvent("backend.workers", func(key string) {
	fmt.Println("Key was changed:", key)
})

// You can get rid of callbacks too of course:
cfg.RemoveEvent(cid)

// One nifty feature is to pass only a sub section of the config
// to specific parts of the program - Which saves you from typing
// the full keys and disallowing them to remove other parts.
backendCfg := cfg.Section("backend")
backendCfg.Get("name") // -> the_good_one

// When you're done, you can always serialize the config
// and save it wherever you like.
buf := &bytes.Buffer{}
if err := cfg.Save(NewYamlEncoder(buf)); err != nil {
	log.Fatalf("Failed to save config: %v", err)
}

fmt.Println(buf.String())
Output:

backend.name: Choose what backend you want to use. (restart: true)
backend.workers: How many workers to start. (restart: false)
ui.show_tooltips: Show tooltips when you least expect them (restart: false)
# version: 0 (DO NOT MODIFY THIS LINE)
backend:
  name: the_good_one
  workers: 15
ui:
  show_tooltips: true

func (*Config) AddEvent

func (cfg *Config) AddEvent(key string, fn func(key string)) int

AddEvent registers a callback to be called when `key` is changed. Special case: if key is the empy string, the registered callback will get called for every change (with the respective key) This function supports registering several callbacks for the same `key`. The returned id can be used to unregister a callback with RemoveEvent() Note: This function will panic when using an invalid key.

func (*Config) Bool

func (cfg *Config) Bool(key string) bool

Bool returns the boolean value (or default) at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used. If an error happens it will return the zero value.

func (*Config) Bools added in v0.2.0

func (cfg *Config) Bools(key string) []bool

Bools returns the boolean list value (or default) at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used. If an error happens it will return the zero value.

func (*Config) Cast

func (cfg *Config) Cast(key, val string) (interface{}, error)

Cast takes `val` and reads the type of `key`. It then tries to convert it to one of the supported types (and possibly fails due to that)

This cast assumes that `val` is always a string, which is useful for data coming fom the client. Note: This function will panic if the key does not exist.

func (*Config) ClearEvents

func (cfg *Config) ClearEvents()

ClearEvents removes all registered events.

func (*Config) Duration added in v0.2.0

func (cfg *Config) Duration(key string) time.Duration

Duration returns the duration value (or default) at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used. If an error happens it will return the zero value.

func (*Config) Durations added in v0.2.0

func (cfg *Config) Durations(key string) []time.Duration

Durations returns the duration value (or default) at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used. If an error happens it will return the zero value.

func (*Config) Float

func (cfg *Config) Float(key string) float64

Float returns the float value (or default) at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used. If an error happens it will return the zero value.

func (*Config) Floats added in v0.2.0

func (cfg *Config) Floats(key string) []float64

Floats returns the float list value (or default) at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used. If an error happens it will return the zero value.

func (*Config) Get

func (cfg *Config) Get(key string) interface{}

Get returns the raw value at `key`. Do not use this method when possible, use the typeed convinience methods. Note: This function might panic when they key does not exist and StrictnessPanic is used. If an error happens it will return the zero value.

func (*Config) GetDefault

func (cfg *Config) GetDefault(key string) DefaultEntry

GetDefault retrieves the default for a certain key. Note: This function might panic when they key does not exist and StrictnessPanic is used.

func (*Config) Int

func (cfg *Config) Int(key string) int64

Int returns the int value (or default) at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used. If an error happens it will return the zero value.

func (*Config) Ints added in v0.2.0

func (cfg *Config) Ints(key string) []int64

Ints returns the int list value (or default) at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used. If an error happens it will return the zero value.

func (*Config) IsDefault added in v0.2.0

func (cfg *Config) IsDefault(key string) bool

IsDefault will return true if this key was not explicitly set, but taken over from the defaults. Note: This function might panic when they key does not exist and StrictnessPanic is used.

func (*Config) IsValidKey

func (cfg *Config) IsValidKey(key string) bool

IsValidKey can be checked to see if untrusted keys actually are valid. It should not be used to check keys from string literals.

func (*Config) Keys

func (cfg *Config) Keys() []string

Keys returns all possible keys (including the default keys)

func (*Config) Merge added in v0.2.0

func (cfg *Config) Merge(other *Config) error

Merge takes all values from `other` that were set explicitly and sets them in `cfg`. If any key changes, the respective event callback will be called.

func (*Config) Reload

func (cfg *Config) Reload(dec Decoder) error

Reload re-sets all values in the config to the data in `dec`. If `dec` is nil, all default values will be returned. All keys that changed will trigger a signal, if registered.

Note that you cannot pass different defaults on Reload, since this might alter the structure of the config, potentially causing incompatibillies. Use the migration interface if you really need to change the layout.

func (*Config) RemoveEvent

func (cfg *Config) RemoveEvent(id int)

RemoveEvent removes a previously registered callback.

func (*Config) Reset added in v0.2.0

func (cfg *Config) Reset(key string) error

Reset reverts a key or a section to its defaults. If key points to a value, only this value is reset. If key points to a section, all keys in it are reset. If key is an empty string, the whole config is reset to defaults.

When calling reset on parts of the config that includes __many__ sections, those will be totally cleared and won't be serialized when calling Save(). Retrieving values from those will yield default values as if they were not set.

func (*Config) Save

func (cfg *Config) Save(enc Encoder) error

Save will write a representation defined by `enc` of the current config to `w`.

func (*Config) Section

func (cfg *Config) Section(section string) *Config

Section returns a new config that

func (*Config) Set

func (cfg *Config) Set(key string, val interface{}) error

Set creates or sets the `val` at `key`. Please only use this function only if you have an interface{} that you do not want to cast yourself. Note: This function might panic when they key does not exist and StrictnessPanic is used.

func (*Config) SetBool

func (cfg *Config) SetBool(key string, val bool) error

SetBool creates or sets the `val` at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used.

func (*Config) SetBools added in v0.2.0

func (cfg *Config) SetBools(key string, val []bool) error

SetBools creates or sets the `val` at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used.

func (*Config) SetDuration added in v0.2.0

func (cfg *Config) SetDuration(key string, val time.Duration) error

SetDuration creates or sets the `val` at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used.

func (*Config) SetDurations added in v0.2.0

func (cfg *Config) SetDurations(key string, val []time.Duration) error

SetDurations creates or sets the `val` at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used.

func (*Config) SetFloat

func (cfg *Config) SetFloat(key string, val float64) error

SetFloat creates or sets the `val` at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used.

func (*Config) SetFloats added in v0.2.0

func (cfg *Config) SetFloats(key string, val []float64) error

SetFloats creates or sets the `val` at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used.

func (*Config) SetInt

func (cfg *Config) SetInt(key string, val int64) error

SetInt creates or sets the `val` at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used.

func (*Config) SetInts added in v0.2.0

func (cfg *Config) SetInts(key string, val []int64) error

SetInts creates or sets the `val` at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used.

func (*Config) SetString

func (cfg *Config) SetString(key string, val string) error

SetString creates or sets the `val` at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used.

func (*Config) SetStrings added in v0.2.0

func (cfg *Config) SetStrings(key string, val []string) error

SetStrings creates or sets the `val` at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used.

func (*Config) String

func (cfg *Config) String(key string) string

String returns the string value (or default) at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used. If an error happens it will return the zero value.

func (*Config) Strings added in v0.2.0

func (cfg *Config) Strings(key string) []string

Strings returns the string list value (or default) at `key`. Note: This function might panic when they key does not exist and StrictnessPanic is used. If an error happens it will return the zero value.

func (*Config) Uncast added in v0.2.0

func (cfg *Config) Uncast(key string) string

Uncast is the opposite of Cast. It works like Get(), but always returns a stringified version of a value, even in case of slices. The representation returned by Uncast() can be later read in again by using Cast().

Cast() and Uncast() are mainly useful in systems where you can only use strings, e.g. when building an API between different programming languages. Note: Slice items are separated by " ;; ".

func (*Config) Version

func (cfg *Config) Version() Version

Version returns the version of the config The initial version is always 0. It will be only updated by migrating to newer versions.

type Decoder

type Decoder interface {
	// Decode takes a byte stream from when the decoder was created
	// and parses the version and the internal representation out of it.
	Decode() (Version, map[interface{}]interface{}, error)
}

Decoder defines how a byte stream can be parsed to a config

func NewYamlDecoder

func NewYamlDecoder(r io.Reader) Decoder

NewYamlDecoder creates a new Decoder that parses the data in `r`. It will look at the first line of the input to get the version.

type DefaultEntry

type DefaultEntry struct {
	// Default is the fallback value for this config key.
	// The confg type will be inferred from its literal type.
	Default interface{}

	// NeedsRestart indicates that we need to restart the daemon
	// to have an effect here.
	NeedsRestart bool

	// Docs describes the meaning of the configuration value.
	Docs string

	// Function that can be used to check
	Validator func(val interface{}) error
}

DefaultEntry represents the metadata for a default value in the config. Every possible key has to have a DefaultEntry, otherwise Get() and Set() will panic at you since this is considered a programmer error.

type DefaultMapping

type DefaultMapping map[interface{}]interface{}

DefaultMapping is a container to hold all required DefaultEntries. It is a nested map with sections as string keys.

type Encoder

type Encoder interface {
	// Encode takes the version and the internal config representation
	// and turns it to a byte stream that was passed when creatin the encoder.
	Encode(version Version, data map[interface{}]interface{}) error
}

Encoder defines how the config can be serialized to a byte stream

func NewYamlEncoder

func NewYamlEncoder(w io.Writer) Encoder

NewYamlEncoder creates a new Encoder that writes a YAML file with the config data. The file will start with a comment indicating the version, so pay attention to not remove it by accident.

type Migrater

type Migrater struct {
	// contains filtered or unexported fields
}

Migrater is a factory for creating version'd configs. See NewMigrater() for more details.

Example
// This config package optionally supports versioned configs.
// Whenever you decide to change the layout of the config,
// you can bump the version and register a new migration func
// that will be run over older config upon opening them.
mgr := NewMigrater(CurrentVersion, StrictnessPanic)

// Add a migration - the first one for version "0" has no func attached.
mgr.Add(0, nil, ExampleDefaultsV0)

// For version "1" we gonna need a function that transforms the config:
migrateToV1 := func(oldCfg, newCfg *Config) error {
	// Use the helpful MigrateKeys method to migrate most of the keys.
	// It will call you back on every missing key or any errors.
	return MigrateKeys(oldCfg, newCfg, func(key string, err error) error {
		switch key {
		case "backend.accuracy":
			// Do something based on the old config key:
			return newCfg.SetFloat(
				"backend.accuracy",
				float64(oldCfg.Int("backend.workers"))+0.5,
			)
		default:
			return fmt.Errorf("Incomplete migration for key: %v", key)
		}
	})
}

// Add it with the new respective results:
mgr.Add(1, migrateToV1, ExampleDefaultsV1)

rawConfig := `# version: 0
ui:
  show_tooltips: true
backend:
  name: the_good_one
  workers: 10
`

// The Migrate call works like a factory method.
// It creates the config in a versioned way:
cfg, err := mgr.Migrate(NewYamlDecoder(strings.NewReader(rawConfig)))
if err != nil {
	// Handle errors...
}

cfg.Version() // -> 1 now.

// If you print it, you will notice a changed version tag:
buf := &bytes.Buffer{}
cfg.Save(NewYamlEncoder(buf))
fmt.Println(buf.String())
Output:

# version: 1 (DO NOT MODIFY THIS LINE)
backend:
  accuracy: 10.5
  name: the_good_one
ui:
  show_tooltips: true

func NewMigrater

func NewMigrater(currentVersion Version, strictness Strictness) *Migrater

NewMigrater returns a new Migrater.

It can be seen as an migration capable version of config.Open(). Instead of directly passing the defaults you register a number of migrations (each with their own migration func, defaults and version). The actual work is done by the migration functions which are written by the caller of this API. The caller defined migration method will likely call MigrateKeys() though.

Call Migrate() on the migrater will read the current version and try to migrate to the most recent one.

func (*Migrater) Add

func (mm *Migrater) Add(version Version, migration Migration, defaults DefaultMapping)

Add a new migration entry. Adding a migration without a migration func is allowed

func (*Migrater) Migrate

func (mm *Migrater) Migrate(dec Decoder) (*Config, error)

Migrate reads the config from `dec` and converts it to the newest version if required.

type Migration

type Migration func(oldCfg, newConfig *Config) error

Migration is a function that is executed to replicate the changes between to versions. It should modify `newCfg` in a way so all keys from `oldCfg` that were portable are transferred.

type Strictness added in v0.2.0

type Strictness int

Strictness defines how the API of the config reacts when it thinks that the programmer made a mistake. These kind of mistakes usually fall into one of the following categories:

- A wrong key was used for a Get(), Set() etc. - The wrong type getter was used for a certain key (bool for a string e.g.) - The defaults are faulty (wrong types, __many__ in the wrong place etc.)

All of those errors should be catched early in the development and should not make it to the user. Therefore StrictnessPanic is recommended for developing. When building the software in release mode, one might want to switch to StrictnessWarn, which just logs when it found something awful.

type Version

type Version int

Version specifies the version of the config. The exact number does not matter; the migration only cares about less or more. It's recommended to use successive numbers though. The first version is 0.

Jump to

Keyboard shortcuts

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