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

bonzai

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Feb 18, 2022 License: Apache-2.0 Imports: 6 Imported by: 61

README

Bonzai, Well-Tended CLI Command Trees for Gophers

Bonzai commands are unlike anything you've probably encountered so far, no getopt dashes, no ugly commander interface to learn, no 12637 lines of shell tab completion bloat to source before it will work, just well manicured commands organized into rooted node trees for your command-line, nested-tab-completing enjoyment with built-in (optional) help documentation that can easily be marshalled into JSON and presented either as text or well-styled HTML. Think "readthedocs" but served locally from your Bonzai command-line apps themselves.

"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.

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.

Then, of course, there's the image of Buckaroo invoked every time we say the name. In fact, I think we have our new theme song.

Example GitHub Template

https://github.com/rwxrob/template-bonzai

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 Methods, Cmds are most designed for documentation and completion, not heavy Method implementations. Eventually, most Method implementations should be moved into their own package libraries, perhaps even in the same Go module. Cmds should never be communicating with each other directly. While the idea of adding a Channel attribute was intriguing, it quickly became clear that doing so would promote too much code and tight coupling --- even with channels --- between specific commands. Cmds should be very light. In fact, most Cmds should assign their Method directly from one matching the Method function signature in a callable, high-level library.

Copyright 2022 Robert S. Muhlestein (mailto:rob@rwx.gg)
Licensed under Apache-2.0

"Bonzai" and "bonzai" are legal trademarks of Robert S. Muhlestein but can be used freely to refer to the cmdbox 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 DoNotExit bool

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

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 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.

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"`
	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"`
	Commands    []*Cmd         `json:"commands,omitempty"`
	Params      []string       `json:"params,omitempty"`
	Hidden      []string       `json:"hide,omitempty"`
	Completer   comp.Completer `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) 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) 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) 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) Param

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

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

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/cmd"
)

func main() {

	hello := &bonzai.Cmd{
		Name:   `hello`,
		Params: []string{"there"},
		Call: func(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(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(args ...string) error {
			fmt.Println("yo")
			return nil
		},
	}

	salut := &bonzai.Cmd{
		Name:   `salut`,
		Params: []string{"la"},
		Call: func(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{cmd.Help, salut},
	}

	greet := &bonzai.Cmd{
		Name:     `greet`,
		Commands: []*bonzai.Cmd{cmd.Help, 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 []

type Method

type Method func(args ...string) error

Method defines the main code to execute for a command (Cmd). By convention the parameter list should be names "args" if there are args expected and "none" 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
Package cmd contains a predefined set of popular commands that some may choose to compose into their Bonzai command trees, notably cmd.Help.
Package cmd contains a predefined set of popular commands that some may choose to compose into their Bonzai command trees, notably cmd.Help.
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
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
yt module

Jump to

Keyboard shortcuts

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