bonzai

package
v0.21.0 Latest Latest
Warning

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

Go to latest
Published: Nov 2, 2024 License: Apache-2.0 Imports: 17 Imported by: 0

Documentation

Index

Examples

Constants

View Source
const (
	FAILED   = -1
	NOTFOUND = 0
	SUCCESS  = 1
)

Variables

View Source
var FuncMap = template.FuncMap{

	"exe": func(a string) string { return term.Under + a + term.Reset },
	"pkg": func(a string) string { return term.Bold + a + term.Reset },
	"cmd": func(a string) string { return term.Bold + a + term.Reset },

	"indent": indent,
	"pre":    func(a string) string { return term.Under + a + term.Reset },

	"exepath":      run.ExePath,
	"exename":      run.ExeName,
	"execachedir":  run.ExeCacheDir,
	"exestatedir":  run.ExeStateDir,
	"execonfigdir": run.ExeConfigDir,
	"cachedir":     futil.UserCacheDir,
	"confdir":      futil.UserConfigDir,
	"homedir":      futil.UserHomeDir,
	"statedir":     futil.UserStateDir,
	"pathsep":      func() string { return string(os.PathSeparator) },
	"pathjoin":     filepath.Join,
}

FuncMap contains the package global default template domain specific language implemented as a collection of functions in a template.FuncMap which can be supplemented or overridden by Bonzai developers. Note this is in addition to any specific syntax added specifically to a Cmd with Cmd.FuncMap (which takes higher priority). Note that there is no protection against any [Cmd.Call] function changing one or all of these entries for every other Cmd within the same executable. This flexibility is by design but must be taken into careful consideration when deciding to alter this package-scoped variable. It is almost always preferable to use the [Cmd.FuncMap] instead.

View Source
var IsValidName = is.AllLatinASCIILower

IsValidName is assigned a function that returns a boolean for the given name. See is.AllLatinASCIILower for an example. Note that if this is changed certain characters may break the creation of multicall binary links and bash completion.

Functions

This section is empty.

Types

type CallOrDef

type CallOrDef struct {
	Cmd *Cmd
}

func (CallOrDef) Error

func (e CallOrDef) Error() string

type Cmd

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

	// Work done by this command
	Init Method // run-time initialization/validation
	Call Method // if nil, Def must be set

	// Delegation to subcommands
	Def  *Cmd   // default [Cmd] if no Call and no matching Cmds
	Cmds []*Cmd // compiled/composed commands in no particular order
	Hide string // disable completion: ex: old|defunct

	// Minimal when [Cmd.Docs] is overkill
	Usage string
	Vers  string
	Short string
	Long  string

	// Faster than lots of "if" conditions in [Cmd.Call]. Consider
	// [Cmd.Init] when more complex argument validation is needed.
	MinArgs   int    // min
	MaxArgs   int    // max
	NumArgs   int    // exact, also used for NoArg (0)
	MatchArgs string // PCRE/Go regular expression (requires Usage)

	// Self-completion support: complete -C foo foo
	Comp Completer

	// Def vars declaration and initial values. Does not overwrite
	// existing vars. All vars used with [Cmd.Get] and [Cmd.Set] must be declared
	// even if empty. See [Vars], [VarsDriver], and package [is].
	Vars map[string]string

	// Optional embedded documentation in any format used by help and
	// documentation commands such as [doc.Cmd] from the core/cmds
	// package. Embedded content is usually lazy loaded only when the doc
	// command is called. Structure and format of the files can be anything
	// supported by any [Cmd] but Bonzai [mark] is recommended for
	// greatest compatibility. Use of an embedded file system instead of
	// a string allows, for example, support for multiple languages to be
	// embedded into a single binary.
	Docs embed.FS

	// Template commands/functions to be added (or overwrite) the internal
	// [FuncMap] collection of template commands used by the [Cmd.Fill]
	// command. These apply to this [Cmd] only and will not be available
	// to subcommands. To share between commands (including subcommands)
	// assign the same FuncMap to all of them.
	FuncMap template.FuncMap

	// Following are never assigned in declarations but are instead
	// set at [Run] time for use in [Call] methods:
	Caller *Cmd // delegation
	// contains filtered or unexported fields
}

func (*Cmd) Add

func (x *Cmd) Add(name string, aliases ...string) *Cmd

Add creates a new Cmd and sets the [Name] and [Alias] and adds to [Cmds] returning a reference to the new Cmd. Name must be first.

func (*Cmd) AliasSlice

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

AliasSlice updates the [aliases] internal cache ([CacheAlias]) and returns it as a slice.

Example
package main

import (
	"fmt"

	bonzai "github.com/rwxrob/bonzai/pkg"
)

func main() {

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

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

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

}
Output:

["b" "rab"][]

func (*Cmd) AppendCmd

func (x *Cmd) AppendCmd(cmd *Cmd)

