bonzai

package module
v0.56.6 Latest Latest
Warning

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

Go to latest
Published: Dec 23, 2024 License: Apache-2.0 Imports: 12 Imported by: 47

README ¶

🌳 Go Bonzai™ CLI Framework and Library

Bonzai is undergoing a major upgrade and a v1.0 should be here by 2025. We recommend waiting until then before migrating any existing stuff or embarking on RRIB shell script collections in your Dot files. Come join us on https://linktr.ee/rwxrob to help us finish up the new version.

GoDoc License Go Report Card

"It's like a modular, multicall BusyBox builder for Go with built in completion and embedded documentation support."

"The utility here is that Bonzai lets you maintain your own personal 'toolbox' with built in auto-complete that you can assemble from various Go modules. Individual commands are isolated and unaware of each other and possibly maintained by other people."

"I used Bonzai for a pentest ... was able to inject ebpf.io kernel modules that were part of bin lol! Bonzai is Genius!"

logo

Bonzaiâ„¢ was born from a very real need to replace messy collections of shell scripts, wasteful completion sourcing, and OS-specific documentation with a single, stateful, multicall binary composed of commands organized as rooted node trees with a clean, modular, portable, statically-compiled, and dynamically self-documenting design. There's simply nothing else like it in Go or any other language, and there's no better language than Go for such things. Crafting homekit/rootkit binaries with embedded resources that don't bloat RAM consumption is a breeze. No other language can do it.

Bonzai gets its name from the fact that Bonzai users are fond of meticulously manicuring their own stateful command trees, built from imported composite commands that they can easily copy and run on on any device, anywhere.

Bonzai users can easily share their own commands with others just like they would any other Go module and since most Bonzai commands also double as a high-level library package, even non-Bonzai users benefit. In fact, this monorepo is full of other Go modules containing cookbook recipe code for many of the things an avid techy with Go skills would want, the missing "batteries" that make Go really light up a well-crafted multicall command tree.

Getting started

Take a look at the following commands to get an idea of what can be done:

We have worked hard keep things as simple as possible so they are intuitive and to document this package as succinctly as possible so it is very usable from any decent tool that allows looking up documentation while writing the code.

Copyright 2024 Robert S. Muhlestein (mailto:rob@rwx.gg)
SPDX-License-Identifier: Apache-2.0

"Bonzai" and "bonzai" are legal trademarks of Robert S. Muhlestein but can be used freely to refer to the Bonzaiâ„¢ project https://github.com/rwxrob/bonzai without limitation. To avoid potential developer confusion, intentionally using these trademarks to refer to other projects --- free or proprietary --- is prohibited.

Documentation ¶

Index ¶

Examples ¶

Constants ¶

This section is empty.

Variables ¶

View Source
var AllowPanic bool

AllowsPanic if set will turn any panic into an exit with an error message.

View Source
var IsValidName = allLatinASCIILowerWithDashes

IsValidName is assigned a function that returns a boolean for the given name. Note that if this is changed certain characters may break the creation of multicall binary links and bash completion. See the pkg/github.com/rwxrob/bonzai/is package for alternatives.

View Source
var Nothing = func(*Cmd, ...string) error { return nil }

Nothing is a no-op function that implements the command function signature for Cmd. It takes a command *Cmd and a variadic number of string arguments and always returns nil, indicating no operation is performed.

Functions ¶

This section is empty.

Types ¶

type Cmd ¶

type Cmd struct {
	Name  string // ex: delete (required)
	Alias string // ex: rm|d|del (optional)
	Opts  string // ex: mon|wed|fri (optional)

	// Declaration of shareable variables (set to nil at runtime after
	// caching internal map, see [Cmd.VarsSlice], [Cmd.Get], [Cmd.Set]).
	Vars    Vars
	Persist Persister

	// Work down by this command itself
	Init func(x *Cmd, args ...string) error // initialization with [SeekInit]
	Do   func(x *Cmd, args ...string) error // main (optional if Def or Cmds)

	// Delegated work
	Cmds []*Cmd // composed subcommands (optional if Do or Def)
	Def  *Cmd   // default (optional if Do or Cmds, not required in Cmds)

	// Documentation
	Usage string           // text (<70 runes) (optional)
	Short string           // text (<50 runes) (optional)
	Long  string           // text/markup (optional)
	Vers  string           // text (<50 runes) (optional)
	Funcs template.FuncMap // own template tags (optional)

	// Faster than "if" conditions in [Cmd.Do] (all optional)
	MinArgs  int    // min
	MaxArgs  int    // max
	NumArgs  int    // exact
	NoArgs   bool   // 0
	RegxArgs string // regx check each arg (document in Long)

	// Self-completion support: complete -C foo foo
	Comp Completer
	// contains filtered or unexported fields
}

