boa

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

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

Go to latest
Published: Jul 25, 2023 License: MIT Imports: 13 Imported by: 0

README

Boa Logo

A friendlier viper.

Builds GoDoc GitHub Dependencies

go get snai.pe/boa

Introduction

Boa aims to be a simple, human-focused, no-dependency configuration management library.

It supports JSON5 and TOML.

Configurations are expressed as Go types, using struct field tags to map configuration files to type-safe data types.

For instance, the following TOML configuration file:

[person]
first-name = "John"
last-name = "Doe"
dob = 1953-02-25T00:00:42-08:00

Can be loaded into this Go type:

type Config struct {
  Person struct {
    FirstName string
    LastName  string
    Birth     time.Time `name:"dob"`
  } `naming:"kebab-case"`
}

Why boa?

At the time of writing, none of the other configuration parsers are actually designed for configuration. Most of the standard parsers under the encoding package are designed for robots. Non-standard parsers either do not follow the same semantics as the standard packages, or suffer the same flaws as standard parsers. Comments and formatting are usually not parsed nor preserved. Encoders interact poorly with other encoders, usually necessitating multiple struct tags per configuration language, or do not provide a good interface for accessing and discovering configuration values.

Boa aims to be an overall better configuration management library. It has no dependencies outside of the standard Go library, and provides a unified way to load, manipulate, and save configurations.

The following languages are supported:

  • JSON5
  • TOML

In addition, all configuration parsers have the following properties:

  • Error messages contain the filename when available as well as the line and column number where the error occured.
  • Parsers support the same set of base struct tags for consistency and conciseness.
  • Comments and whitespace are preserved by the parsers in the configuration AST, which makes it possible to edit configuration while still preserving the style of the original file.

Supported tags

Tag Description
name:"⁠<name>" Set key name.
help:"⁠<help>" Set documentation; appears as comment in the config.
naming:"⁠<name>" Set naming convention for key and subkeys.
env:"⁠<var>" Populate field with specified environment variable.
inline Inline field. All sub-fields will be treated as if they were in the containing struct itself. Does the same as embedding the field.
- Ignore field.

Supported types

Any type that implements encoding.TextMarshaler can be saved as a string. Any type that implements encoding.TextUnmarshaler can be loaded from a string.

In addition, the following standard library types are marshaled and unmarshaled as the appropriate type:

Type Treated as
[]byte String
*big.Int Number
*big.Float Number
*big.Rat Number
time.Time String
*url.URL String
*regexp.Regexp String

Some packages also define or support some specialized types for specific configuration objects:

Type Treated as Packages (under snai.pe/boa/encoding)
time.Time DateTime toml
toml.LocalDateTime DateTime toml
toml.LocalDate DateTime toml
toml.LocalTime DateTime toml

Examples

Loading configuration

package main

import (
	"fmt"
	"log"

	"snai.pe/boa"
)

func main() {

	var config struct {
		Answer   int               `help:"This is an important field that needs to be 42"`
		Primes   []int             `help:"Some prime numbers"`
		Contacts map[string]string `help:"Some people in my contact list"`
	}

	// Will load any matching "appname.toml" config file from the system config path,
	// then the user config path. The TOML decoder is inferred from the .toml extension.
	//
	// For instance, on Linux, this will load in order:
	//     - /etc/<appname>.toml
	//     - /etc/xdg/<appname>.toml
	//     - ~/.config/<appname>.toml
	//
	if err := boa.Load("appname", &config); err != nil {
		log.Fatalln(err)
	}

}

Loading configuration, with defaults

Configuration defaults are not, by design, set via struct tags or other field-specific mechanisms.

Instead, write a default configuration file in your package, and embed it. Multiple configs defaults can be embedded into the same embed.FS declaration -- see the documentation of the embed package.

package main

import (
	"embed"
	"fmt"
	"log"

	"snai.pe/boa"
)

//go:embed appname.toml
var defaults embed.FS

