Help us shape the future of Go! Take the 2026 Go Developer Survey

bonzai

package module
v0.0.30 Latest Latest
Warning

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

Go to latest
Published: Mar 28, 2022 License: Apache-2.0 Imports: 17 Imported by: 61

README

Bonzai™ Command Compositor

Dominate the command line, on any device.

🚧 under construction 🚧

Go Version GoDoc License Go Report Card

Installation

🎉 Bonzai shamelessly requires Go 1.18+ 💋

  1. Install Go 1.18 and the tooling your require for it
  2. go install github.com/rwxrob/bonzai@latest
  3. import "github.com/rwxrob/bonzai"
  4. Consider using the template to get started

😎 Yes, we use the wonderful new generics all over. 👍

Welcome to Bonzai

Yes, "banzai" is something people yell going into battle. But isn't that what making command line utilities in Go (instead of your favorite shell script) actually is?

And yes, "bonsai" trees are well-manicured, meticulously crafted, miniature trees that rival their larger cousins, just like Bonzai command and data node trees. They are unlike anything you've probably encountered so far, no getopt dashes (we kind of hate them), no ugly commander interface to learn, no 12637 lines of shell tab completion bloat to source before your command will complete, just well manicured nested-tab-complete-with-magical-aliases-enabled commands organized into rooted node trees for your command-line enjoyment. Your right-pinky will be particularly grateful.

But wait, there's more! What about all those other tasks you need to do to make a command line application honorable in anyone's eyes? Tools are needed.

Contributors/PRs Welcome

... especially for "Completers", included popular commands, and Runtime Detection.

Speaking of sharing, why not send a PR for your addition to the ever growing collection of comp subpackage Completers for everything from days of the week, to tab-driven inline math calculations, to a list of all the listening ports running on your current system.

CONTRIBUTING

"It's spelled bonsai/banzai."

We know. The domains were taken. Plus, this makes it more unique and easier to find once you know the strange spelling we chose to use. Sorry if that triggers your OCD.

If you must know, the primary motivator was the similarity to a well-manicured tree (since it is for creating trees of commands). And Buckaroo Banzai was always a favorite. We like to think he would use Bonzai today to make amazing things.

On a lighter note, it just so happens that "banzai" means 'a traditional Japanese idiom meaning "ten thousand years" of long life,' a cheer used in celebrations. So combining the notion of a happy, little, well-manicured, beautiful tree and "ten thousand years of long life" works out just fine for us.

It turns out that the "call to war" associated with Bonzai is not entirely without merit as well. Bonzai makes short work of creating offensive and defensive tool kits all wrapping into one nice Go binary, popular for building single-binary Linux container distros (like BusyBox and Alpine, watch for Bonzai Linux soon), as well as root kits, and other security tools

"Why not just use Cobra?"

Just because something is popular (or first) doesn't mean it was well designed. In fact, often inferior designs are rushed to market just to gain adoption. Cobra seems to suffer from this. Discerning developers and engineers have been not-so-quietly complaining about Cobra's horrible design for years. It's time for something new.

Cobra requires wasteful and error-prone sourcing of thousands of lines of shell code every time you create a new shell that needs to use a Cobra command with shell tab completion (kubectl requires 12637). It is not uncommon for operations people to be sourcing 100s of thousands of lines of shell code just to enable basic completion that could have been enabled easily with complete -C instead. Bonzai manages all completion in Go instead of shell and therefore allows the modular addition of any number of Completers including the standard file completion as well as calculators, dates, and anything anyone can conceive of completing. Completion is not dependent on any underlying operating system. Any Bonzai command can provide its own completion algorithm or use one of the many already provided. Cobra can never do this.

Corba is also not designed to be a command compositor at all, which is really unfortunate because they missed a golden opportunity there. Bonzai branches can be imported and composed into other branches and monoliths with just a few lines of Go. Registries of Bonzai commands can be easily inferred from dependencies on the bonzai package and creators are free to compose their monoliths from a rich eco-system of Bonzai branches and commands. Bonzai allows creation of Go multicall binary monoliths (like BusyBox) to be made easily, and from a diverse, modular, importable, composable sources. Such is simply not possible with Cobra and never will be.

