Skip to content

Custom formats

The format driver reads config files of any format with nested objects. The format is set by a function that decodes the content to a map, everything else is the same as in the yaml, json and toml drivers: several files, merging, embedded files, remote documents, environment variables in the content, reloading, documentation.

import "github.com/insei/tinyconf/drivers/format"

driver, err := format.New("hujson", decodeHuJSON, "config.hujson")

The name of the driver is the name of the struct tag with the keys, and the name for GenDoc:

type Config struct {
    HTTP struct {
        Port int `hujson:"port"`
    } `hujson:"http"`
}

Decoder

type Decoder func(data []byte) (map[string]any, error)

The decoder returns the content as a map:

  • keys of the map are the keys of the config, nested maps with string keys are nested objects;
  • slices are lists, other values are scalars: strings, numbers, booleans, time.Time;
  • empty content can be decoded to a nil map.

Values are converted to the types of the fields by the same rules as the values of yaml and json, see Supported types.

For example, HuJSON — JSON with comments and trailing commas:

import "github.com/tailscale/hujson"

func decodeHuJSON(data []byte) (map[string]any, error) {
    data, err := hujson.Standardize(data)
    if err != nil {
        return nil, err
    }
    var doc map[string]any
    return doc, json.Unmarshal(data, &doc)
}

Libraries that decode to the structs only, like HCL, need the conversion of their result to the map.

Options

Option Description
WithFiles(files...) additional files from the OS file system
WithFS(fsys, files...) files from any fs.FS, embed.FS for example
WithData(name, data) in-memory content, the name is used in error messages
WithSource(source) content from a remote storage: consul.Document, etcd.Document
WithOptional() files can be absent
WithEnvExpansion() replaces ${VAR} in the content with environment variables
WithWatchInterval(interval) interval of polling the files for reloading, 5 seconds by default
WithEncoder(encode) encoder of the documents for GenDoc

The rules of the files are the same as in the yaml driver.

Documentation

GenDoc needs the encoder:

type Encoder func(doc map[string]any) ([]byte, error)

driver, err := format.New("hujson", decodeHuJSON, "config.hujson", format.WithEncoder(func(doc map[string]any) ([]byte, error) {
    return json.MarshalIndent(doc, "", "  ")
}))

The document has the same structure as the result of the decoder, with the current values of the registered configs; values of hidden:"true" fields are empty strings. Without the encoder GenDoc returns an empty string.

Not a file format?

If the values are not stored as documents — a database, an API, a secret manager — write a custom driver.