gofigure

package module
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Jun 14, 2023 License: MIT Imports: 13 Imported by: 3

README

Go figure!

Build Issues Pull Requests Go Doc License

A configuration tool that will allow configuration from the command line, environment variable, local config file, or a remote configure file. You give it the details and just say "Go figure!".

Usage

go get -u bitbucket.org/idomdavis/gofigure

See gofigure_test.go for example usage.

Documentation

Overview

Package gofigure allows for configuration of an application using command line flags, environment variables and configuration files.

Example
package main

import (
	"fmt"
	"time"

	"bitbucket.org/idomdavis/gofigure"
)

func main() {
	var settings struct {
		Mode    int
		Name    string
		Address string
		Timeout time.Duration
		TLS     bool

		Duration time.Duration
		Int      int
		Float    float64
	}

	config := gofigure.NewConfiguration("EXAMPLE")
	config.AddHelp(gofigure.CommandLine)
	config.AddConfigFile(gofigure.CommandLine)

	group := config.Group("settings")

	group.Add(gofigure.Required("App Name", "name", &settings.Name,
		gofigure.AllSources, gofigure.ReportValue, "Application name"))
	group.Add(gofigure.Required("Mode", "mode", &settings.Mode, gofigure.Flag,
		gofigure.ReportValue, "Mode indicator"))
	group.Add(gofigure.Optional("IP Address", "address", &settings.Address,
		"", gofigure.AllSources, gofigure.MaskUnset, "Remote server address"))
	group.Add(gofigure.Optional("Timeout", "timeout", &settings.Timeout,
		time.Minute, gofigure.AllSources, gofigure.ReportValue,
		"Remote server address"))
	group.Add(gofigure.Optional("TLS", "tls", &settings.TLS, false,
		gofigure.NamedSources, gofigure.ReportValue, "Use TLS"))

	types := config.Group("JSON types")

	types.Add(gofigure.Required("Duration", "duration", &settings.Duration,
		gofigure.Key, gofigure.ReportValue, "Duration type"))
	types.Add(gofigure.Required("Int", "int", &settings.Int,
		gofigure.Key, gofigure.ReportValue, "int type"))
	types.Add(gofigure.Required("Float", "float", &settings.Float,
		gofigure.Key, gofigure.ReportValue, "float type"))

	// Ordinarily this would be config.Parse().
	err := config.ParseUsing([]string{
		"-c", "testdata/config.json",
		"--mode", "3",
		"--name", "example",
		"-h",
	})

	if err != nil {
		fmt.Println(config.Format(err))
		fmt.Println(config.Usage())
	}

	fmt.Printf("%v\n\n", settings)

	if config.Help {
		fmt.Println(config.Usage())
	}

}
Output:

{3 example localhost:8000 1m0s false 1h0m0s 0 0}

usage:
  Help [-h, --help]
    Display usage information

  Config File [-c, --config]
    Provide configuration from an external JSON file

  App Name [JSON key: "name", env EXAMPLE_NAME, -n, --name]
    Application name (required)

  Mode [--mode]
    Mode indicator (required)

  IP Address [JSON key: "address", env EXAMPLE_ADDRESS, -a, --address]
    Remote server address

  Timeout [JSON key: "timeout", env EXAMPLE_TIMEOUT, -t, --timeout]
    Remote server address (default: 1m0s)

  TLS [JSON key: "tls", env EXAMPLE_TLS, --tls]
    Use TLS (default: false)

  Duration [JSON key: "duration"]
    Duration type (required)

  Int [JSON key: "int"]
    int type (required)

  Float [JSON key: "float"]
    float type (required)

Index

Examples

Constants