Cobra buys into the broken boomer "getopt" view of the world requiring people to remember all sorts of ungodly different combinations of dashes and equals signs hoping things will just work. Bonzai takes a no-dashes approach promoting much cleaner command lines with context and promotion of domain specific languages (created with PEGN, scan.X, or others) that easily translate directly to chat and other command-line interfaces for most humans without much need to look up the documentation, which, by the way, is embedded in the Bonzai command tree.

Cobra provides minimal, unappealing command documentation that is virtually unreadable in source form. And Cobra provides no means of markup or use of color and doesn't even promote the same look and feel of manual page documentation. Bonzai has its own subset of Markdown, BonzaiMark, respects the well established readability of manual pages, and allows for the creation of elegant documentation that can be viewed from the command line or easily from a local browser on the same computer running the command. And the source containing the documentation is as easy to read as the documentation itself.

In short, Cobra got us a long way, but has proved to be so laden with crushing technical debt from failed base design decisions that it simply is not sustainable given current modern expectations for good user interfaces and documentation. Bonzai is a fresh, extensible, sustainable, human-friendly command compositor to take us into the future of command line interfaces, for everyone.

What People Are Saying

"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." (tadasv123)

Example GitHub Template

https://github.com/rwxrob/foo

Design Considerations

  • Promote high-level package library API calls over Cmd bloat.

    Code belongs in package libraries, not Cmds.

    While Bonzai allows for rapid applications development by putting everything initially in Cmd Call first-class function, Cmds are most designed for documentation and completion, not heavy Call implementations (even though many will organically start there from people's personal Bonzai trees).

    Eventually, most Call implementations should be moved into their own package libraries, perhaps even in the same Go module. Cmds should never communicate with each other directly (other than calling one another on occasion). While the idea of adding a Channel attribute was intriguing, it quickly became clear that doing so would promote undesirable tight coupling --- even with channels --- between specific commands.

  • Cmds should be very light.

    Most Cmds should assign their first-class Call function to one that lightly wraps a similar function signature in a callable, high-level library that works entirely independently from the bonzai package. It's best to promote strong support for sustainable API packages.

  • Only bash completion and shell.Cmd planned.

    If it doesn't work with complete -C or equivalent then just run the Bonzai command tree monolith as a temporary shell (shell.Cmd) and use its cross-platform support for tab completion.

    Zsh, Powershell, and Fish have no equivalent to complete -C (which allows any executable to provide its own completion). This forces inferior dependencies on overly verbose external "completer" scripts written in only those languages for those specific shells. This dependency completely negates any possibility of providing modular completers and composable commands that carry their own completion logic. This one objective fact alone should give everyone pause before opting to use one of these inferior shells for their command line interactions.

    Bonzai commands leverage this best-of-breed completion functionality of bash to provide an unlimited number of completion methods and combinations. The equivalent implementations, perhaps as an export collected from all composed commands providing their shell equivalent of completion scripts, would be preposterously large just for its basic completion tree). Instead, Bonzai uses Go itself to manage that completion --- drawing on a rich collection of completers included in the standard Bonzai module --- and provides documented shortcut aliases when completion is not available (h|help, for example).

  • Bonzai commands may default to shell.Cmd or help.Cmd

    These provide help information and optional interactive assistance including tab completion in runtime environments that do not have complete -C foo foo enabled.

    shell.Cmd is still under development and likely will be for a while

  • One major use case is to replace shell scripts in "dot files" collections.

    By creating a cmd subdirectory of a dot files repo a multi-call Bonzai command named cmd can be easily maintained and added to just as quickly as any shell script. This has the added bonus of allowing others to quickly add one of your little commands with just a simple import (for example, import github.com/rwxrob/dot/cmd and then cmd.Isosec) from their own cmd monoliths. This also enables the fastest possible prototyping of code that would otherwise require significant, problematic mocks. Developers can work out the details of a thing just as fast as with shell scripting --- but with the power of all the Go standard library --- and then factor out their favorites as they grow into their own Bonzai command repos. This approach keeps "Go on the brain" (instead of having to port a bunch of bash later) and promotes the massive benefits of rapid applications development the fullest extent.

  • Use either foo.Cmd or cmd.Foo convention.

    People may decide to put all their Bonzai commands into a single cmd package or to put each command into its own package. Both are perfectly acceptable and allow the developer making the import to alias the packages as needed using Go's excellent package import design.