AppendCmd safely appends the passed *Cmd to the [Cmds] slice.

func (*Cmd) CacheAlias

func (x *Cmd) CacheAlias()

CacheAlias updates the [aliases] cache by splitting [Alias] and adding the [Name] to the end. Remember to call this whenever dynamically altering the value at runtime.

func (*Cmd) CacheCmdAlias

func (x *Cmd) CacheCmdAlias()

CacheCmdAlias splits the [Cmd.Alias] for each Cmd in [Cmds] with its respective Cmd.AliasSlice and assigns them the Cmd.CmdAliasMap cache map. This is primarily used for bash tab completion support in [Run] and use as a multicall binary. If [Cmds] is nil or [Name] is empty silently returns.

func (*Cmd) CacheHide

func (x *Cmd) CacheHide()

CacheHide updates the [hidden] cache by splitting [Hide] . Remember to call this whenever dynamically altering the value at runtime.

func (*Cmd) CacheOpts

func (x *Cmd) CacheOpts()

CacheOpts updates the [opts] cache by splitting [Opts]. Remember to call this whenever dynamically altering the value at runtime.

func (*Cmd) Can

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

Can returns the *Cmd from [Cmds] if the [Cmd.Name] or any alias in [Cmd.Alias] for that command matches the name passed. If more than one argument is passed calls itself recursively on each item in the list.

Example
package main

import (
	"fmt"

	bonzai "github.com/rwxrob/bonzai/pkg"
)

func main() {

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

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

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

}
Output:

bar

func (*Cmd) CmdAliasMap

func (x *Cmd) CmdAliasMap() map[string]*Cmd

CmdAliasMap calls [CacheCmdAlias] to update cache if it is nil and then returns it. [Hide] is not applied.

func (*Cmd) CmdNames

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

CmdNames returns the names of every Cmd from [Cmds]

func (*Cmd) Fill

func (x *Cmd) Fill(tmpl string) string

Fill fills out the text/template string using the Cmd data fields and [Cmd.FuncMap] values combined with bonzai.FuncMap.

func (*Cmd) Get

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

Get is a shorter version of Vars.Get(x.Path()+"."+key) which fetches and returns persisted cache values (see Vars and [VarsDriver]). If a value has not yet been assigned returns the value from Vars and sets it with [Set]. All var keys must be declared and assigned initial values with Vars or they cannot be used and throw an UnsupportedVar run.ExitError.

func (*Cmd) HideSlice

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

HideSlice updates the [hidden] internal cache ([CacheHide]) and returns it as a slice.

func (*Cmd) IsHidden

func (x *Cmd) IsHidden() bool

IsHidden returns true if one of [Hide] matches the [Name].

func (*Cmd) Names

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

Names returns slice of all [Alias] and the [Name] as the last item.

func (*Cmd) Opt

func (x *Cmd) Opt(p string) string

Opt returns the [Opts] entry matching name if found, empty string if not.

func (*Cmd) OptsSlice

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

OptsSlice updates the [params] internal cache ([CacheOpts]) and returns it as a slice.

func (*Cmd) Path

func (x *Cmd) Path(more ...string) string

Path returns a dotted notation of the [PathNames] including an initial dot (for root). This is useful for associating configuration and other data specifically with this command. If any arguments are passed then will be added with dots between them.

func (*Cmd) PathCmds

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

PathCmds 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. Also see [Path], [PathNames].

func (*Cmd) PathNames

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

PathNames returns the path of command names 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. Also see Path.

func (*Cmd) PathWithDashes

func (x *Cmd) PathWithDashes(more ...string) string

PathWithDashes is the same as [Path] but with dashes/hyphens instead and without the leading dot.

func (*Cmd) PrependCmd

func (x *Cmd) PrependCmd(cmd *Cmd)

PrependCmd safely prepends the passed *Cmd to the [Cmds] slice.

func (*Cmd) Print

func (x *Cmd) Print(tmpl string)

Print calls [Fill] on string and prints it with fmt.Print. This is a rather expensive operation by comparison. Consider the simpler alternative or term.Print.

func (*Cmd) Println

func (x *Cmd) Println(tmpl string)

Println calls [Fill] on string and prints it with fmt.Println. This is a rather expensive operation by comparison. Consider the simpler alternative or term.Print.

func (*Cmd) Resolve

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

Resolve looks up a given Cmd by name or alias from [Alias] (caching a lookup map of aliases in the process).

func (*Cmd) Root

func (x *Cmd) Root() *Cmd

Root returns the root Cmd from the current [Path]. This must always be calculated every time since any Cmd can change positions and pedigrees at any time at run time. Returns self if no [PathCmds] found.

func (*Cmd) Run

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