View Source
const (
	// HideSet will not report the value if it's set.
	HideSet = Mask(1 << iota)

	// HideUnset will not report anything if the value is not set.
	HideUnset = Mask(1 << iota)

	// MaskSet will report Set rather than the definition value.
	MaskSet = Mask(1 << iota)

	// MaskUnset will report Unset rather than the definition value.
	MaskUnset = Mask(1 << iota)

	// DefaultIsSet sets the behaviour of Mask to treat a default value as
	// Set rather than NotSet.
	DefaultIsSet = Mask(1 << iota)

	// ReportValue will report the definition value.
	ReportValue = 0

	// HideValue will not report anything for the definition.
	HideValue = HideSet | HideUnset

	// MaskValue will report Set or Unset rather than the definition value.
	MaskValue = MaskSet | MaskUnset
)
View Source
const (
	// Set is used when MaskSet is specified.
	Set = "SET"

	// NotSet is used when MaskNotSet is specified.
	NotSet = "UNSET"

	// Invalid is used when a Value is invalid.
	Invalid = "INVALID"
)
View Source
const (
	// None indicates no source has set the Value.
	None = Source(1 << iota)

	// A Default Value has been used. No other sources have overwritten this.
	Default = Source(1 << iota)

	// A Key in a JSON file. Can be used either to indicate the Value can be set
	// via this Key, or has been set via this Key.
	Key = Source(1 << iota)

	// An EnvVar or environment variable. Can be used either to indicate the
	// Value can be set via this environment variable, or that it has been set
	// via this environment variable.
	EnvVar = Source(1 << iota)

	// A ShortFlag on the command line. Can be used either to indicate the
	// Value can be set via this flag, or that it has been set via this flag.
	ShortFlag = Source(1 << iota)

	// A Flag on the command line. Can be used either to indicate the
	// Value can be set via this flag, or that it has been set via this flag.
	Flag = Source(1 << iota)

	// Reference parameter that cannot be set. Reference parameters are used to
	// provide output in a report without being settable beyond the default
	// value. By definition Reference parameters must be Optional.
	Reference = Source(1 << iota)
)
View Source
const (
	// CommandLine indicates an option can come from a short or long flag.
	CommandLine = Flag | ShortFlag

	// NamedSources indicates an option should be set from a flag, environment
	// variable, or JSON key.
	NamedSources = Flag | EnvVar | Key

	// AllSources indicates an option should be set from a short flag, flag,
	// environment variable, or JSON key.
	AllSources = NamedSources | ShortFlag
)
View Source
const Dev = "dev"

Dev identifier, used as a default when the Identifier is unset and cannot be inferred.

View Source
const Unset = "<unset>"

Unset is used when version data is unset and cannot be inferred.

Variables

View Source
var (
	Identifier = Dev
	BuildTime  = Unset
	CommitHash = Unset
)

Build identifiers. These are given sensible defaults, but can be overridden when an application is compiled using -ldflags. Within a Makefile this may look like:

 PACKAGE = bitbucket.org/idomdavis/gofigure
 BUILD_TIME = $(shell date +"%Y/%m/%d-%H:%M:%S")
 HASH = $(shell git rev-parse HEAD)

 ifdef BITBUCKET_TAG
 TAG = $(BITBUCKET_TAG)
 else ifdef BITBUCKET_BRANCH
 TAG = $(BITBUCKET_BRANCH)
 else
 TAG = $(shell git rev-parse --abbrev-ref HEAD)
 endif

 ifeq (, $(TAG))
 TAG = dev
 endif

 LDFLAGS = -X $(PACKAGE).BuildTime=$(BUILD_TIME) \
	-X $(PACKAGE).CommitHash=$(HASH) \
	-X $(PACKAGE).Identifier=$(TAG)

 build:
 	go build -ldflags "$(LDFLAGS)"

The identifiers are used for reference only and have no impact on the operation of gofigure.

View Source
var (
	ErrMissingName        = errors.New("value for Value.Name is empty")
	ErrMissingDescription = errors.New("value for Value.Description is empty")
	ErrNilPointer         = errors.New("value for Value.Ptr is nil")
	ErrInvalidType        = errors.New("invalid type")
)

Value validation errors.

View Source
var ErrInvalidValue = errors.New("invalid value")

ErrInvalidValue is used when an option can't be mapped.

View Source
var ErrLoadingConfig = errors.New("error loading config")

ErrLoadingConfig is given to a ConfigError if Load fails.

View Source
var ErrLoadingJSON = errors.New("error loading JSON")

ErrLoadingJSON is returned if an external file cannot be read from the path or URL given.