Style Guidelines

  • Everything through go fmt or equiv, no exceptions
  • In Vim set textwidth=72 (not 80 to line numbers fit)
  • Use /* */ for package documentation comment, // elsewhere
  • Smallest possible names for given scope while still clear
  • Favor additional packages (possibly in internal) over long names
  • Package globals that will be used a lot can be single capital
  • Must be good reason to use more than 4 character pkg name
  • Avoid unnecessary comments

Acknowledgements

The https://twitch.tv/rwxrob community has been constantly involved with the development of this project, making suggestions about everything from my use of init, to the name "bonzai". While all their contributions are too numerous to name specifically, they more than deserve a huge thank you here.

Copyright 2022 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

Overview

Package bonzai provides a rooted node tree of commands and singular parameters making tab completion a breeze and complicated applications much easier to intuit without reading all the docs. Documentation is embedded with each command removing the need for separate man pages and such and can be viewed as text or a locally served web page.

Rooted Node Tree

Commands and parameters are linked to create a rooted node tree of the following types of nodes:

  • Leaves with a method and optional parameters
  • Branches with leaves, other branches, and a optional method
  • Parameters, single words that are passed to a leaf command

Index

Examples

Constants

This section is empty.

Variables

View Source
var Aliases = make(map[string][]string)

Aliases allows Bonzai tree developers to create aliases (similar to shell aliases) that are directly translated into arguments to the Bonzai tree executable by overriding the os.Args in a controlled way. The value of an alias is always a slice of strings that will replace the os.Args[2:]. A slice is used (instead of a string parsed with strings.Fields) to ensure that hard-coded arguments containing whitespace are properly handled.

View Source
var DefaultConfigurer = new(config.Configurer)

DefaultConfigurer is assigned to the Cmd.Root.Config during Cmd.Run. It is conventional for only Cmd.Root to have a Configurer defined.

View Source
var DoNotExit bool

DoNotExit effectively disables Exit and ExitError allowing the program to continue running, usually for test evaluation.

View Source
var ExeName string

ExeName holds just the base name of the executable without any suffix (ex: .exe) and is set at init() time (see ExePath).

View Source
var ExePath string

ExePath holds the full path to the current running process executable which is determined at init() time by calling os.Executable and passing it to path/filepath.EvalSymlinks to ensure it is the actual binary executable file. Errors are reported to stderr, but there should never be an error logged unless something is wrong with the Go runtime environment.

Functions

func ArgsFrom

func ArgsFrom(line string) []string

ArgsFrom returns a list of field strings split on space with an extra trailing special space item appended if the line has any trailing spaces at all signifying a definite word boundary and not a potential prefix.

Example
package main

import (
	"fmt"

	"github.com/rwxrob/bonzai"
)

func main() {
	fmt.Printf("%q\n", bonzai.ArgsFrom(`greet  hi french`))
	fmt.Printf("%q\n", bonzai.ArgsFrom(`greet hi   french `))
}
Output:
["greet" "hi" "french"]
["greet" "hi" "french" ""]

func ArgsOrIn added in v0.0.29

func ArgsOrIn(args []string) string

ArgsOrIn takes an slice or nil as argument and if the slice has any length greater than 0 returns all the argument joined together with a single space between them. Otherwise, will read standard input until end of file reached (Cntl-D).

Example (Args_Joined)
package main

import (
	"fmt"

	"github.com/rwxrob/bonzai"
)

func main() {

	fmt.Println(bonzai.ArgsOrIn([]string{"some", "thing"}))

}
Output:
some thing
Example (Read_Nil)
package main

import (
	"fmt"
	"os"

	"github.com/rwxrob/bonzai"
)

func main() {

	orig := os.Stdin
	defer func() { os.Stdin = orig }()
	os.Stdin, _ = os.Open(`testdata/in`)

	fmt.Println(bonzai.ArgsOrIn(nil))

}
Output:
some thing
Example (Read_Zero_Args)
package main

import (
	"fmt"
	"os"

	"github.com/rwxrob/bonzai"
)