func (*Cmd) Aliases ¶

func (x *Cmd) Aliases() []string

Aliases returns the Cmd.Alias value split into a slice with the Cmd.Name added as the last item.

Example ¶
package main

import (
	"fmt"

	"github.com/rwxrob/bonzai"
)

func main() {
	var barCmd = &bonzai.Cmd{
		Name:  `bar`,
		Alias: `b|rab`,
		Do: func(*bonzai.Cmd, ...string) error {
			fmt.Println(`i am bar`)
			return nil
		},
	}
	fmt.Printf("%q", barCmd.Aliases())

	var fooCmd = &bonzai.Cmd{
		Name: `foo`,
		Cmds: []*bonzai.Cmd{barCmd},
	}

	fmt.Printf("%q", fooCmd.Aliases())

}
Output:

["b" "rab" "bar"]["foo"]

func (Cmd) AsHidden ¶ added in v0.33.0

func (x Cmd) AsHidden() *Cmd

AsHidden returns a copy of the Cmd with its internal hidden property set to true so that Cmd.IsHidden returns true. Use cases include convenient inclusion of leaf commands that are already available elsewhere (like help or var) and allowing deprecated commands to be supported but hidden in help output. See the pkg/github.com/rwxrob/bonzai/mark/funcs package for examples.

func (Cmd) Caller ¶ added in v0.0.7

func (x Cmd) Caller() *Cmd

Caller returns the internal reference to the parent/caller of this command. It is not set until Cmd.Seek or Cmd.SeekInit is called or indirectly by Cmd.Run or Cmd.Exec. Caller is set to itself if there is no caller (see Cmd.IsRoot).

func (*Cmd) Can ¶ added in v0.22.0

func (x *Cmd) Can(names ...string) *Cmd

Can returns the first pointer to a command from the Cmd.Cmds list that has a matching Name or Alias. If more than one argument is passed calls itself recursively on each item in the list.

Example ¶
package main

import (
	"fmt"

	"github.com/rwxrob/bonzai"
)

func main() {
	var barCmd = &bonzai.Cmd{
		Name: `bar`,
		Do: func(*bonzai.Cmd, ...string) error {
			fmt.Println(`i am bar`)
			return nil
		},
	}

	var fooCmd = &bonzai.Cmd{
		Name: `foo`,
		Cmds: []*bonzai.Cmd{barCmd},
	}

	fmt.Println(fooCmd.Can(`bar`))

}
Output:

bar

func (*Cmd) CmdNames ¶

func (x *Cmd) CmdNames() []string

CmdNames returns the names of all Cmd.Cmds.

func (*Cmd) Exec ¶ added in v0.38.0

func (x *Cmd) Exec(args ...string)

Exec is called from main function in the main package and never returns, always exiting with either 0 or 1 usually printing any error encountered. Exec calls Cmd.Run (which returns errors instead of exiting) after the following runtime considerations.

Self-completion ¶

Exec checks the COMP_LINE environment variable and if found assumes the program is being called in self-completion context by a user tapping the tab key one or more times. See the bash man page section "Programmable Completion" (although zsh can also be setup to enable bash self-completion). All Bonzai programs can, therefore, be set to complete themselves:

complete -C foo foo

See Completer and CmdCompleter for more information about completion and the pkg/github.com/rwxrob/bonzai/comp/completers package for a growing collection of community maintained common completer implementations. Contributions always welcome.

Trapped panics ¶

Exec traps any panics unless the DEBUG environment variable is set (truthy).

Multicall ¶

Exec uses pkg/os.Args[0] compared to the Cmd.Name to resolve what to run enabling the use of multicall binaries with dashes in the name (a common design pattern used by other monolith multicalls such as Git and BusyBox/Alpine).

func (*Cmd) Get ¶ added in v0.22.0

func (x *Cmd) Get(key string) string