Run method resolves [Cmd.Alias] and seeks the leaf Cmd. It then calls the leaf's first-class [Cmd.Call] function passing itself as the first argument along with any remaining command line arguments. Run returns nothing because it usually exits the program. Normally, Run is called from within main() to convert the Cmd into an actual executable program. Use Call instead of Run when delegation is needed. However, avoid tight-coupling that comes from delegation with Call when possible. Also, Call automatically assumes the proper number and type of arguments have already been checked (see [Cmd.MinArgs], etc.) which is normally done by Run.

Completion

Since Run is the main execution entry point for all Bonzai command trees it is also responsible for handling bash completion. Only bash completion is supported within the binary itself because only bash provides self-completion (complete -C foo foo). However, zsh can also be made to support it by adding a few functions from the oh-my-zsh code base).

Completion mode is triggered by the detection of the COMP_LINE environment variable. (complete -C cmd cmd).

When COMP_LINE is set, Run prints a list of possible completions to standard output by calling the [Completer.Complete] function of its [Comp] field. If [Comp] is nil no completion is attempted. Each Cmd explicitly manages its own completion and can draw from an growing ecosystem of Completers or assign its own. See the core/comp package for more examples.

Popularized by BusyBox/Alpine, a multicall binary is a single executable that behaves differently based on its name, either through copying the binary to another name, or linking (symbolic or hard). All Bonzai compiled binaries automatically behave as multicall binaries provided the name of the actual binary or link matches the name of Cmd.

Note that this method should never be used to obscure a highly sensitive command thinking it won't be discovered. Discovering every possible command is very easy to brute force.

Never panic

All panics are trapped with run.TrapPanic which normally exits with 1 and outputs the main message. See run.TrapPanic for details.

Valid name check

Throws InvalidName error and exist if [Name] does not pass InvalidName check.

Subcommand optional arguments

If any argument is detected, delegation through recursive Run calls to subcommands is attempted. If more than one argument, each argument is assumed to be a [Name] or alias from [Alias] and so on (see [Can] for details). As a convenience, if only one argument is passed and that argument contains a dash, it is assumed to be a [PathWithDashes] and is split and expanded into a new args list as if every field where passed as separate strings instead.

func (*Cmd) Seek

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

Seek checks the args for command names returning the deepest along with the remaining arguments. Typically the args passed are directly from the command line. Seek also sets the Caller on each Cmd found during resolution.

func (*Cmd) Set

func (x *Cmd) Set(key, val string) error

Set is shorter version of Vars.Set(x.Path()+"."+key.val).

func (Cmd) String

func (x Cmd) String() string

String fulfills the fmt.Stringer interface for debugging.

type Completer

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

Completer specifies a struct with a [Completer.Complete] function that will complete the first argument (usually a command of some kind) based on the remaining arguments. The [Complete] method must never panic and always return at least an empty slice of strings. By convention Completers that do not make use of or other arguments should use an underscore identifier since they are ignored.

type IncorrectUsage

type IncorrectUsage struct {
	Cmd *Cmd
}

func (IncorrectUsage) Error

func (e IncorrectUsage) Error() string

type InvalidMultiName

type InvalidMultiName struct {
	Got  string
	Want string
}

func (InvalidMultiName) Error

func (e InvalidMultiName) Error() string

type InvalidName

type InvalidName struct {
	Name string
}

func (InvalidName) Error

func (e InvalidName) Error() string

type Method

type Method func(x *Cmd, args ...string) error

Method defines the main code to execute for a command [Cmd.Call]. By convention the parameter list should be named "args" and the caller "x". If either is unused an underscore should be used instead.

type MissingVar

type MissingVar struct {
	Path string
}

func (MissingVar) Error

func (e MissingVar) Error() string

type NoCallNoDef

type NoCallNoDef struct {
	Cmd *Cmd
}

func (NoCallNoDef) Error

func (e NoCallNoDef) Error() string

type NotEnoughArgs

type NotEnoughArgs struct {
	Count int
	Min   int
}

func (NotEnoughArgs) Error

func (e NotEnoughArgs) Error() string

type TooManyArgs

type TooManyArgs struct {
	Count int
	Max   int
}

func (TooManyArgs) Error

func (e TooManyArgs) Error() string

type Uncallable

type Uncallable struct {
	Cmd *Cmd
}

func (Uncallable) Error

func (e Uncallable) Error() string

type UnsupportedVar

type UnsupportedVar struct {
	Name string
}

func (UnsupportedVar) Error

func (e UnsupportedVar) Error() string

type VarsInitFailed

type VarsInitFailed struct {
	Err error
}

func (VarsInitFailed) Error

func (e VarsInitFailed) Error() string

type WrongNumArgs

type WrongNumArgs struct {
	Count int
	Num   int
}

func (WrongNumArgs) Error

func (e WrongNumArgs) Error() string

Directories

Path Synopsis
cmd
bon module
clip module
var module

Jump to

Keyboard shortcuts

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