func main() {

	orig := os.Stdin
	defer func() { os.Stdin = orig }()
	os.Stdin, _ = os.Open(`testdata/in`)

	fmt.Println(bonzai.ArgsOrIn([]string{}))

}
Output:
some thing

func AutoUpdate added in v0.0.19

func AutoUpdate(version, versurl, binurl, sumurl string)

AutoUpdate automatically updates the current process executable version by starting a goroutine that checks the current semantic version (version) against a remote one (versurl) and calling ReplaceSelf with the URL of the binary (binurl) and checksum (sumurl) if and update is needed. Note that the binary will often be named quite differently than the name of the currently running executable (ex: foo-mac -> foo, foo-linux.

If a URL to a checksum file (sumurl) is not empty will optionally validate the new version downloaded against the checksum before replacing the currently running process executable with the new one. The format of the checksum file is the same as that output by any of the major checksum commands (sha512sum, for example) with one or more lines beginning with the checksum, whitespace, and then the name of the file. A single checksum file can be used for multiple versions but the most recent should always be at the top. When the update completes a message notifying of the update is logged to stderr.

The function will fail silently under any of the following conditions:

  • current user does not have write access to executable
  • unable to establish a network connection
  • checksum provided does not match

Since AutoUpdate happens in the background no return value is provided. To enable logging of the update process (presumably for debugging) add the AutoUpdate flag to the Trace flags (trace.Flags|=trace.AutoUpdate). Also see Cmd.AutoUpdate.

func CompareUpdated added in v0.0.21

func CompareUpdated(current int, remoteURL string) int

CompareUpdated compares the current isosec integer (second at GMT) to that retrieved from the URL (usually pointing to a file called UPDATED) which must return nothing but a single isosec integer (which is unmarshaled as a JSON number). Returns 1 if current is more recent, 0 if they are equal, -1 if current is older, and -2 if unable to determine (which will also log any error encountered).

Example
package main

import (
	"fmt"
	"net/http"

	ht "net/http/httptest"

	"github.com/rwxrob/bonzai"
)

func main() {

	handler := http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintf(w, `20220322080540`)
		})
	older := ht.NewServer(handler)
	defer older.Close()

	handler = http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintf(w, `20220322080546`)
		})
	newer := ht.NewServer(handler)
	defer newer.Close()

	handler = http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintf(w, `20220322080542`)
		})
	same := ht.NewServer(handler)
	defer same.Close()

	fmt.Println(bonzai.CompareUpdated(20220322080542, older.URL))
	fmt.Println(bonzai.CompareUpdated(20220322080542, newer.URL))
	fmt.Println(bonzai.CompareUpdated(20220322080542, same.URL))
	fmt.Println(bonzai.CompareUpdated(20220322080542, "foobar"))

}
Output:
-1
1
0
-2

func CompareVersions added in v0.0.21

func CompareVersions(current, remoteURL string) int

CompareVersions compares the current semantic version string to that retrieved from the URL (usually pointing to a file called VERSION) which must return nothing but a single semantic version. Like semver.Compare returns 0 if they are the same, 1 if current is more recent, -1 if current is behind remote, and -2 if unable to retrieve the remote semver string (in which case an error is also logged).

Example
package main

import (
	"fmt"
	"net/http"

	ht "net/http/httptest"

	"github.com/rwxrob/bonzai"
)

func main() {

	handler := http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintf(w, `"v0.0.1"`)
		})
	older := ht.NewServer(handler)
	defer older.Close()

	handler = http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintf(w, `"v0.1.0"`)
		})
	newer := ht.NewServer(handler)
	defer newer.Close()

	handler = http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintf(w, `"v0.0.2"`)
		})
	same := ht.NewServer(handler)
	defer same.Close()

	fmt.Println(bonzai.CompareVersions(`v0.0.2`, older.URL))
	fmt.Println(bonzai.CompareVersions(`v0.0.2`, newer.URL))
	fmt.Println(bonzai.CompareVersions(`v0.0.2`, same.URL))
	fmt.Println(bonzai.CompareVersions(`v0.0.2`, "foobar"))

}
Output:
1
-1
0
-2

func Exec added in v0.0.21

func Exec(args ...string) error

