tri: github.com/parallelcointeam/tri Index | Files

package tri

import "github.com/parallelcointeam/tri"

Package Tri is a library for defining the configuration parameters for a CLI application, managing the configuration files, application data directory, defining subcommands, parameters, default values and automatically loading configuration structures with the processed configuration.

The primary construct used in Go to implement generic types is the interface, and to implement any kind of structure that involves lists, the slice-of-interface []interface{} is a structure that can hold zero or more items of any type whatsoever.

By deriving from this primary primitive using names, the type aliases can become metadata that can be used to specify how its contents are to be interpreted.

Implementation Notes

In this implementation you can see I take advantage of the possibility to put any type into the list to place string labels at the heads of the lists as identifiers, which then can form a tagged tree structure that can be addressed by providing a list of the identifier strings at each node.

Go's strict static typing means that such hierarchies cannot be written without a complete set of types already pre-defined, so in each case the implementation has to be written specifically for the types of data used.

In spite of it being perhaps less logical, all elements of a Tri are a derivative of a Tri. This way one never has to type assert the container, only the contents, and in most cases, the possible types depend on the type of a branch.

Declaration Syntax Validation

Validators for each type in this package automatically trigger the validators for their (valid set of) constituent elements, so only one Valid() invocation is needed, on the Tri type. If Valid functions return an error, it passes back through the cascade to the root where it is printed to tell (the proogrammer) that their declaration was erroneous.

Without validity checks, in a strict static typing language, dynamic variables can cause errors when assuming the input is correct, and such errors can potentially escape notice for a long time, so by adding these runtime bounds and set membership tests, such errors are caught before the application they configure even starts execution of the main() - the Tri type's Valid() method should be invoked in a init() so it runs after the calls in any var blocks and before the main() tries to parse the CLI flags.

NOTE: all types use the Tri []interface{} type because, while it is possible to omit fields by using field tags, if the object is another composite object, its type must be specified unless it is an array and the members are implicitly typed. By using interface slice instead, the declaration syntax has minimum redundant type specifications.

Index

Package Files

parser.go types.go validators.go

func LoadAllDefaults Uses

func LoadAllDefaults(t *Tri)

LoadAllDefaults walks a Tri and calls LoadDefaults on each one for the first step in composition of configuration

func LoadDefaults Uses

func LoadDefaults(v *Var) (found bool)

LoadDefaults reads the Default (if any) in a Var, and copies the value into the Slot, returns true if there was a Default and it was filled

func ValidName Uses

func ValidName(s string) error

ValidName checks that a Tri name element that should be a name only contains letters.

type Branch Uses

type Branch interface {
    Validate() error
}

Branch is an interface that all Tri nodes implement to do runtime checks on the contents of a Tri derived type, basically something like Assert for the constraints required of the subtype.

The validation method is used at runtime and basically is a declaration syntax check, and only the root Valid() function must be called, it does the rest and returns as soon as it finds an error when walking the tree, or proceeds to next step of reading CLI args and compositing defaults, config file, env together filling the configuration structure.

This validation has no benefit if the Tri declaration is valid, but if it is not checked, the parse/configure process will almost certainly panic when it finds the wrong type of object in the wrong position, so it may seem overly verbose but this ensures that no further checking is required before parsing the several inputs that result in a filled out configuration struct.

Constraints are not the most visible feature of generic types, but if constraints aren't applied to generic types they can be very hard bugs to spot, and simply do not occur if you first check the parts of the structures are correct.

type Brief Uses

type Brief Tri

Brief is a short description up to 80 characters long containing one string with no control characters, that is intended to describe the item it is embedded in.

func (*Brief) Validate Uses

func (r *Brief) Validate() error

Validate checks to ensure the contents of this node type satisfy constraints. Brief only contains one thing, so we make sure it has it - one string. This string may not contain any type of control characters, and is limited to 80 characters in length.

type Command Uses

type Command Tri

Command is the specification for an individual subcommand. Shown below is the full set of allowable items, the metadata items may only appear once, there must be a Brief, the name at the start, and a Handler function.

{"name",
	Short{"c"}, // single character shortcut for full length name
	Brief{"brief"},
	Usage{"usage"},
	Help{"help"},
	Examples{
		"example 1", "explaining text",
		...
	},
	Var{...
	},
	Trigger{...
	},
	func(Tri) int {
		...
		return 0
	},
}

func (*Command) Validate Uses

func (r *Command) Validate() error

Validate checks to ensure the contents of this node type satisfy constraints. This validator only has to check the elements of the slice are zero or more Command items, and a valid name at index 0.

type Commands Uses

type Commands []Command

Commands is just an array of Command, providing a symbol-free and human-friendly name for the array of commands in an application declaration.

func (*Commands) Validate Uses

func (r *Commands) Validate() error

Validate checks to ensure the contents of this node type satisfy constraints. This validator only triggers the validator on its elements.

type Default Uses

type Default Tri

Default is specifies the default value for a Variable, it must contain only one variable inside its first element.

func (*Default) Validate Uses

func (r *Default) Validate() error

Validate checks to ensure the contents of this node type satisfy constraints. The only constraint on the Default subtype is that it contains at only one element, the value is checked for correct typing by the Commands validator.

type DefaultCommand Uses

type DefaultCommand Tri

DefaultCommand specifies the Command that should run when no subcommand is specified on the commandline.

func (*DefaultCommand) Validate Uses

func (r *DefaultCommand) Validate() error

Validate checks to ensure the contents of this node type satisfy constraints. The constraint of DefaultCommand is that it has at least one element, and that the 0 element is a string. The check for the command name's presence in the Commands set is in the Tri validator.