func main() {

	// Register defaults
	boa.SetDefaults(defaults)

	var config struct {
		Answer   int               `help:"This is an important field that needs to be 42"`
		Primes   []int             `help:"Some prime numbers"`
		Contacts map[string]string `help:"Some people in my contact list"`
	}

	if err := boa.Load("appname", &config); err != nil {
		log.Fatalln(err)
	}

}

Good configuration defaults should be consistent and self-explanatory. Consider making the default for fields their respective type's zero value.

Environment variables

Configuration fields can be explicitly bound to environment variables via the env struct tag:

type Config struct {
	Shell string   `env:"SHELL"`
	Path  []string `env:"PATH"`
}

Environment values are generally parsed according to the strconv Parse functions, or using UnmarshalText if the field's type implements encoding.TextUnmarshaler.

Slices and arrays are parsed as a path-list-separated list of strings. The delimiter is os.PathListSeparator: with the above example, on Unix derivatives, PATH=a:b:c would get unmarshaled as [a, b, c], while on Windows the value would need to be PATH=a;b;c.

Fields with no env tag are not populated from the environment, unless the AutomaticEnv option is provided:

type Config struct {
	ImplicitVariable string
}

boa.SetOptions(
	boa.AutomaticEnv("PREFIX"),
)

In this example, PREFIX_IMPLICIT_VARIABLE=value would set Config.ImplicitVariable.

Credits

Logo made by Irina Mir

Documentation

Overview

Example
package main

import (
	"embed"
	"fmt"
	"log"

	"snai.pe/boa"
)

// This embed declaration embeds the example_defaults.toml file (relative to
// the package directory) into the filesystem at `defaults`.

//go:embed example_defaults.*
var defaults embed.FS

var config struct {
	Greeting string `help:"A nice hello."`
}

func main() {

	// Register the default files
	boa.SetDefaults(defaults)

	// Opens and loads, in order, the example_defaults.toml files from the
	// following paths:
	//
	//     -  <defaults>/example_defaults.toml
	//     -  /etc/example_defaults.toml
	//     -  ~/.config/example_defaults.toml
	//
	cfg := boa.Open("example_defaults.toml")
	defer cfg.Close()

	// Load the defaults into the config variable
	if err := boa.NewDecoder(cfg).Decode(&config); err != nil {
		log.Fatalln(err)
	}

	fmt.Println(config.Greeting)
}
Output:

Hello from TOML!

Index

Examples

Constants

This section is empty.

Variables

View Source
var Decoders = map[string]func(io.Reader) encoding.Decoder{
	".toml":  toml.NewDecoder,
	".json5": json5.NewDecoder,
	".json":  json5.NewDecoder,
}

Decoders map filename extensions to decoders. By default, the following coniguration languages are associated with the extensions:

  • JSON5: .json and .json5
  • TOML: .toml

The contents of this map controls how the Decoder type deduces which decoder to use based on the file extension of the input.

View Source
var Encoders = map[string]func(io.Writer) encoding.Encoder{
	".toml":  toml.NewEncoder,
	".json5": json5.NewEncoder,
	".json":  json5.NewEncoder,
}

Encoders map filename extensions to encoders. By default, the following coniguration languages are associated with the extensions:

  • JSON5: .json and .json5
  • TOML: .toml

The contents of this map controls how the Encoder type deduces which encoder to use based on the file extension of the output.

Functions

func ConfigHome

func ConfigHome() (string, error)

ConfigHome returns the filesystem path to the current user's configuration home, or an error if there is none.

This function should be used to determine where to save configuration. If it returns an error, no configuration should be saved.

The configuration home of a user is a os-dependent directory that is writeable by that user, and contains configuration files for the programs used by that user.

The returned path is generally OS-specific. Typical values per OS are:

  • Linux & UNIX derivatives: ~/.config ($XDG_CONFIG_HOME)
  • macOS: ~/Library/Preferences
  • Windows: C:\Users\<user>\AppData\Roaming (%APPDATA%)