Exec (not to be confused with Execute) will check for the existence of the first argument as an executable on the system and then execute it using syscall.Exec(), which replaces the currently running program with the new one in all respects (stdin, stdout, stderr, process ID, signal handling, etc).

Note that although this is exceptionally faster and cleaner than calling any of the os/exec variations it may be less compatible with different operating systems.

func Exit

func Exit()

Exit calls os.Exit(0) unless DoNotExit has been set to true. Cmds should never call Exit themselves returning a nil error from their Methods instead.

func ExitError

func ExitError(err ...interface{})

ExitError prints err and exits with 1 return value unless DoNotExit has been set to true. Commands should usually never call ExitError themselves returning an error from their Method instead.

func ExitOff

func ExitOff()

ExitOff sets DoNotExit to false.

func ExitOn

func ExitOn()

ExitOn sets DoNotExit to true.

func Files added in v0.0.19

func Files(dir string) []string

Files returns a slice of strings matching the names of the files within the given directory adding a slash to the end of any directories and escaping any spaces by adding backslash. Note that this function assumes forward slash path separators since completion is only supported on operating systems where such is the case.

Example
package main

import (
	"github.com/rwxrob/bonzai"
	"github.com/rwxrob/fn/each"
)

func main() {
	each.Println(bonzai.Files("testdata/files"))
}
Output:
testdata/files/bar
testdata/files/blah
testdata/files/dir1/
testdata/files/foo
testdata/files/other
testdata/files/some
Example (Empty)
package main

import (
	"os"

	"github.com/rwxrob/bonzai"
	"github.com/rwxrob/fn/each"
)

func main() {
	os.Chdir("testdata/files")
	defer os.Chdir("../..")
	each.Println(bonzai.Files(""))
}
Output:
bar
blah
dir1/
foo
other
some
Example (Not_Directory)
package main

import (
	"fmt"

	"github.com/rwxrob/bonzai"
)

func main() {
	fmt.Println(bonzai.Files("none"))
}
Output:
[]
Example (Spaces)
package main

import (
	"github.com/rwxrob/bonzai"
	"github.com/rwxrob/fn/each"
)

func main() {
	each.Println(bonzai.Files("testdata/files/dir1"))
}
Output:
testdata/files/dir1/some\ thing

func ReplaceSelf added in v0.0.19

func ReplaceSelf(url, checksum string) error

ReplaceSelf replaces the current running executable at its current location with the successfully retrieved file at the specified URL or file path and duplicates the original files permissions. Only http and https URLs are currently supported. If not empty, a checksum file will be fetched from sumurl and used to validate the download before making the replacement. For security reasons, no backup copy of the replaced executable is kept. Also see AutoUpdate.

func Run added in v0.0.21

func Run(args ...string) error

Run checks for existence of first argument as an executable on the system and then runs it without exiting in a way that is supported across different operating systems. The stdin, stdout, and stderr are connected directly to that of the calling program. Use more specific exec alternatives if intercepting stdout and stderr are desired. Also see Exec.

Types

type Cmd

type Cmd struct {
	Name        string            `json:"name,omitempty"`
	Aliases     []string          `json:"aliases,omitempty"`
	Summary     string            `json:"summary,omitempty"`
	Usage       string            `json:"usage,omitempty"`
	Version     string            `json:"version,omitempty"`
	Updated     int               `json:"updated,omitempty"` // isosec
	Copyright   string            `json:"copyright,omitempty"`
	License     string            `json:"license,omitempty"`
	Description string            `json:"description,omitempty"`
	Site        string            `json:"site,omitempty"`
	Source      string            `json:"source,omitempty"`
	Issues      string            `json:"issues,omitempty"`
	Other       map[string]string `json:"issues,omitempty"`
	Commands    []*Cmd            `json:"commands,omitempty"`
	Params      []string          `json:"params,omitempty"`
	Hidden      []string          `json:"hide,omitempty"`

	Completer comp.Completer  `json:"-"`
	Conf      conf.Configurer `json:"-"`

	Root   *Cmd   `json:"-"`
	Caller *Cmd   `json:"-"`
	Call   Method `json:"-"`
	// contains filtered or unexported fields
}

Cmd is a struct the easier to use and read when creating implementations of the Command interface.

Params

Params require a Method. While Methods may receive any number of arguments, Params are a way of helping completion for regular parameters. Standard completion will not recursively complete multiple params, one param per completion.