Get returns the value of pkg/os.LookupEnv if Var.E was set and a corresponding environment variable was found overriding everything else in priority and shadowing any initially declared in-memory value or persisted value even if the found env variable is set to empty string. Var.V is completely ignored in this case. The environment variable never changes the persisted value.

Otherwise, if Var.P is true attempts to look it up from either internal persistence set with Cmd.Persist or the package DefaultPersister default if either is not nil. If a persister is available but returns an empty string then it is assumed the initial value has never been persisted and the in-memory cached value is returned and also persisted with Cmd.Set ensuring that initial Cmd.Vars declarations are persisted if they need to be for the first time. Note that none of this works until Cmd.Run is called which caches the Vars and sets up persistence. Panics if key was never declared. Locks the variable so safe for concurrency but persisters must implement their own file-level locking if shared between multiple processes.

func (*Cmd) IsHidden ¶

func (x *Cmd) IsHidden() bool

IsHidden returns true if Cmd.AsHidden was used to create.

func (*Cmd) IsRoot ¶ added in v0.26.4

func (x *Cmd) IsRoot() bool

IsRoot determines if the command has no parent commands above it by checking if its Cmd.Caller is the same as itself returning true if so.

func (*Cmd) OptsSlice ¶ added in v0.22.0

func (x *Cmd) OptsSlice() []string

OptsSlice returns the Cmd.Opts as a cached slice (derived from the delimited string).

func (*Cmd) Path ¶ added in v0.22.0

func (x *Cmd) Path() []*Cmd

Path returns the path of commands used to arrive at this command. The path is determined by walking backward from current caller up rather than depending on anything from the command line used to invoke the composing binary.

func (*Cmd) PathDashed ¶ added in v0.51.0

func (x *Cmd) PathDashed() string

PathDashed returns a string representation of the command path for the command with each command name joined by a dash ('-'). It utilizes the Cmd.PathNames method to obtain the names of the commands in the path.

Example ¶
package main

import (
	"fmt"

	"github.com/rwxrob/bonzai"
)

func main() {
	var subFooCmd = &bonzai.Cmd{
		Name:  `subfoo`,
		Alias: `sf`,
		Short: `under the foo command`,
	}

	var sssh = &bonzai.Cmd{
		Name: `sssh`,
		Do:   bonzai.Nothing,
	}

	var fooCmd = &bonzai.Cmd{
		Name:  `foo`,
		Alias: `f`,
		Short: `foo this command`,
		Cmds:  []*bonzai.Cmd{subFooCmd, sssh.AsHidden()},
	}

	var barCmd = &bonzai.Cmd{
		Name:  `bar`,
		Alias: `b`,
		Short: `bar this command`,
	}

	var Cmd = &bonzai.Cmd{
		Name:  `mycmd`,
		Alias: `my|cmd`,
		Short: `my command short summary`,
		Cmds:  []*bonzai.Cmd{fooCmd, barCmd},
		Def:   fooCmd,
	}

	cmd, args, err := Cmd.SeekInit(`foo`, `sssh`)
	fmt.Println(cmd, args, err)
	fmt.Println(cmd.PathDashed())

}
Output:

sssh [] <nil>
foo-sssh

func (*Cmd) PathNames ¶ added in v0.22.0

func (x *Cmd) PathNames() []string

PathNames returns a slice of strings containing the names of all commands in the command path for the command. It retrieves the commands using Cmd.Path and constructs a slice with their respective Name fields before returning it.

func (*Cmd) Root ¶ added in v0.0.27

func (x *Cmd) Root() *Cmd

Root returns the root Cmd traversing up its Cmd.Caller tree returning self if already root. The value returned will always return true of its Cmd.IsRoot is called.

func (*Cmd) Run ¶

func (x *Cmd) Run(args ...string) error

Run seeks the leaf command in the arguments passed, validating all with Cmd.SeekInit and calls its Cmd.Do method passing itself as the first argument along with any remaining arguments. Run is always called from Cmd.Exec but can be called directly from another command's Do method to enable powerful command composition and delegation at a high-level. Run returns an error if a command cannot be found or the command fails validation in any way.

Example ¶
package main

import (
	"fmt"

	"github.com/rwxrob/bonzai"
)