func ConfigHomeFS

func ConfigHomeFS() fs.FS

ConfigHomeFS returns the fs.FS for the user's configuration home.

By default, it returns os.DirFS(ConfigHome()) (or nil if unsuccessful), unless SetConfigHomeFS has been called, in which case the FS that was set by the function is returned.

func ConfigPaths

func ConfigPaths() []fs.FS

ConfigPaths returns, in order of least important to most important, the paths that may hold configuration files for the current user.

This function should be used to determine from where to load configuration. Every matching configuration file in the paths should be loaded into the same configuration object, from least important to most important, in order to construct the full configuration.

User directories are always more important than system directories. Typical values per OS are:

  • Linux & UNIX derivatives: /etc, /etc/xdg, ~/.config ($XDG_CONFIG_DIRS & $XDG_CONFIG_HOME)
  • macOS: /Library/Preferences, ~/Library/Preferences
  • Windows: C:\ProgramData, C:\Users\<user>\AppData\Roaming

func Load

func Load(name string, v interface{}) error

Load loads the configuration files for the specified name into the specified value pointed to by v.

The configuration language is deduced based on the file extension of the specified path:

  • JSON5: .json and .json5
  • TOML: .toml

This is a convenience function that is functionally equivalent to:

Custom file extensions are not supported, and one of the decoders in snai.pe/boa/encoding must be used instead.

func NewSingleFileFS

func NewSingleFileFS(name, path string) fs.FS

NewSingleFileFS returns an io/fs.FS containing the file at path, under the specified name.

For instance, NewSingleFileFS("program.toml", os.Discard) will return an FS object such that opening the program.toml file opens the os.Discard file instead.

This is particularly useful to pass single file config overrides to Open and OpenMultiple.

If path is the empty string, NewSingleFileFS returns nil.

func Save

func Save(name string, v interface{}) error

Save saves the specified value in v into a named configuration file.

The name is interpreted relative to the return value of ConfigHome(). To save to arbitrary file paths, use os.Create and NewEncoder instead.

The configuration language is deduced based on the file extension of the specified path:

  • JSON5: .json and .json5
  • TOML: .toml

Custom file extensions are not supported, and one of the decoders in snai.pe/boa/encoding must be used instead.

func SetConfigHomeFS

func SetConfigHomeFS(f fs.FS)

SetConfigHomeFS overrides the user configuration home, which gets added as the most important path in the slice returned by ConfigPaths.

Example
var config struct {
	Greeting string `help:"A nice hello."`
}

// Use NewSingleFileFS to expose example_defaults.toml as program.toml.
//
// This could be used to override the default config paths via the
// environment or a flag
SetConfigHomeFS(
	NewSingleFileFS("program.toml", "example_defaults.toml"),
)

cfg := Open("program.toml")
defer cfg.Close()

// Load the defaults into the config variable
if err := NewDecoder(cfg).Decode(&config); err != nil {
	log.Fatalln(err)
}

fmt.Println(config.Greeting)
Output:

Hello from TOML!

func SetDefaults

func SetDefaults(defaults fs.FS)

SetDefaults sets the FS object containing configuration file defaults.

It is added as the least important path in the slice returned by ConfigPaths.

func SetOptions

func SetOptions(options ...interface{})

SetOptions sets the default set of common, encoder-specific, and decoder-specific options for the functions in the boa package.

Note that it does not change the defaults of specific encoding packages.

Example
package main

import (
	"io/ioutil"
	"log"
	"os"

	"snai.pe/boa"
)

func main() {

	type Config struct {
		FirstName string
		LastName  string
		Nickname  string
	}

	config := Config{
		FirstName: "Franklin",
		LastName:  "Mathieu",
		Nickname:  "Snaipe",
	}

	boa.SetOptions(
		boa.Indent("\t"),
		boa.NamingConvention("kebab-case"),
	)

	f, err := os.Create("testdata/example_opts.json5")
	if err != nil {
		log.Fatalln(err)
	}
	defer f.Close()

	if err := boa.NewEncoder(f).Encode(config); err != nil {
		log.Fatalln(err)
	}

	out, err := ioutil.ReadFile("testdata/example_opts.json5")
	if err != nil {
		log.Fatalln(err)
	}
	os.Stdout.Write(out)

}
Output:

{
	"first-name": "Franklin",
	"last-name": "Mathieu",
	nickname: "Snaipe",
}

func SystemConfigDirs

func SystemConfigDirs() []string

SystemConfigDirs returns the filesystem paths to the system configuration directories.

The returned paths are OS-specific. Typical valies per OS are:

  • Linux & UNIX derivatives: /etc, /etc/xdg ($XDG_CONFIG_DIRS)
  • macOS: /Library/Preferences
  • Windows: C:\ProgramData

Types

type CommonOption

type CommonOption = encoding.CommonOption

CommonOption represents an option common to all encoders and decoders in boa.

func NamingConvention

func NamingConvention(name interface{}) CommonOption

NamingConvention returns an option that sets the default naming convention of an encoder or decoder to the specified convention.

This option takes either a known convention name, or a naming convention value (as defined in the snai.pe/boa/encoding package)

Supported naming conventions names are:

  • "camelCase"
  • "PascalCase"
  • "snake_case"
  • "SCREAMING_SNAKE_CASE"
  • "kebab-case"
  • "SCREAMING-KEBAB-CASE"
  • "camel_Snake_Case"
  • "Pascal_Snake_Case"
  • "Train-Case"
  • "flatcase"
  • "UPPERFLATCASE"

type Decoder

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

A Decoder reads and decodes a configuration from an input file.

Example
package main

import (
	"fmt"
	"log"
	"os"

	"snai.pe/boa"
)

func main() {

	var config struct {
		Answer   int               `help:"This is an important field that needs to be 42"`
		Primes   []int             `help:"Some prime numbers"`
		Contacts map[string]string `help:"Some people in my contact list"`
	}

	f, err := os.Open("testdata/example.json5")
	if err != nil {
		log.Fatalln(err)
	}
	defer f.Close()

	if err := boa.NewDecoder(f).Decode(&config); err != nil {
		log.Fatalln(err)
	}

	fmt.Println("answer:", config.Answer)
	fmt.Println("primes:", config.Primes)
	fmt.Println("contacts:", config.Contacts)
}
Output:

answer: 42
primes: [2 3 5 7 11]
contacts: map[alice:alice@example.com bob:bob@example.com snaipe:me@snai.pe]

func NewDecoder

func NewDecoder(in encoding.StatableReader) *Decoder

NewDecoder returns a new Decoder that reads from `in`.

The configuration language of the input file is deduced based on the file extension of its file path. The file-extension-to-decoder mapping is defined by the contents of the Decoders map in this package.

To only use one specific configuration language, do not use this decoder: Use instead the decoder for the chosen language in snai.pe/boa/encoding.

func (*Decoder) Decode

func (dec *Decoder) Decode(v interface{}) error

func (*Decoder) Option

func (dec *Decoder) Option(opts ...interface{}) encoding.Decoder

type DecoderOption

type DecoderOption = encoding.DecoderOption

DecoderOption represents an option common to all decoders in boa.

func AutomaticEnv

func AutomaticEnv(prefix string) DecoderOption

AutomaticEnv enables the automatic population of config values from the environment. An optional prefix may be specified for the environment variable names.

Example
package main

import (
	"fmt"
	"log"
	"os"
	"strings"

	"snai.pe/boa"
)