func (*Cmd) Add

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

Add creates a new Cmd and sets the name and aliases and adds to Commands returning a reference to the new Cmd. The name must be first.

func (*Cmd) Branch added in v0.0.26

func (x *Cmd) Branch() string

Branch returns the dotted path to this command location within the parent tree. This is useful for associating configuration and other data specifically with this command. The branch 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.

Example
package main

import (
	"fmt"
	"os"

	"github.com/rwxrob/bonzai"
)

func main() {
	bonzai.ExitOff()

	z := new(bonzai.Cmd)
	c := z.Add("some")
	//fmt.Print(z.Commands[0].Name)
	c = c.Add("thing")
	//fmt.Print(z.Commands[0].Commands[0].Name)
	c = c.Add("deep")
	//fmt.Print(z.Commands[0].Commands[0].Commands[0].Name)

	c.Call = func(x *bonzai.Cmd, _ ...string) error {
		fmt.Println(x.Branch())
		return nil
	}

	defer func() { args := os.Args; os.Args = args }()
	os.Args = []string{"z", "some", "thing", "deep"}

	z.Run()

}
Output:
some.thing.deep

func (*Cmd) Cmd

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

Cmd looks up a given Command by name or name from Aliases.

func (*Cmd) CmdNames

func (x *Cmd) CmdNames() []string
Example
package main

import (
	"fmt"

	"github.com/rwxrob/bonzai"
)

func main() {
	foo := new(bonzai.Cmd)
	foo.Add("bar")
	foo.Add("blah")
	foo.Add("other")
	fmt.Println(foo.CmdNames())
}
Output:
[bar blah other]

func (*Cmd) GetCaller added in v0.0.7

func (x *Cmd) GetCaller() comp.Command

GetCaller fulfills the comp.Command interface.

func (*Cmd) GetCommands

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

GetCommands fulfills the comp.Command interface.

Example
package main

import (
	"fmt"

	"github.com/rwxrob/bonzai"
)

func main() {
	foo := new(bonzai.Cmd)
	foo.Add("bar")
	foo.Add("blah")
	foo.Add("other")
	fmt.Println(foo.GetCommands())
}
Output:
[bar blah other]

func (*Cmd) GetCompleter

func (x *Cmd) GetCompleter() comp.Completer

GetCompleter fulfills the Command interface.

func (*Cmd) GetHidden

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

GetHidden fulfills the comp.Command interface.

func (*Cmd) GetName

func (x *Cmd) GetName() string

GetName fulfills the comp.Command interface.

func (*Cmd) GetOther added in v0.0.7

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

GetOther fulfills the comp.Command interface.

func (*Cmd) GetParams

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

GetParams fulfills the comp.Command interface.

Example
package main

import (
	"fmt"

	"github.com/rwxrob/bonzai"
)

func main() {
	foo := new(bonzai.Cmd)
	foo.Params = []string{"box", "bing", "and"}
	fmt.Println(foo.GetParams())
}
Output:
[box bing and]

func (*Cmd) IsHidden

func (x *Cmd) IsHidden(name string) bool

IsHidden returns true if the specified name is in the list of Hidden commands.

func (*Cmd) MissingConfig added in v0.0.29

func (x *Cmd) MissingConfig(path string) error

MissingConfig returns an error showing the expected configuration entry that is missing from the given path.

func (*Cmd) Param

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

Param returns Param matching name if found, empty string if not.

func (*Cmd) Q added in v0.0.28

func (x *Cmd) Q(q string) string

Q is a shorter version of x.Conf.Query(x.Root.Name,x.Branch()+"."+q) for convenience.

func (*Cmd) Run

func (x *Cmd) Run()

Run is for running a command within a specific runtime (shell) and performs completion if completion context is detected. Otherwise, it executes the leaf Cmd returned from Seek calling its Method, and then Exits. Normally, Run is called from within main() to convert the Cmd into an actual executable program and normally it exits the program. Exiting can be controlled, however, with ExitOn/ExitOff when testing or for other purposes requiring multiple Run calls. Using Call instead will also just call the Cmd's Call Method without exiting. Note: Only bash runtime ("COMP_LINE") is currently supported, but others such a zsh and shell-less REPLs are planned.