func main() {
	var fooCmd = &bonzai.Cmd{
		Name: `foo`,
		Do: func(*bonzai.Cmd, ...string) error {
			fmt.Println(`I am foo`)
			return nil
		},
	}

	var barCmd = &bonzai.Cmd{
		Name: `bar`,
		Cmds: []*bonzai.Cmd{fooCmd},
		Do: func(*bonzai.Cmd, ...string) error {
			fmt.Println(`I am bar`)
			return nil
		},
	}

	var bazCmd = &bonzai.Cmd{
		Name: `baz`,
		Cmds: []*bonzai.Cmd{barCmd},
		Do: func(*bonzai.Cmd, ...string) error {
			fmt.Println(`I am baz`)
			return nil
		},
	}

	fooCmd.Run()
	bazCmd.Run("bar")
	bazCmd.Run("bar", "foo")

}
Output:

I am foo
I am bar
I am foo

func (*Cmd) Seek ¶

func (x *Cmd) Seek(args ...string) (*Cmd, []string)

Seek checks the args passed for command names returning the deepest along with the remaining arguments. Typically the args passed are directly derived from the command line. Seek also sets Cmd.Caller on each Cmd in the path. Seek is indirectly called by Cmd.Run and Cmd.Exec. See pkg/github.com/rwxrob/bonzai/cmds/help for a practical example of how and why a command might need to call Seek. Also see Cmd.SeekInit when environment variables and initialization functions are wanted as well. Seek returns its receiver and the same single empty string argument if there is only one argument and it is an empty string (a special case used for completion). Returns self and the arguments passed if both Cmds and the default command (Def) are nil. If Cmds is nil but Def is not, returns the default command and the same list of arguments passed.

func (*Cmd) SeekInit ¶ added in v0.48.0

func (x *Cmd) SeekInit(args ...string) (*Cmd, []string, error)

SeekInit is the same as Cmd.Seek but Vars are cached and the Cmd.Validate and [Cmd.Init] functions are called (if any). Returns early with nil values and the error if any Validate or Init function produces an error.

Example ¶
package main

import (
	"fmt"

	"github.com/rwxrob/bonzai"
)

func main() {

	var foo = &bonzai.Cmd{
		Name: `foo`,
		Do: func(*bonzai.Cmd, ...string) error {
			fmt.Println(`I am foo`)
			return nil
		},
	}

	var bar = &bonzai.Cmd{
		Name: `bar`,
		Cmds: []*bonzai.Cmd{foo},
		Do: func(*bonzai.Cmd, ...string) error {
			fmt.Println(`I am bar`)
			return nil
		},
	}

	var baz = &bonzai.Cmd{
		Name:   `baz`,
		Cmds:   []*bonzai.Cmd{bar},
		NoArgs: true,
		Do: func(*bonzai.Cmd, ...string) error {
			fmt.Println(`I am baz`)
			return nil
		},
	}

	fmt.Println(baz.SeekInit(`bar`))
	fmt.Println(baz.SeekInit(`bar`, `arg1`))

}
Output:

bar [] <nil>
bar [arg1] <nil>

func (*Cmd) Set ¶ added in v0.22.0

func (x *Cmd) Set(key, value string)

Set assigns the value of the internal vars value for the given key. If the Var.E is found to exist with os.LookupEnv then it is only set instead. This allows environment variables to shadow in-memory and persistent variables. If Cmd.Persister is set, then attempts to persist using the internal persister created with Cmd.Persister or the DefaultPersister default if either is not nil. Otherwise, assumes no persistence and only changes the in-memory value. Panics if key was not declared in Cmd.Vars. Locks the Var in question so safe for concurrency.

func (Cmd) String ¶ added in v0.22.0

func (x Cmd) String() string

String fulfills the pkg/fmt.Stringer interface for pkg/fmt.Print debugging and template inclusion by simply printing the Cmd.Name.

func (*Cmd) Validate ¶ added in v0.45.1

func (c *Cmd) Validate() error

Validate checks the integrity of the command. It verifies that the command is not nil, validates the length and format of the Short and Vers fields, checks the validity of the command Name, and ensures that the command is callable by checking the associated function Do, Def, and subcommands. It returns an error if any validation check fails. Validate is automatically called for every command during the Cmd.SeekInit descent to the leaf command.

func (*Cmd) ValidateArgs ¶ added in v0.50.0