func main() {

	type Config struct {
		// Explicitly set by PATH variable. Does not need AutomaticEnv.
		Path []string `env:"PATH"`

		// Implicitly defined by BOA_SHELL due to AutomaticEnv("BOA") option.
		Shell string

		// Maps do not get implicitly populated from the environment -- however
		// existing values can still be overriden by environment variables if
		// the AutomaticEnv option is used.
		//
		// For instance, setting BOA_CONTACTS_NOT_MENTIONED will not create
		// an entry named "NOT_MENTIONED" in the Contacts field (nor will it
		// create a map named "NOT" under it with a "MENTIONED" entry, or
		// try to divine the user's intentions on the naming convention to
		// use with any of these). However, setting BOA_CONTACTS_ALICE to
		// alice@acme.org will successfully replace the existing contact in
		// the example config for alice to alice@acme.org.
		Contacts map[string]string
	}

	environ := []string{
		"PATH=" + strings.Join([]string{"/bin", "/usr/bin", "/sbin", "/usr/sbin"}, string(os.PathListSeparator)),
		"BOA_SHELL=/bin/sh",
		"BOA_CONTACTS_ALICE=alice@acme.org",
		"BOA_CONTACTS_BOB=bob@acme.org",
	}

	boa.SetOptions(
		boa.AutomaticEnv("BOA"),
		boa.Environ(environ),
	)

	var config Config

	f, err := os.Open("testdata/example.json5")
	if err != nil {
		log.Fatalln(err)
	}
	defer f.Close()

	if err := boa.NewDecoder(f).Decode(&config); err != nil {
		log.Fatalln(err)
	}

	fmt.Println("Path:", config.Path)
	fmt.Println("Shell:", config.Shell)
	fmt.Println("Contacts:", config.Contacts)

}
Output:

Path: [/bin /usr/bin /sbin /usr/sbin]
Shell: /bin/sh
Contacts: map[alice:alice@acme.org bob:bob@acme.org snaipe:me@snai.pe]

func Environ

func Environ(env []string) DecoderOption

Environ sets the environment variables that will be used for any substitution, either by fields marked with an `env` tag, or fields implicitly matching variables via AutomaticEnv.

Incompatible with EnvironFunc. Setting Environ after EnvironFunc overrides the lookup function that EnvironFunc previously set.

func EnvironFunc

func EnvironFunc(fn func(string) (string, bool)) DecoderOption

EnvironFunc sets the lookup function for environment variables. By default, os.LookupEnv is used.

Incompatible with EnvironFunc. Setting EnvironFunc after Environ overrides the variables that Environ previously set.

type Encoder

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

An Encoder encodes and writes a configuration into an output file.

Example (Json5)
package main

import (
	"io/ioutil"
	"log"
	"os"

	"snai.pe/boa"
)

func main() {

	type Person struct {
		Name  string
		Email string
	}

	type Database struct {
		Server string `help:"Database endpoint; can be one of:
						* IPv4
						* IPv6
						* DNS host name."`

		Ports         []uint16 `help:"Database ports, in the range [1, 65535)."`
		ConnectionMax int      `help:"Maximum number of connections."`
		Enabled       bool
	}

	type Server struct {
		IP string
		DC string
	}

	type Config struct {
		Title    string
		Owner    Person
		Database Database
		Servers  map[string]Server `help:"Set of servers. Each server has a name, an IP, and a datacenter name."`

		// This field is ignored
		Ignored string `-`

		ForeignKeys struct {
			SomeInt int
		} `naming:"kebab-case" help:"This map has a different naming convention"`
	}

	config := Config{
		Title: "JSON5 Example",

		Owner: Person{
			Name:  "Snaipe",
			Email: "me@snai.pe",
		},

		Database: Database{
			Server:        "192.168.1.1",
			Ports:         []uint16{8001, 8001, 8002},
			ConnectionMax: 5000,
			Enabled:       true,
		},

		Servers: map[string]Server{
			"alpha": Server{
				IP: "10.0.0.1",
				DC: "eqdc10",
			},
			"beta": Server{
				IP: "10.0.0.2",
				DC: "eqdc10",
			},
		},

		Ignored: "this field is ignored",
	}

	f, err := os.Create("testdata/example_save.json5")
	if err != nil {
		log.Fatalln(err)
	}
	defer f.Close()

	if err := boa.NewEncoder(f).Encode(config); err != nil {
		log.Fatalln(err)
	}

	out, err := ioutil.ReadFile("testdata/example_save.json5")
	if err != nil {
		log.Fatalln(err)
	}
	os.Stdout.Write(out)

}
Output:

{
  title: "JSON5 Example",
  owner: {
    name: "Snaipe",
    email: "me@snai.pe",
  },
  database: {
    // Database endpoint; can be one of:
    // * IPv4
    // * IPv6
    // * DNS host name.
    server: "192.168.1.1",
    // Database ports, in the range [1, 65535).
    ports: [
      8001,
      8001,
      8002,
    ],
    // Maximum number of connections.
    connectionMax: 5000,
    enabled: true,
  },
  // Set of servers. Each server has a name, an IP, and a datacenter name.
  servers: {
    alpha: {
      ip: "10.0.0.1",
      dc: "eqdc10",
    },
    beta: {
      ip: "10.0.0.2",
      dc: "eqdc10",
    },
  },
  // This map has a different naming convention
  "foreign-keys": {
    "some-int": 0,
  },
}
Example (Toml)
package main

import (
	"io/ioutil"
	"log"
	"os"
	"time"

	"snai.pe/boa"
)

func main() {

	type Person struct {
		Name string
		DOB  time.Time
	}

	type Database struct {
		Server string `help:"Database endpoint; can be one of:
						* IPv4
						* IPv6
						* DNS host name."`

		Ports         []uint16 `help:"Database ports, in the range [1, 65535)."`
		ConnectionMax int      `help:"Maximum number of connections."`
		Enabled       bool
	}

	type Server struct {
		IP string
		DC string
	}

	type Config struct {
		Title    string
		Owner    Person
		Database Database
		Servers  map[string]Server `help:"Set of servers. Each server has a name, an IP, and a datacenter name."`

		// This field is ignored
		Ignored string `-`

		ForeignKeys struct {
			SomeInt int
		} `naming:"kebab-case" help:"This table has a different naming convention"`
	}

	config := Config{
		Title: "TOML Example",

		Owner: Person{
			Name: "Snaipe",
			DOB:  time.Date(1979, 05, 27, 07, 32, 00, 0, time.FixedZone("", -8*60*60)),
		},

		Database: Database{
			Server:        "192.168.1.1",
			Ports:         []uint16{8001, 8001, 8002},
			ConnectionMax: 5000,
			Enabled:       true,
		},

		Servers: map[string]Server{
			"alpha": Server{
				IP: "10.0.0.1",
				DC: "eqdc10",
			},
			"beta": Server{
				IP: "10.0.0.2",
				DC: "eqdc10",
			},
		},
	}

	f, err := os.Create("testdata/example_save.toml")
	if err != nil {
		log.Fatalln(err)
	}
	defer f.Close()

	if err := boa.NewEncoder(f).Encode(config); err != nil {
		log.Fatalln(err)
	}

	out, err := ioutil.ReadFile("testdata/example_save.toml")
	if err != nil {
		log.Fatalln(err)
	}
	os.Stdout.Write(out)

}
Output:

title = "TOML Example"

[owner]
name = "Snaipe"
dob = 1979-05-27T07:32:00-08:00

[database]
# Database endpoint; can be one of:
# * IPv4
# * IPv6
# * DNS host name.
server = "192.168.1.1"
# Database ports, in the range [1, 65535).
ports = [
  8001,
  8001,
  8002,
]
# Maximum number of connections.
connection_max = 5000
enabled = true

# Set of servers. Each server has a name, an IP, and a datacenter name.
[servers]

  [servers.alpha]
  ip = "10.0.0.1"
  dc = "eqdc10"

  [servers.beta]
  ip = "10.0.0.2"
  dc = "eqdc10"

# This table has a different naming convention
[foreign-keys]
some-int = 0

func NewEncoder