View Source
var ErrMissingRequiredOption = errors.New("missing required option")

ErrMissingRequiredOption is returned if a required option has not been set after parsing.

View Source
var ErrParsingJSON = errors.New("error parsing JSON")

ErrParsingJSON is returned if the external file is not valid JSON.

View Source
var ErrUnexpectedArgument = errors.New("unexpected argument")

ErrUnexpectedArgument is returned if an unexpected argument is passed.

Functions

func Assign

func Assign[T Type](target *T, value any) (err error)

Assign the value to the target, returning an error if assignment fails. Assign will attempt to coerce string values to the correct type.

func Build added in v0.1.1

func Build() string

Build returns a build string for the application. This relies on the correct ldflags being set.

Example
package main

import (
	"fmt"

	"bitbucket.org/idomdavis/gofigure"
)

func main() {
	fmt.Println(gofigure.Build())

}
Output:

dev [<unset>] (built: <unset>)

func Cast added in v0.2.6

func Cast(value, typeOf any) any

Cast a float64 value to an integer value. If the value isn't a float64, or the type isn't an integer then Cast will simply return the value.

Example
package main

import (
	"fmt"

	"bitbucket.org/idomdavis/gofigure"
)

func main() {
	fmt.Printf("%T: %[1]v\n", gofigure.Cast(1.0, 0))
	fmt.Printf("%T: %[1]v\n", gofigure.Cast(8.0, int8(0)))
	fmt.Printf("%T: %[1]v\n", gofigure.Cast(16.0, int16(0)))
	fmt.Printf("%T: %[1]v\n", gofigure.Cast(32.0, int32(0)))
	fmt.Printf("%T: %[1]v\n", gofigure.Cast(64.0, int64(0)))
	fmt.Printf("%T: %[1]v\n", gofigure.Cast(1.0, uint(0)))
	fmt.Printf("%T: %[1]v\n", gofigure.Cast(8.0, uint8(0)))
	fmt.Printf("%T: %[1]v\n", gofigure.Cast(16.0, uint16(0)))
	fmt.Printf("%T: %[1]v\n", gofigure.Cast(32.0, uint32(0)))
	fmt.Printf("%T: %[1]v\n", gofigure.Cast(64.0, uint64(0)))
	fmt.Printf("%T: %[1]v\n", gofigure.Cast(float32(1.0), float32(0)))
	fmt.Printf("%T: %[1]v\n", gofigure.Cast(1.0, float64(0)))
	fmt.Printf("%T: %[1]v\n", gofigure.Cast("1.0", 1))

}
Output:

int: 1
int8: 8
int16: 16
int32: 32
int64: 64
uint: 1
uint8: 8
uint16: 16
uint32: 32
uint64: 64
float32: 1
float64: 1
string: 1.0

func Coerce

func Coerce(value, typeOf any) (r any, err error)

Coerce will coerce string values to the correct type. All other value types are simply returned as is. If the string value cannot be coerced to the type an error is returned.

Example
package main

import (
	"fmt"
	"time"

	"bitbucket.org/idomdavis/gofigure"
)

func main() {
	fmt.Println(gofigure.Coerce(1, 0))
	fmt.Println(gofigure.Coerce("1m0s", time.Nanosecond))
	fmt.Println(gofigure.Coerce("true", false))
	fmt.Println(gofigure.Coerce("1", 0))
	fmt.Println(gofigure.Coerce("8", int8(0)))
	fmt.Println(gofigure.Coerce("16", int16(0)))
	fmt.Println(gofigure.Coerce("32", int32(0)))
	fmt.Println(gofigure.Coerce("64", int64(0)))
	fmt.Println(gofigure.Coerce("1", uint(0)))
	fmt.Println(gofigure.Coerce("8", uint8(0)))
	fmt.Println(gofigure.Coerce("16", uint16(0)))
	fmt.Println(gofigure.Coerce("32", uint32(0)))
	fmt.Println(gofigure.Coerce("64", uint64(0)))
	fmt.Println(gofigure.Coerce("1", float32(0)))
	fmt.Println(gofigure.Coerce("6.4", float64(0)))
	fmt.Println(gofigure.Coerce("1", uintptr(0)))

}
Output:

1 <nil>
1m0s <nil>
true <nil>
1 <nil>
8 <nil>
16 <nil>
32 <nil>
64 <nil>
1 <nil>
8 <nil>
16 <nil>
32 <nil>
64 <nil>
1 <nil>
6.4 <nil>
1 cannot coerce "1": invalid type: uintptr

func Dereference

func Dereference(in any) any

Dereference a value. If the value isn't a pointer then it is returned as is.

Example
package main

import (
	"fmt"

	"bitbucket.org/idomdavis/gofigure"
)

func main() {
	i := 1

	fmt.Println(gofigure.Dereference(i))
	fmt.Println(gofigure.Dereference(&i))

}
Output:

1
1

func Get added in v0.2.5

func Get(uri string) (map[string]any, error)

Get a JSON object from an external source.

Types

type ConfigError

type ConfigError struct {
	Cause    error
	Internal error

	Parameters Parameters
	Value      any
}

ConfigError holds data about what specifically caused configuration to fail. This can e used to generate a user-friendly report.

func NewConfigError

func NewConfigError(cause, internal error, parameters ...Parameter) ConfigError

NewConfigError will return a new ConfigError for the given errors and Parameters.

func (ConfigError) Error

func (c ConfigError) Error() string

func (ConfigError) Format

func (c ConfigError) Format(prefix string) string

Format the error in a user-centric way.

func (ConfigError) Unwrap

func (c ConfigError) Unwrap() error

type Configuration

type Configuration struct {
	Help   bool
	Prefix string
	Groups []*Group
	// contains filtered or unexported fields
}

Configuration for a program.

func NewConfiguration

func NewConfiguration(envPrefix string) *Configuration

NewConfiguration returns a new Configuration set to use the given prefix for environment variables.

func (*Configuration) AddConfigFile added in v0.2.4

func (c *Configuration) AddConfigFile(sources Source)

AddConfigFile will add a "config" option to the set of options. If ShortFlag is set on the sources then a short flag of 'h' is also added. All other sources are ignored. The provided value will be used as a path or URI to load and external configuration file from.

func (*Configuration) AddHelp

func (c *Configuration) AddHelp(sources Source)

AddHelp will add a "help" flag to the set of options. If ShortFlag is set on the sources then a short flag of 'h' is also added. All other sources are ignored.

func (*Configuration) Format

func (c *Configuration) Format(err error) string

Format an error for user consumption. This will remove most of the technical details and leave a simple message as to why the configuration failed. Format should be used to report any errors to the user.

func (*Configuration) Group

func (c *Configuration) Group(name string) *Group

Group of Definitions for this Configuration.

func (*Configuration) Parse

func (c *Configuration) Parse() error

Parse the Options.

func (*Configuration) ParseUsing

func (c *Configuration) ParseUsing(args []string) error

ParseUsing uses the given arguments as the set of command line arguments.

func (*Configuration) Report added in v0.2.0

func (c *Configuration) Report() Report

Report on the configuration, returning the values in a format that can be displayed to the user. Empty groups will be stripped from the Report. Values in the Report will respect the Mask setting.

func (*Configuration) Usage

func (c *Configuration) Usage() string

Usage string for this set of Options.

type External

type External string

External types hold a path to an external configuration file.

type Group

type Group struct {
	Name     string
	Settings []*Setting
}

Group of Settings.

func (*Group) Add

func (g *Group) Add(setting *Setting)

Add a Setting to the Group.

func (*Group) Values

func (g *Group) Values() map[string]any

Values set on this group. Values will strip any Setting with a Mask that indicates it should be hidden.

Example
package main

import (
	"fmt"

	"bitbucket.org/idomdavis/gofigure"
)

func main() {
	var a, b string

	g := gofigure.Group{}

	g.Add(gofigure.Required("a", "a", &a, gofigure.Flag, gofigure.HideUnset, "a"))
	g.Add(gofigure.Required("b", "b", &b, gofigure.Flag, gofigure.MaskValue, "b"))

	fmt.Println(g.Values())

}
Output:

map[b:UNSET]