func (c *Cmd) ValidateArgs(args ...string) error

ValidateArgs checks the validity of the provided args against the constraints defined in the command. It returns an error if the arguments do not meet the minimum or maximum requirements, do not match the expected number, or fail regex validation. ValidateArgs is automatically called from Cmd.SeekInit right before the leaf command and its arguments are returned.

func (*Cmd) VarsSlice ¶ added in v0.52.0

func (x *Cmd) VarsSlice() Vars

VarsSlice returns a slice with a copy of the current Cmd.Vars. Use Cmd.Get and Cmd.Set to access the actual value.

func (*Cmd) WalkDeep ¶ added in v0.43.0

func (x *Cmd) WalkDeep(fn func(int, *Cmd) error, onError func(error))

WalkDeep recursively traverses the command tree starting from itself, applying the function (fn) to each Cmd within Cmd.Cmds with its level. If an error occurs while executing fn, the onError function is called with the error. Note that Cmd.Def is only included if it is also in the Cmds slice.

Example ¶
package main

import (
	"fmt"

	"github.com/rwxrob/bonzai"
)

func main() {

	var barCmd = &bonzai.Cmd{Name: `bar`}

	var fooCmd = &bonzai.Cmd{
		Name: `foo`,
		Cmds: []*bonzai.Cmd{barCmd, barCmd.WithName(`bar2`)},
	}

	var Cmd = &bonzai.Cmd{
		Name: `top`,
		Cmds: []*bonzai.Cmd{fooCmd, fooCmd.WithName(`foo2`)},
	}

	names := []string{} // enclosed

	aggregate := func(level int, x *bonzai.Cmd) error {
		names = append(names, fmt.Sprintf("%v-%v", x.Name, level))
		return nil
	}

	errors := []error{} // enclosed
	onerror := func(err error) {
		errors = append(errors, err)
	}

	Cmd.WalkDeep(aggregate, onerror)
	fmt.Println(names)

}
Output:

[top-0 foo-1 bar-2 bar2-2 foo2-1 bar-2 bar2-2]

func (*Cmd) WalkWide ¶ added in v0.43.0

func (x *Cmd) WalkWide(fn func(int, *Cmd) error, onError func(error))

WalkWide performs a breadth-first traversal (BFS) of the command tree starting from the command itself, applying the function (fn) with the current level to each Cmd in Cmd.Cmds recursively. If an error occurs during the execution of fn, the onError function is called with the error. It uses a queue to process each command and its subcommands iteratively. Note that Cmd.Def is only included if it is also in the Cmds slice.

Example ¶
package main

import (
	"fmt"

	"github.com/rwxrob/bonzai"
)

func main() {

	var barCmd = &bonzai.Cmd{Name: `bar`}

	var fooCmd = &bonzai.Cmd{
		Name: `foo`,
		Cmds: []*bonzai.Cmd{barCmd, barCmd.WithName(`bar2`)},
	}

	var Cmd = &bonzai.Cmd{
		Name: `top`,
		Cmds: []*bonzai.Cmd{fooCmd, fooCmd.WithName(`foo2`)},
	}

	names := []string{} // enclosed

	aggregate := func(level int, x *bonzai.Cmd) error {
		names = append(names, fmt.Sprintf("%v-%v", x.Name, level))
		return nil
	}

	errors := []error{} // enclosed
	onerror := func(err error) {
		errors = append(errors, err)
	}

	Cmd.WalkWide(aggregate, onerror)
	fmt.Println(names)

}
Output:

[top-0 foo-1 foo2-1 bar-1 bar2-1 bar-1 bar2-1]

func (Cmd) WithName ¶ added in v0.25.0

func (x Cmd) WithName(name string) *Cmd

WithName sets the Cmd.Name to name and returns a pointer to a copy of the updated command with the new name. This covers a very specific but important use case when a naming conflict exists between two different commands at the same level within the command tree, for example, a help command that displays help in the local web browser and another help command with a new name that sends it to the terminal.

Example ¶
package main

import (
	"fmt"

	"github.com/rwxrob/bonzai"
)

func main() {
	var barCmd = &bonzai.Cmd{
		Name: `bar`,
		Do: func(*bonzai.Cmd, ...string) error {
			fmt.Println(`i am bar`)
			return nil
		},
	}

	fooCmd := barCmd.WithName(`foo`)
	fmt.Println(barCmd.Name)
	barCmd.Do(barCmd)
	fmt.Println(fooCmd.Name)
	fooCmd.Do(fooCmd)

}
Output:

bar
i am bar
foo
i am bar

func (Cmd) WithPersister ¶ added in v0.54.0

func (x Cmd) WithPersister(a Persister) *Cmd

WithPersister overrides or adds Cmd.Persist. The Persister.Setup method is called and panics on error.

type Completer ¶ added in v0.3.0

type Completer interface {
	Complete(args ...string) []string
}

Completer specifies anything with Complete function based on the remaining arguments. The Complete method must never panic and always return at least an empty slice of strings. For completing data from a Cmd use CmdCompleter instead. Implementations must not examine anything from the command line itself depending entirely on the passed arguments instead (which are usually the remaining arguments from the command line). Implementations may and will often depend on external data sources to determine the possible completion values, for example, current host names, users, or data from a web API endpoint.

type ErrBadVarInheritance ¶ added in v0.54.0

type ErrBadVarInheritance struct {
	Var Var
}

ErrBadVarInheritance indicates a Cmd.Vars Var that violated Bonzai variable rules regarding inheritance.

func (ErrBadVarInheritance) Error ¶ added in v0.54.0

func (e ErrBadVarInheritance) Error() string

type ErrDoOrDef ¶ added in v0.38.0

type ErrDoOrDef struct {
	Cmd *Cmd
}

ErrDoOrDef suggests that a command cannot have both Do and Def functions, providing details on the conflicting Cmd.

func (ErrDoOrDef) Error ¶ added in v0.38.0

func (e ErrDoOrDef) Error() string

type ErrInvalidArg ¶ added in v0.44.0

type ErrInvalidArg struct {
	Exp   string
	Index int
}

ErrInvalidArg indicates that the arguments did not match a particular possible regular expression.

Example ¶
package main

import (
	"fmt"

	"github.com/rwxrob/bonzai"
)

func main() {
	var foo = &bonzai.Cmd{
		Name:     `foo`,
		Short:    `just oooo`,
		RegxArgs: `^o+$`,
		Cmds:     []*bonzai.Cmd{&bonzai.Cmd{Name: `foo`}},
	}

	err := foo.Run(`fooo`)
	fmt.Println(err)

}
Output:

usage error: arg #1 must match: ^o+$

func (ErrInvalidArg) Error ¶ added in v0.44.0

func (e ErrInvalidArg) Error() string

type ErrInvalidName ¶ added in v0.28.5

type ErrInvalidName struct {
	Name string
}

ErrInvalidName indicates that the provided name for the command is invalid. It includes the invalid [Name] that caused the error.

func (ErrInvalidName) Error ¶ added in v0.28.5

func (e ErrInvalidName) Error() string

type ErrInvalidShort ¶ added in v0.28.5

type ErrInvalidShort struct {
	Cmd *Cmd
}

ErrInvalidShort indicates that the short description length exceeds 50 characters, providing a reference to the Cmd and its [Short] description.

Example ¶
package main

import (
	"fmt"

	"github.com/rwxrob/bonzai"
)

func main() {
	var foo = &bonzai.Cmd{
		Name:  `foo`,
		Short: `this is a long short desc that is longer than 50 runes`,
	}

	err := foo.Run()
	fmt.Println(err)

}
Output:

developer-error: Cmd.Short (foo) length must be less than 50 runes and must begin with a lowercase letter

func (ErrInvalidShort) Error ¶ added in v0.28.5

func (e ErrInvalidShort) Error() string

type ErrInvalidUsage ¶ added in v0.56.5

type ErrInvalidUsage struct {
	Cmd *Cmd
}

ErrInvalidUsage indicates that the short description length exceeds 50 characters, providing a reference to the Cmd and its [Usage] description.

func (ErrInvalidUsage) Error ¶ added in v0.56.5

func (e ErrInvalidUsage) Error() string

type ErrInvalidVers ¶ added in v0.39.3

type ErrInvalidVers struct {
	Cmd *Cmd
}

ErrInvalidVers indicates that the short description length exceeds 50 characters, providing a reference to the Cmd and its [Vers] description.

Example ¶
package main

import (
	"fmt"

	"github.com/rwxrob/bonzai"
)

