configape

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Sep 26, 2023 License: MIT Imports: 11 Imported by: 0

README

Config Ape

A go library to make configuration and commandline arguments trivial and convenient and easy to use. You specify your application configuration using a standard golang struct, and struct tags (like json) to specify options for those fields.

Example:

var config = struct {
    Name string `help:"Your name" required:"true"`
    Verbosity int `name:"verbose" help:"Verbosity level" default:"0" cfgtype:"counter"`
    Database struct {
        Host string `help:"Database host"`
        Port int `help:"Database port"`
    }
    // This allows the user to override which config file to read from. By default it is config.json
    ConfigFile string `name:"config" help:"Config file to read from" cfgtype:"configfile"`
    // Any additional arguments on the command line are put into this slice.
    Files []string `cfgtype:"remaining" help:"Files to process"`
}{}
func main() {
    // Checks for config.json, an environment variable named CFG_NAME, and a commandline argument .named --name
    // If you run this as my-program --name=Bob
    configape.Apply(&config, configape.Options{ConfigFile: "config.json"})
    // Output:
    // Hello Bob
    fmt.Printf("Hello %s\n", config.Name)
}

If you add --help, you will automatically get the cli help:

example (v0.0.0)

  --simple-string <SimpleString>
    This is the help for simple string
  --simple-int <SimpleInt> (default: 42)
  --required-string <RequiredString>
  --something <something>
  --short, -s
    This is the help for the short flag
  --config <ConfigFile>
    Load config from this file

database

  --database-host <Host>
    Database host to connect to
  --database-port <Port>
    Database port to use with the database host

Options

The Apply function takes a pointer to a struct, and an optional options struct. The options struct can contain the following fields (full documentation is in the godoc):

Field Description
ConfigFile The name of the config file to read from, if not specified it defaults to config.json
ConfigFileType The type of the config file, if not specified it is automatically detected from the file extension
EnvironmentPrefix The prefix to use for environment variables, if not specified it defaults to CFG_
Help A function to call to display help text, if not specified it defaults to configape.Help
HelpHeader Help text that is prefixed to the help output.
HelpFooter Help text that is appended to the help output.
Name The name of the application, if not specified it defaults to the name of the executable
Version The version of the application, if not specified it defaults to 0.0.0
Writer The writer to use for output, if not specified it defaults to os.Stderr
DisableEnviroment If set to true, then environment variables are not used to set config variables
DisableConfigFile If set to true, then config files are not used to set config variables
DisableCommandLine If set to true, then command line arguments are not used to set config variables
DisableHelp If set to true, then the help text is not displayed to the user
DisableVersion If set to true, then the version text is not displayed to the user
DisableHelpOnMissingRequired If set to true, then the help text is not displayed to the user if a required config variable is missing
AllowUnknownConfigFileKeys If set to true, then unknown keys in the config file are ignored, otherwise an error is returned

Tags

By default all exported struct fields are used and available to be set by the user, the name is automatically calculated by hyphenating the camel case and using lowercase letters (eg HomeDirectory becomes home-directory), you can change this with the name tag (see below).

Tag Description
name The name of the config variable, if not specified the name is calculated from the struct field name, hyphenating the camel case and using lowercase letters (eg HomeDirectory becomes home-directory)
help The help text to display to the user
required If the variable is required, if not set an error is returned
default The default value for the variable
cfgtype The type of the variable, see below for more information
cli Override the cli argument name, by default it is the name value (see defaults for it), set to "-" to disable setting this field via the cli
env The name of the environment variable to use, if not specified the name is calculated by uppercasing the name tag and prepending CFG_. Set to - to disable this config field being set in the environment

Special fields

There are a few special fields specified with cfgtype that can be used in your config struct:

cfgtype Description
configfile The user can specify a config file to read from, see below for more information
counter The user can specify the variable multiple times on the commandline, and the value is incremented each time. Otherwise in config files and environment it can just be set to a number. (You can also specify it's exact value on the commandline by using the --verbose=9 format)

Booleans/flags

If a struct field is of a boolean type, then it is a flag, and specifying --field-name will set it to true. You can also specify --field-name=false to set it to false. If you want to specify a default value, you can use the default tag, eg default:"true".

Config file

Config Ape by default looks for a file called config.json in the current working directory, but you can provide a different file name with the options argument to Apply. The config file can be yaml, json, or toml. The config file is loaded first, and then the environment, followed by the command line arguments. The command line arguments override the environment, and the environment overrides the config file.

If you provide a variable in your config struct that has the type:"configfile" tag, then the user can supply a config file to read from rather than the default. For example:

var config = struct {
    ConfigFile string `cfg:"config",help:"Config file to read from",type:"configfile"`
}{}

Environment

By default all config variables are settable by enviroment variables prefixed with "CFG_" (to avoid name collisions with other environment variables). For example, the config variable Name can be set by the environment variable CFG_NAME. You can change the prefix by setting the EnvPrefix field in the options argument to Apply. You can disable a prefix by setting the EnvPrefix`` to !` (exclamation mark), which is not recommended.

Command line arguments

By default all config variables are settable by command line arguments prefixed with "--" (double dash). For example, the config variable YourHouse can be set by the command line argument --your-house. The library is smart enough to handle many variations. Eg --your-house, --your_house or --YourHouse. You can disable a config from being set via the cli by setting the cli tag to -.

Sections

Config Ape can handle structs within structs, and will automatically create sections for them. For example:

var config = struct {
    Name string `help:"Your name",required:"true"`
    Verbosity int `name:"verbose",help:"Verbosity level",default:"0",cfgtype:"counter"`
    Database struct {
        Host string `help:"Database host"`
        Port int `help:"Database port"`
    }
}{}

In this example, you can set the database host by setting the environment variable CFG_DATABASE_HOST or the command line argument --database-host. You can also set the database port by setting the environment variable CFG_DATABASE_PORT or the command line argument --database-port.

Help

ConfigApe automatically generates help text for you, and displays it if the user specifies --help on the command line. If you wish to handle this yourself, then add a field called Help of type boolean, then check for that being true after calling Apply. You can also use the Help function to output the default help text. For example:

var config = struct {
    Name string `help:"Your name" required:"true"`
    Help bool `cfgtype:"help" help:"Show help"`
}{}
cfgape.Apply(&config, nil)
if config.Help {
    fmt.Print(configape.Help(&config))
    os.Exit(0)
}

Documentation

Overview

A unified configuration/environment/cli parser for Go, with a focus on simplicity and ease of use.

Example
package main

import (
	"fmt"
	"os"

	"github.com/zafnz/configape"
)

func main() {
	cfg := struct {
		Foo       string `name:"foo" default:"baz" help:"This is the help for foo"`
		Bar       int    `default:"42"`
		Test      bool   `default:"true"`
		Verbosity int    `name:"verbose" short:"v" default:"0" cfgtype:"counter" help:"Verbosity level"`
		Baz       string
	}{}
	// Fake the os arguments, here we use --no-test to override
	// the default true value of test, and we increment verbosity twice
	os.Args = []string{"test", "--foo", "bar", "--no-test", "-v", "-v"}
	os.Setenv("CFG_BAZ", "environment")

	configape.Apply(&cfg, nil)

	fmt.Printf("Foo: %s\n", cfg.Foo)
	fmt.Printf("Bar: %d\n", cfg.Bar)
	fmt.Printf("Test: %t\n", cfg.Test)
	fmt.Printf("Baz: %s\n", cfg.Baz)
	fmt.Printf("Verbosity: %d\n", cfg.Verbosity)

}
Output:

Foo: bar
Bar: 42
Test: false
Baz: environment
Verbosity: 2
Example (Complex)
package main

import (
	"fmt"
	"os"

	"github.com/zafnz/configape"
)

// This example shows how to use configape to parse a complex configuration

// Define a struct to hold the configuration
type ComplexConfig struct {
	// The name tag is used to map the configuration to the struct
	// field. If the name tag is not specified, the field name is
	// used instead.
	// The default tag is used to specify the default value for the
	// field.
	// The help tag is used to specify the help text for the field.
	// The cfgtype tag is used to specify the type of the field.
	// The short tag is used to specify the short name for the field.
	// The env tag is used to specify the environment variable name
	// for the field.
	// The required tag is used to specify that the field must be
	// specified.

	// This can be set with --simple-string or CFG_SIMPLE_STRING
	// or "SimpleString" in the config file.
	SimpleString   string `help:"This is the help for simple string"`
	SimpleInt      int    `default:"42"`     // This will default to 42
	RequiredString string `required:"true"`  // This must be specified
	Different      string `name:"something"` // This can be set with --something or CFG_SOMETHING
	// Here this can be set with --short or -s.
	ShortFlag bool   `name:"short" short:"s" help:"This is the help for the short flag"`
	NotOnCli  string `cli:"-"` // This will not be on the cli, but is settable in the config file

	ConfigFile string `cli:"config" cfgtype:"configfile" help:"Load config from this file"`

	// Example of a sub-configuration. Here all of these settings
	// can be set with a prefix of "--database--" on the cli, or
	// as an object in the config file.
	Database struct {
		// Set with --database-host or CFG_DATABASE_HOST
		Host string `help:"Database host"`
		// Set with --database-port or CFG_DATABASE_PORT
		Port int `help:"Database port"`
	}
}

// Here we can see how to apply the configuration to the struct.
// Note that because we haven't specified a specific name, we can
// use camel case, or underscores, or dashes, for those fields.
var cfgFile = `
{
	"simple_string": "foo",
	"SimpleInt": 69,
	"required_string": "bar",
	"not_on_cli": "set in config file"
}`

func main() {
	// Write out the config as a temporary file to use
	fh, _ := os.CreateTemp("", "configape")
	defer os.Remove(fh.Name())
	fh.WriteString(cfgFile)

	os.Args = []string{"test", "--something", "set on cli", "--database-host", "dbhost", "-s", "--config", fh.Name()}

	cfg := ComplexConfig{}
	err := configape.Apply(&cfg, nil)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Printf("SimpleString: %s\n", cfg.SimpleString)
	fmt.Printf("SimpleInt: %d\n", cfg.SimpleInt)
	fmt.Printf("RequiredString: %s\n", cfg.RequiredString)
	fmt.Printf("Different: %s\n", cfg.Different)
	fmt.Printf("ShortFlag: %t\n", cfg.ShortFlag)
	fmt.Printf("NotOnCli: %s\n", cfg.NotOnCli)
	fmt.Printf("Database.Host: %s\n", cfg.Database.Host)

}
Output:

SimpleString: foo
SimpleInt: 69
RequiredString: bar
Different: set on cli
ShortFlag: true
NotOnCli: set in config file
Database.Host: dbhost
Example (Help)
package main

import (
	"fmt"

	"github.com/zafnz/configape"
)

// This example shows how to use configape to parse a complex configuration

// Define a struct to hold the configuration
type HelpExampleConfig struct {
	// This is the same as the complex example, see that for an explanation
	// of all the tags.
	SimpleString   string `help:"This is the help for simple string"`
	SimpleInt      int    `default:"42"`
	RequiredString string `required:"true"`
	Different      string `name:"something"`
	ShortFlag      bool   `name:"short" short:"s" help:"This is the help for the short flag"`
	NotOnCli       string `cli:"-"`

	ConfigFile string `cli:"config" cfgtype:"configfile" help:"Load config from this file"`

	Database struct {
		// Set with --database-host or CFG_DATABASE_HOST
		Host string `help:"Database host to connect to"`
		// Set with --database-port or CFG_DATABASE_PORT
		Port int `help:"Database port to use with the database host"`
	}
}

func main() {
	cfg := HelpExampleConfig{}
	help, _ := configape.Help(cfg, &configape.Options{Name: "example"})
	fmt.Println(help)

}
Output:

example (v0.0.0)

  --simple-string <SimpleString>
    This is the help for simple string
  --simple-int <SimpleInt> (default: 42)
  --required-string <RequiredString>
  --something <something>
  --short, -s
    This is the help for the short flag
  --config <ConfigFile>
    Load config from this file

database

  --database-host <Host>
    Database host to connect to
  --database-port <Port>
    Database port to use with the database host

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Apply

func Apply(cfg interface{}, options *Options) error

Apply the configuration to the provided cfg struct, using the options provided.

Example
package main

import (
	"fmt"
	"os"

	"github.com/zafnz/configape"
)

func main() {
	cfg := struct {
		Foo       string `name:"foo" default:"baz" help:"This is the help for foo"`
		Bar       int    `default:"42"`
		Test      bool   `default:"true"`
		Verbosity int    `name:"verbose" short:"v" default:"0" cfgtype:"counter" help:"Verbosity level"`
	}{}
	// Fake the os arguments, here we use --no-test to override
	// the default true value of test, and we increment verbosity twice
	os.Args = []string{"test", "--foo", "bar", "--no-test", "-v", "-v"}

	configape.Apply(&cfg, nil)

	fmt.Printf("Foo: %s\n", cfg.Foo)
	fmt.Printf("Bar: %d\n", cfg.Bar)
	fmt.Printf("Test: %t\n", cfg.Test)
	fmt.Printf("Verbosity: %d\n", cfg.Verbosity)

}
Output:

Foo: bar
Bar: 42
Test: false
Verbosity: 2

func Help

func Help(cfg interface{}, options *Options) (string, error)

Returns a string suitable as the output for the help command. You can override the default output using the HelpWriter or Help function in the Options struct. Or you can disable help entirely with DisableHelp

Example
package main

import (
	"os"

	"github.com/zafnz/configape"
)

func main() {
	cfg := struct {
		Foo string `name:"foo" default:"baz" help:"This is the help for foo"`
	}{}
	options := configape.Options{
		HelpWriter: os.Stdout, // By default goes to Stderr
		Name:       "my-prog",
		Version:    "1.2.3",
	}
	os.Args = []string{"test", "--help"}

	configape.Apply(&cfg, &options)
}
Output:

my-prog (v1.2.3)

  --foo <foo> (default: baz)
    This is the help for foo

Types

type Options

type Options struct {
	ConfigFilename         string // Name of the config file to use
	ConfigFileType         string // The file type, defaults to json and determines the file extension.
	EnvironmentPrefix      string // Prefix for environment variables, empty string defaults to CFG_, if you really want no prefix, set to ! (not recommended)
	UseSingleDashArguments bool   // If set, then arguments are expected as "-foo bar" instead of "--foo bar" (not recommended)

	Help                         func(str string) // If set, then this function will be called when the help flag is set.
	HelpHeader                   string           // Help text that is prefixed to the help output.
	HelpFooter                   string           // Help text that is appended to the help output.
	HelpWriter                   io.Writer        // Where to write the help output, defaults to os.Stderr
	DisableHelpOnMissingRequired bool             // If set, then the help will not be printed if a required setting is missing.
	DisableHelp                  bool             // Disable the help flag
	DisableVersion               bool             // Disable the version flag

	Name    string // Name of the program, used in the help output. Defaults to os.Args[0]
	Version string // Version of the program, used in the help output. Defaults to "v0.0.0"

	DisableEnviornment bool // Disable environment variables
	DisableConfigFile  bool // Disable config file parsing
	DisableCommandLine bool // Disable command line parsing

	AllowUnknownConfigFileKeys bool // If set, then unknown keys in the config file will not cause an error.
	// contains filtered or unexported fields
}

Options on how Config Ape should work.

Jump to

Keyboard shortcuts

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