type Line added in v0.2.0

type Line struct {
	Name   string
	Values map[string]any
}

Line in a Report. The values in the Line will respect Mask settings.

type Mask

type Mask uint8

Mask definition values when they are logged.

func (Mask) Contains

func (m Mask) Contains(mask Mask) bool

Contains returns true if the Mask contains the given Mask.

func (Mask) String

func (m Mask) String() string
Example
package main

import (
	"fmt"

	"bitbucket.org/idomdavis/gofigure"
)

func main() {
	fmt.Println(gofigure.HideValue)
	fmt.Println(gofigure.MaskValue)
	fmt.Println(gofigure.DefaultIsSet)

}
Output:

Hide value
Mask value
Mask value: 16

type Options

type Options map[Parameter]any

Options use to configure an application.

func Environment

func Environment(prefix string, settings Settings) Options

Environment Options defined by the Settings.

func Flags

func Flags(args []string) (Options, error)

Flags will build a set of Options from the given argument list.

Example
package main

import (
	"fmt"

	"bitbucket.org/idomdavis/gofigure"
)

func main() {
	flags, err := gofigure.Flags([]string{"-v", "--name", "example"})

	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(flags)

}
Output:

[name:example, v:true]

func Load

func Load(uri string) (Options, error)

Load external Options from a URI. The external file can be any JSON object.

Example
package main

import (
	"fmt"

	"bitbucket.org/idomdavis/gofigure"
)

func main() {
	d, err := gofigure.Load("testdata/config.json")

	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(d)

}
Output:

[address:localhost:8000, duration:1h, float:0, int:0, name:overridden]

func (Options) String

func (o Options) String() string

type Parameter

type Parameter struct {
	Name   string
	Source Source

	Stub string
}

Parameter used to set a Value.

func (Parameter) FullName

func (p Parameter) FullName() string

FullName returns the fully formatted name for the parameter. For most parameter types this is just the name. For Environment variables the prefix is appended if there is one, and all -'s are converted to _'s.

func (Parameter) Matches

func (p Parameter) Matches(parameter Parameter) bool

Matches returns true if the Argument matches the Parameter.

Example
package main

import (
	"fmt"

	"bitbucket.org/idomdavis/gofigure"
)

func main() {
	a := gofigure.Parameter{Name: "param", Source: gofigure.AllSources}
	b := gofigure.Parameter{Name: "param", Source: gofigure.Flag}
	c := gofigure.Parameter{Name: "param", Source: gofigure.ShortFlag}

	fmt.Println(a.Matches(b))
	fmt.Println(b.Matches(c))
	fmt.Println(b.Matches(a))
	fmt.Println(c.Matches(b))

}
Output:

true
false
true
false

func (Parameter) String

func (p Parameter) String() string

type Parameters

type Parameters []Parameter

Parameters that can be used to set a Value.

func NewParameters

func NewParameters(name string, sources Source) Parameters

NewParameters returns a set of named parameters for the given sources. Combine multiple sources with | (e.g. Flag | EnvVar). The given name is used for each source with Flag and Key using the name as is, EnvSuffix set to the uppercase version of the name, and ShortFlag set to the first character of name.

Example
package main

import (
	"fmt"

	"bitbucket.org/idomdavis/gofigure"
)

func main() {
	fmt.Println(gofigure.NewParameters("param", gofigure.AllSources))

}
Output:

[JSON key: "param" env PARAM -p --param]

func (Parameters) Format

func (p Parameters) Format(prefix string) string

Format the given Parameters into a human-readable string.

Example
package main

import (
	"fmt"

	"bitbucket.org/idomdavis/gofigure"
)

func main() {
	p := gofigure.NewParameters("param", gofigure.AllSources)

	fmt.Println(p.Format("STUB"))

}
Output:

[JSON key: "param", env STUB_PARAM, -p, --param]

type Report added in v0.2.0

type Report []Line

Report holds the setting configuration in a way that can be reported to the user. Values in the Report will respect Mask settings.

type Setting

type Setting struct {
	Value      *Value
	Parameters Parameters
	Mask       Mask
}