func main() {
	var foo = &bonzai.Cmd{
		Name: `foo`,
		Vers: `this is a long version that is longer than 50 runes`,
	}

	err := foo.Run()
	fmt.Println(err)

}
Output:

developer-error: Cmd.Vers (foo) length must be less than 50 runes

func (ErrInvalidVers) Error ¶ added in v0.39.3

func (e ErrInvalidVers) Error() string

type ErrNotEnoughArgs ¶ added in v0.28.5

type ErrNotEnoughArgs struct {
	Count int
	Min   int
}

ErrNotEnoughArgs indicates that insufficient arguments were provided, describing the current [Count] and the minimum [Min] required.

func (ErrNotEnoughArgs) Error ¶ added in v0.28.5

func (e ErrNotEnoughArgs) Error() string

type ErrTooManyArgs ¶ added in v0.28.5

type ErrTooManyArgs struct {
	Count int
	Max   int
}

ErrTooManyArgs signifies that too many arguments were provided, including the current [Count] and the maximum [Max] allowed.

func (ErrTooManyArgs) Error ¶ added in v0.28.5

func (e ErrTooManyArgs) Error() string

type ErrUncallable ¶ added in v0.28.5

type ErrUncallable struct {
	Cmd *Cmd
}

ErrUncallable indicates that a command requires a Do, one Cmd.Cmds or a Cmd.Def assigned.

func (ErrUncallable) Error ¶ added in v0.28.5

func (e ErrUncallable) Error() string

type ErrWrongNumArgs ¶ added in v0.28.5

type ErrWrongNumArgs struct {
	Count int
	Num   int
}

ErrWrongNumArgs indicates that the number of arguments does not match the expected count, showing the current [Count] and the required [Num].

func (ErrWrongNumArgs) Error ¶ added in v0.28.5

func (e ErrWrongNumArgs) Error() string

type Persister ¶ added in v0.54.0

type Persister interface {
	Setup() error          // setup existing or create (never clear)
	Get(key string) string // accessor, "" if non-existent
	Set(key, val string)   // mutator, "" to effectively delete
}

Persister specifies anything that implements a persistence layer for storage and retrieval of key/value combinations.

Empty values ¶

Since all implementations must return and set strings, the empty string value is considered unset or non-existent. This is consistent with working with pkg/os.Getenv. Therefore, there is no Delete or Has equivalent since a Set("") works to delete a value and a len(Get())>0 is the same as Has.

Setup ¶

Set up an existing persistence store or create and initialize a new one if one does not yet exist. Never clears or deletes one that has been previously initialized (which is outside the scope of this interface). Usually this is called within an init() function after the other specific configurations of the driver have been set (much like database or other drivers). When called from init should usually prompt a panic since something has gone wrong during initialization and no attempt to run main should proceed, but this depends on the severity of the error, and that is up to the implementations to decide.

Get ¶

Retrieves a value for a specific key in a case-sensitive way or returns an empty string if not found.

Set ¶

Assigns a value for a given key. If the key did not exist, must create it. Callers can choose to check for the declaration of a key before calling Set, such as with [Cmd.Vars] and Cmd.Get and Cmd.Set (which themselves are not implementations of this interface although they use one internally).

var DefaultPersister Persister

DefaultPersister for any Cmd that is not created with its own persistence using Cmd.Persist. The Cmd.Get and Cmd.Set use this if Cmd does not have its own. If assigned, its [Persist] method is called during init of the bonzai package.

type Var ¶ added in v0.40.0

type Var struct {
	sync.Mutex
	K string  `json:"k,omitempty"` // key
	V string  `json:"v,omitempty"` // value
	E string  `json:"e,omitempty"` // environment variable name
	S string  `json:"s,omitempty"` // short description
	P bool    `json:"p,omitempty"` // persistent
	I string  `json:"i,omitempty"` // inherits
	R bool    `json:"r,omitempty"` // required to be non-empty
	X *Cmd    `json:"-"`           // inherited from
	G *string `json:"-"`           // fetch first value into global
}

Var contains information to be shared between Cmd instances and contains a pkg/sync.Mutex allowing safe-for-concurrency modification when needed. This is used instead of pkg/sync.RWMutex because it is common for the first Get operation to also set the declared initial value such as when working with persistence.