type DefaultOn Uses

type DefaultOn Tri

DefaultOn specifies that the trigger it is inside is disabled by its name appearing in the invocation.

func (*DefaultOn) Validate Uses

func (r *DefaultOn) Validate() error

Validate checks to ensure the contents of this node type satisfy constraints. RunAfter is a simple flag that indicates by existence of an empty value, so it is an error if it has anything inside it.

type Examples Uses

type Examples Tri

Examples is is a list of pairs of strings containing a snippet of an example invocation and a short description of the effect of this example.

func (*Examples) Validate Uses

func (r *Examples) Validate() error

Validate checks to ensure the contents of this node type satisfy constraints. The constraints of examples are minimum two elements and all elements are strings. The intent is the even numbered items are snippets showing invocation and a description string of the same format as Brief{}.

type Group Uses

type Group Tri

Group is a single string tag with the same format as name fields that functions as a tag to gather related items in the help output.

func (*Group) Validate Uses

func (r *Group) Validate() error

Validate checks to ensure the contents of this node type satisfy constraints. A group must contain one string, anything else is invalid. It also has the same limitation as a name - only letters.

type Help Uses

type Help Tri

Help is a free-form text that is interpreted as markdown syntax and may optionally be formatted using ANSI codes by a preprocessor to represent the structured text that a markdown parser will produce, by default all markdown annotations will be removed.

func (*Help) Validate Uses

func (r *Help) Validate() error

Validate checks to ensure the contents of this node type satisfy constraints. Help may only contain one string. It will be parsed as markdown format and possibly can be set to style it with ANSI codes.

type RunAfter Uses

type RunAfter Tri

RunAfter is a flag indicating that a Trigger element of a Command should be run during shutdown instead of before startup.

func (*RunAfter) Validate Uses

func (r *RunAfter) Validate() error

Validate checks to ensure the contents of this node type satisfy constraints. RunAfter is a simple flag that indicates by existence of an empty value, so it is an error if it has anything inside it.

type Short Uses

type Short Tri

Short is a single character symbol that can be used instead of the name at the top of the Tri-derived type in invocation.

func (*Short) Validate Uses

func (r *Short) Validate() error

Validate checks to ensure the contents of this node type satisfy constraints. Short names contain only a single Rune variable.

type Slot Uses

type Slot Tri

Slot can contain pointers to one or more items of the same type and is intended to allow the parser to directly populate the value in a possibly external struct.

func (*Slot) Validate Uses

func (r *Slot) Validate() error

Validate checks to ensure the contents of this node type satisfy constraints. Slot may only contain one type of element. The type check is in the Var, here we only ensure the slots contain pointers to the same type, the parser will put the final parsed value in all of them. Multiple variables are permitted here to enable the configuration of more than one application.

type Terminates Uses

type Terminates Tri

Terminates is a flag for Trigger types that indicates that the function will terminate execution of the application once it completes its work.

func (*Terminates) Validate Uses

func (r *Terminates) Validate() error

Validate checks to ensure the contents of this node type satisfy constraints. Terminates is a flag value, and may not contain anything.

type Tri Uses

type Tri []interface{}

Tri is the root type where the base of an application parameter definition starts.

var exampleTri = Tri{
	"appname", // only letters in Tri tags
	Brief{"up to 80 char string, no control characters, not nil"},
	Usage{"up to 80 char string, no control characters, not nil"},
	Version{0, 1, 1, "alpha"},
	DefaultCommand{"help"},
	Var{"datadir",
		Short{"d"},
		Brief{"brief"},
		Usage{"usage"},
		Help{"help"},
		Default{"~/.pod"},
		Slot{""},
	},
	Trigger{"trigger",
	...
	},
	Commands{},
}

Note that this base specification may have variables and triggers associated with it that can be used to set configuration values common to all (or most) of the Commands specified in the declaration.

func (*Tri) Validate Uses

func (r *Tri) Validate() error

Validate checks to ensure the contents of this node type satisfy constraints. A Tri, the base type, in a declaration must contain a name as first element, a Brief, Version and a Commands item, and only one of each. Also, this and several other subtypes of Tri.

type Trigger Uses

type Trigger Tri

Trigger is for initiating the execution of one-shot functions that often terminate execution, or rewrite, sort, re-index, and this kind of thing.

func (*Trigger) Validate Uses

func (r *Trigger) Validate() error

Validate checks to ensure the contents of this node type satisfy constraints. Trigger must contain (one) name, Brief and Handler, and nothing other than these and Short, Usage, Help, Default, Terminates, RunAfter.

type Usage Uses

type Usage Tri

Usage is is an example showing the invocation of a Tri CLI flag.

func (*Usage) Validate Uses

func (r *Usage) Validate() error

Validate checks to ensure the contents of this node type satisfy constraints. Usage fields contain only one string of no more than 80 characters and no control characters.

type Var Uses

type Var Tri

Var is defines a configuration variable and the means to populate this variable in an optionally separate configuration structure.

func (*Var) Validate Uses

func (r *Var) Validate() error

Validate checks to ensure the contents of this node type satisfy constraints. Var must contain name, Brief and Slot, and optionally, Short, Usage, Help and Default. The type in the Slot and the Default must be the same.

type Version Uses

type Version Tri

Version is a short specification implementing a semver version string.

func (*Version) Validate Uses

func (r *Version) Validate() error

Validate checks to ensure the contents of this node type satisfy constraints. A version item contains three integers and an optional (less than 16 character) string, and the numbers may not be more than 99.

Package tri imports 5 packages (graph). Updated 2019-03-13. Refresh now. Tools for package owners.