Setting in a Configuration. A Setting takes values from a set of Parameters and applies them to a Value. The Mask is used when generating a Display value.

func Optional

func Optional[T Type](name, param string, ptr *T, value T, sources Source,
	mask Mask, description string) *Setting

Optional Setting uses the given default value if no value is provided via its parameters. The parameters are constructed using the param value and the defined sources. Combine multiple sources with | (e.g. Flag | EnvVar). The given name is used for each source with Flag and Key using the name as is, EnvSuffix set to the uppercase version of the name, and ShortFlag set to the first character of name.

func Required

func Required[T Type](name, param string, ptr *T, sources Source, mask Mask,
	description string) *Setting

Required Setting must be set via one of its Parameters. The parameters are constructed using the param value and the defined sources. Combine multiple sources with | (e.g. Flag | EnvVar). The given name is used for each source with Flag and Key using the name as is, EnvSuffix set to the uppercase version of the name, and ShortFlag set to the first character of name.

func (Setting) Accepts

func (s Setting) Accepts(parameter Parameter) bool

Accepts returns true if this Setting accepts the given Parameter.

func (Setting) Display

func (s Setting) Display() (string, bool)

Display string for the Setting. The string should only be displayed if Display returns true, otherwise it should be hidden.

type Settings

type Settings []*Setting

func (Settings) Apply

func (s Settings) Apply(parameter Parameter, value any) error

Apply the Parameter to the correct Setting in the set. Apply will return an error if the relevant Setting cannot be set, or if no Settings have been set.

func (Settings) External

func (s Settings) External() []string

External configuration file paths defined by these settings.

func (Settings) Map

func (s Settings) Map(options map[Parameter]any, ignore ...error) error

Map the options to the settings, ignoring any errors provided.

type Source

type Source uint8

Source of a Value, either accepted sources (i.e. what sources set a Value), or defining Source that set the Value. Combine multiple sources with | when defining accepted sources (e.g. Flag | EnvVar).

func (Source) Contains

func (s Source) Contains(source Source) bool

Contains returns true if the Source contains the given Source.

Example
package main

import (
	"fmt"

	"bitbucket.org/idomdavis/gofigure"
)

func main() {
	sources := gofigure.Flag | gofigure.ShortFlag

	fmt.Println(sources.Contains(gofigure.Flag))
	fmt.Println(sources.Contains(gofigure.Key))

	sources = gofigure.AllSources

	fmt.Println(sources.Contains(gofigure.Key))

}
Output:

true
false
true

func (Source) String

func (s Source) String() string
Example
package main

import (
	"fmt"
	"math"

	"bitbucket.org/idomdavis/gofigure"
)

func main() {
	fmt.Println(gofigure.None)
	fmt.Println(gofigure.Default)
	fmt.Println(gofigure.Key)
	fmt.Println(gofigure.EnvVar)
	fmt.Println(gofigure.ShortFlag)
	fmt.Println(gofigure.Flag)
	fmt.Println(gofigure.Reference)
	fmt.Println(gofigure.Source(math.MaxUint8))
	fmt.Println(gofigure.Source(0))

}
Output:

none
default value
config file key
environment variable
short flag
flag
reference value
config file
source

type Type

type Type interface {
	~bool | ~float32 | ~float64 | ~string |
		~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}

Type accepted by gofigure.

type Value

type Value struct {
	Name        string
	Description string
	Ptr         any

	Source Source
	// contains filtered or unexported fields
}

A Value is used to hold a configured value. The Value must be a pointer to the variable being set, and must satisfy Type. Once set the Value will contain the Source that provided the value.

func NewValue

func NewValue[T Type](name string, ptr *T, value T, source Source, description string) *Value

NewValue returns a new, valid value. An empty name, description, or an invalid ptr will result in a panic.

func (*Value) Assign

func (v *Value) Assign(value any, source Source) error

Assign a value to the Value.Ptr, returning an error if the assignment cannot be made.

func (*Value) Validate

func (v *Value) Validate() error

Validate the setting returning an error if the Value lacks a name or description, if the Ptr is nil, or if the Ptr is of the incorrect type.

Jump to

Keyboard shortcuts

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