Var is rarely used directly being declared in Cmd.Vars and used by Cmd.Get and Cmd.Set under the hood after being cached internally by the Cmd.Run method.

Empty string means undefined ¶

Bonzai variables are assumed to be undefined when empty (much like the use of traditional environment variables). This means that command creators must use something besides an empty string for booleans and such. There are no other specific string-to-type marshaling standards defined by Bonzai conventions, only that an empty string means "undefined".

Mandatory explicit declaration ¶

Cmd.Run, Cmd.Get, or Cmd.Set panics if the key referenced is not declared in the Cmd.Vars slice.

Environment variables ¶

When an environment variable name (E) is provided and an initial value (V) is not set, the value of E is looked up is taken as the exact name of an environment variable that, if found to exist, even if blank, is used exactly as if the initial V value had been explicitly assigned that value returned from the environment variable value lookup. One use case is to allow users to decide if they prefer to manage initial values through environment variables instead of the alternatives. Note that when V is assigned indirectly in this way that any persistence is also applied in addition to the in-memory assignment of the value of V.

When an environment variable name (E) is provided and an initial value (V) is also set, the environment variable, if found to exist, even if blank, is said to "shadow" the initial and current value (V) as well as any persistence. Get and Set operations are applied to the in-memory environment variable only. This is useful for use cases that temporarily alter behavior without changing default values or persistent values, such as when debugging, or creating "dry runs". This also provides a well-supported conventional alternative to getopts style options:

LANG=en greet
GOOS=arch go build

Persistence ¶

When persistence (P) is true then either internal persistence (Cmd.Persist) or default persistence (DefaultPersister) is checked and used if available (not nil). If neither is available then it is as if P is false. In-memory values (V) are always kept in sync with those persisted depending on the method of persistence employed by the Persister. Both are always safe for concurrency.

When persistence (P) is true but the initial value (V) is empty, then persistence is assumed to have the value or be okay with an undefined persisted value.

When persistence (P) is true and the initial value (V) is set and Cmd.Get retrieves an empty value (after the delegated call to Persister.Get), then the initial value (V) is passed to Persister.Set persisting it for the next time.

Explicit inheritance ¶

When the inherits (I) field is set, then the variable is inherited from some command above it and if not found must panic. When found, the inheriting Var internally assigns a reference (Var.X) to the Cmd in which the inherited Var was declared Var.X. The variable-related operations Cmd.Get and Cmd.Set then directly operate on the Var pointed to by the inherited Var ref (X) instead of itself. Setting the inheritor field (I) in addition to any other field causes a panic.

Scope and depth of inheritance ¶

There is no concept of variable scope or depth. This means any subcommand at any depth level may declare that it wants to inherit and therefore retrieve and modify any declared variable above it. It is assumed that any developer composing a Bonzai command branch would look at such declarations in Vars to decide if it is safe to import and compose that subcommand into the compilation of the overall command. The requirement to declare all Vars that do inherit makes this knowledge explicitly visible for all Bonzai commands.

Global package variable injection ¶

If the Var.G is assigned the reference to a string variable, which are usually expected to be package globals that can be used by any command at all in the package, then Get is called on the command when the Cmd.Vars list is cached during the early SeekInit phase. This means that parent commands can trigger the fetching of those variables and setting their values early on before anything else. This saves considerable boilerplate for otherwise calling Get every time it is needed when---and only when---the value is not expected to change over the life of the program execution (e.g. API keys, account IDs, etc.).

func (Var) String ¶ added in v0.52.0

func (v Var) String() string

type Vars ¶ added in v0.9.0

type Vars []Var

func (Vars) String ¶ added in v0.52.0

func (vs Vars) String() string

Directories ¶

Path Synopsis
anim module
choose module
cmds
help Module
kimono Module
rayfish Module
sunrise Module
vars Module
comp module
depends module
ds module
dtime module
edit module
fn module
tr Module
futil module
github module
is module
json module
mark module
funcs Module
renderers/man Module
viewers/less Module
opts module
persisters module
injson Module
inmem Module
inprops Module
inyaml Module
pkg
cmd/bon Module
cmd/clip Module
cmd/var Module
run module
scanner module
term module
test
multicall Module
tinout module
to module
uniq module
vars module
web module
yaml module
yq module

Jump to

Keyboard shortcuts

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