func NewEncoder(out encoding.StatableWriter) *Encoder

NewEncoder returns a new encoder that writes into `out`.

The configuration language of the input file is deduced based on the file extension of its file path. The file-extension-to-decoder mapping is defined by the contents of the Decoders map in this package.

To only use one specific configuration language, do not use this encoder: Use instead the encoder for the chosen language in snai.pe/boa/encoding.

func (*Encoder) Encode

func (enc *Encoder) Encode(v interface{}) error

func (*Encoder) Option

func (enc *Encoder) Option(opts ...interface{}) encoding.Encoder

type EncoderOption

type EncoderOption = encoding.EncoderOption

EncoderOption represents an option common to all encoders in boa.

func Indent

func Indent(indent string) EncoderOption

Indent returns an encoder option that sets the indentation to the specified whitespace string.

func LineBreak

func LineBreak(lb string) EncoderOption

LineBreak sets the line break sequence that must be used when encoding documents.

Can either be "\n" or "\r\n".

type FileSet

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

FileSet represents the combined set of configuration files with a specific name, that are contained within any of the directories in a search path.

func Open

func Open(name string, paths ...fs.FS) *FileSet

Open opens a set of configuration files by name.

The files are searched by matching the stem (i.e. the filename without extension) of the files in the provided search paths to the specified name. If no path is provided, the result of ConfigPaths() is used.

The provided name can be a filename with an extension, or without an extension. It can also include one or more directory components, but must not be an absolute filesystem path; use os.Open instead to load configuration from specific filesystem paths.

Names with an extension will restrict the search to the files matching the specified extension. Conversely, names without an extension will match all files whose stem is the last path component of the filename.

func OpenMultiple

func OpenMultiple(names []string, paths ...fs.FS) *FileSet

OpenMultiple opens a set of configuration files with one or more names.

The order of the `names` slice defines a precedence of files across all search paths. With a single `name`, the behaviour is identical to `Open`.

The iteration order is described in detail in `Next()`.

func (*FileSet) Close

func (cfg *FileSet) Close() error

Close closes the currently opened file.

func (*FileSet) File

func (cfg *FileSet) File() fs.File

File returns the currently opened file.

func (*FileSet) Next

func (cfg *FileSet) Next(exts ...string) error

Next closes the current configuration file if any is opened, and opens the first file in the next path that matches the set of allowed file extensions.

All subsequent calls to Read, Stat, and Close will then apply on the newly opened configuration file.

If all of the search paths have been exhausted, os.ErrNotExist is returned.

When there are multiple names (see OpenMultiple), they will be returned in order first by name and then by path. For instance, if the names slice is "base", "override" and the paths slice is "/etc", "/root/.config" we'll use the ordering:

  • /etc/base.<ext>
  • /root/.config/base.<ext>
  • /etc/override.<ext>
  • /root/.config/override.<ext>

This ordering of the names slice ensures that later entries take precedence over earlier ones, regardless of which directory they're in.

The files are matched in the order of the specified exts slice rather than directory (or lexical) order. For instance, if the extension slice is ".json5", ".json" on a path containing <name>.json5 and <name>.json will open <name>.json5 if it exists, or then <name>.json.

func (*FileSet) Read

func (cfg *FileSet) Read(data []byte) (int, error)

Read reads the contents of the currently opened configuration file into the data slice.

func (*FileSet) Skip

func (cfg *FileSet) Skip()

Skip skips the current search path. This function is particularly useful when all of the matching configurations cannot be opened for any reason, and the application would rather warn but continue parsing configurations in the rest of the paths.

func (*FileSet) Stat

func (cfg *FileSet) Stat() (fs.FileInfo, error)

Stat returns file information of the currently opened configuration file.

func (*FileSet) Used

func (cfg *FileSet) Used() []string

Used returns a slice containing the file names of all files that were opened by calls to Next().

Directories

Path Synopsis
internal
xdg

Jump to

Keyboard shortcuts

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