func (*Cmd) Seek

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

import (
	"fmt"

	"github.com/rwxrob/bonzai"
	"github.com/rwxrob/bonzai/inc/help"
)

func main() {

	hello := &bonzai.Cmd{
		Name:   `hello`,
		Params: []string{"there"},
		Call: func(_ *bonzai.Cmd, args ...string) error {
			if len(args) > 0 {
				fmt.Printf("hello %v\n", args[0])
				return nil
			}
			fmt.Println("hello")
			return nil
		},
	}

	hi := &bonzai.Cmd{
		Name:   `hi`,
		Params: []string{"there", "ya"},
		Call: func(_ *bonzai.Cmd, args ...string) error {
			if len(args) > 0 {
				fmt.Printf("hi %v\n", args[0])
				return nil
			}
			fmt.Println("hi")
			return nil
		},
	}

	yo := &bonzai.Cmd{
		Name: `yo`,
		Call: func(x *bonzai.Cmd, args ...string) error {
			fmt.Println("yo")
			return nil
		},
	}

	salut := &bonzai.Cmd{
		Name:   `salut`,
		Params: []string{"la"},
		Call: func(_ *bonzai.Cmd, args ...string) error {
			if len(args) > 0 {
				fmt.Printf("salut %v\n", args[0])
				return nil
			}
			fmt.Println("salut")
			return nil
		},
	}

	french := &bonzai.Cmd{
		Name:     `french`,
		Aliases:  []string{"fr"},
		Commands: []*bonzai.Cmd{help.Cmd, salut},
	}

	greet := &bonzai.Cmd{
		Name:     `greet`,
		Commands: []*bonzai.Cmd{help.Cmd, yo, hi, hello, french},
	}

	cmd, args := greet.Seek(bonzai.ArgsFrom(`hi there`))
	fmt.Printf("%v %q\n", cmd.Name, args)

	cmd, args = greet.Seek(bonzai.ArgsFrom(`french salut`))
	fmt.Printf("%v %q\n", cmd.Name, args)

	cmd, args = greet.Seek(bonzai.ArgsFrom(`french salut `))
	fmt.Printf("%v %q\n", cmd.Name, args)

	cmd, args = greet.Seek(bonzai.ArgsFrom(`french h`))
	fmt.Printf("%v %q\n", cmd.Name, args)

	cmd, args = greet.Seek(bonzai.ArgsFrom(`french help`))
	fmt.Printf("%v %q\n", cmd.Name, args)

}
Output:
hi ["there"]
salut []
salut [""]
french ["h"]
help []

func (*Cmd) Unimplemented added in v0.0.24

func (x *Cmd) Unimplemented() error

Unimplemented returns an error with a single-line usage string.

func (*Cmd) UsageError added in v0.0.7

func (x *Cmd) UsageError() error

UsageError returns an error with a single-line usage string.

type Method

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

Method defines the main code to execute for a command (Cmd). By convention the parameter list should be named "args" if there are args expected and underscore (_) if not. Methods must never write error output to anything but standard error and should almost always use the log package to do so.

Directories

Path Synopsis
anim module
choose module
cmds
help module
kimono module
rayfish module
sunrise module
vars module
depends module
ds module
dtime module
edit module
fn module
tr module
futil module
github module
inc
Package inc contains a predefined set of popular commands that some may choose to include/compose into their Bonzai command trees.
Package inc contains a predefined set of popular commands that some may choose to include/compose into their Bonzai command trees.
is module
json module
ast
funcs module
renderers/man module
viewers/less module
old
is
Package z (often imported as "is") defines the Bonzai Parsing Expression Grammar Notation (BPEGN) (aka "Bonzai Scanner Expect" language) implemented entirely using Go types (mostly slices and structs).
Package z (often imported as "is") defines the Bonzai Parsing Expression Grammar Notation (BPEGN) (aka "Bonzai Scanner Expect" language) implemented entirely using Go types (mostly slices and structs).
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
Package trace provides a tracing framework for use by Bonzai applications.
Package trace provides a tracing framework for use by Bonzai applications.
uniq module
vars module
web module
yaml module
yq module
yt module

Jump to

Keyboard shortcuts

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