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

Z

package
v0.6.2 Latest Latest
Warning

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

Go to latest
Published: Apr 11, 2022 License: Apache-2.0 Imports: 23 Imported by: 0

Documentation

Overview

Package Z (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

View Source
const (
	Paragraph = iota + 1
	Numbered
	Bulleted
	Verbatim
)

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 AllowPanic = false

AllowPanic disables TrapPanic stopping it from cleaning panic errors.

View Source
var Columns = int(term.WinSize.Col)

Columns is the number of bytes (not runes) at which Wrap will wrap. By default detects the terminal width (if possible) otherwise keeps 80 standard (see rwxrob/term.WinSize). Bonzai command tree creator can change this for every composite command imported their application in this one place.

View Source
var Commands map[string][]any

Commands contains the commands to lookup when Run-ing an executable in "multicall" mode. Each value must begin with a *Cmd and the rest will be assumed to be string arguments to prepend. See Run.

Conf may be optionally assigned any implementation of a bonzai.Configurer. Once assigned it should not be reassigned at any later time during runtime. Certain Bonzai branches and commands may require Z.Conf to be defined and those that do generally require the same implementation throughout all of runtime. Commands that require Z.Conf should set ReqConf to true. Other than the exceptional case of configuration commands that fulfill bonzai.Configurer (and usually assign themselves to Z.Conf at init() time), commands must never require a specific implementation of bonzai.Configurer. This encourages command creators and Bonzai tree composers to centralize on a single form of configuration without creating brittle dependencies and tight coupling. Configuration persistence can be implemented in any number of ways without a problem and Bonzai trees simply need to be recompiled with a different bonzai.Configurer implementation to switch everything that depends on configuration.

View Source
var DoNotExit bool

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

View Source
var EscThese = " \r\t\n|&;()<>![]"

EscThese is set to the default UNIX shell characters which require escaping to be used safely on the terminal. It can be changed to suit the needs of different host shell environments.

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.

View Source
var IndentBy = 7

IndentBy is the number of spaces to indent in Indent. Default is 7. Bonzai command tree creator can change this for every composite command imported their application in this one place.

View Source
var TrapPanic = func() {
	if !AllowPanic {
		if r := recover(); r != nil {
			log.Println(r)
			os.Exit(1)
		}
	}
}

TrapPanic recovers from any panic and more gracefully displays the panic by logging it before exiting with a return value of 1.

View Source
var UsageFunc = InferredUsage

UsageFunc is the default first-class function called if a Cmd that does not already define its own when usage information is needed (see bonzai.UsageFunc and Cmd.UsageError for more). By default, InferredUsage is assigned.

It is used to return a usage summary. Generally, it should only return a single line (even if that line is very long). Developers are encouraged to refer users to their chosen help command rather than producing usually long usage lines. If only the word "usage" needs to be changed (for a given language) consider UsageText instead. Note that most developers will simply change the Usage string when they do not want the default inferred usage string.

View Source
var UsageText = `usage`

UsageText is used for one-line UsageErrors. It's exported to allow for different languages.

Vars may be optionally assigned any implementation of a bonzai.CacheMap. Once assigned it should not be reassigned at any later time during runtime. Certain Bonzai branches and commands may require Z.Vars to be defined and those that do generally require the same implementation throughout all of runtime. Commands that require Z.Vars should set ReqVars to true. Other than the exceptional case of configuration commands that fulfill bonzai.CacheMap (and usually assign themselves to Z.Vars at init() time), commands must never require a specific implementation of bonzai.CacheMap. This encourages command creators and Bonzai tree composers to centralize on a single form of caching without creating brittle dependencies and tight coupling. Caching persistence can be implemented in any number of ways without a problem and Bonzai trees simply need to be recompiled with a different bonzai.CacheMap implementation to switch everything that depends on cached variables.

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"

	Z "github.com/rwxrob/bonzai/z"
)

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

func ArgsOrIn

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"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {

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

}
Output:
some thing
Example (Read_Nil)
package main

import (
	"fmt"
	"os"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {

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

	fmt.Println(Z.ArgsOrIn(nil))

}
Output:
some thing
Example (Read_Zero_Args)
package main

import (
	"fmt"
	"os"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {

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

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

}
Output:
some thing

func Emph

func Emph(buf string) string

Emph renders BonzaiMark emphasis spans specifically for VT100-compatible terminals (which almost all are today):

*Italic*
**Bold**
***BoldItalic***
<under> (keeping brackets)

See Mark for block formatting and rwxrob/term for terminal rendering.

Example (Basics)
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
	"github.com/rwxrob/term"
)

func main() {

	// Emph observes the rwxrob/term escapes
	// (see package documentation for more)

	term.Italic = `<italic>`
	term.Bold = `<bold>`
	term.BoldItalic = `<bolditalic>`
	term.Under = `<under>`
	term.Reset = `<reset>`

	fmt.Println(Z.Emph("*ITALIC*"))
	fmt.Println(Z.Emph("**BOLD**"))
	fmt.Println(Z.Emph("***BOLDITALIC***"))
	fmt.Println(Z.Emph("<UNDER>")) // keeps brackets

}
Output:
<italic>ITALIC<reset>
<bold>BOLD<reset>
<bolditalic>BOLDITALIC<reset>
<<under>UNDER<reset>>

func Emphf

func Emphf(a string, f ...any) string

Emphf calls fmt.Sprintf on the string before passing it to Emph.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	fmt.Println(Z.Emphf(`some *%v* thing`, "italic"))
}
Output:
some <italic>italic<reset> thing

func Esc

func Esc(s string) string

Esc returns a shell-escaped version of the string s. The returned value is a string that can safely be used as one token in a shell command line.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	fmt.Println(Z.Esc("|&;()<>![]"))
	fmt.Printf("%q", Z.Esc(" \n\r"))
}
Output:
\|\&\;\(\)\<\>\!\[\]
"\\ \\\n\\\r"

func EscAll

func EscAll(args []string) []string

EscAll calls Esc on all passed strings.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	list := []string{"so!me", "<here>", "other&"}
	fmt.Println(Z.EscAll(list))
}
Output:
[so\!me \<here\> other\&]

func Exec

func Exec(args ...string) error

Exec 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 all architectures that Go supports. The stdin, stdout, and stderr are connected directly to that of the calling program. Sometimes this is insufficient and the UNIX-specific SysExec is preferred. For example, when handing over control to a terminal editor such as Vim.

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 InWrap

func InWrap(in string) string

InWrap combines both Wrap and Indent.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	defer func() { Z.IndentBy = Z.IndentBy }()
	indent := Z.IndentBy
	col := Z.Columns
	Z.Columns = 10
	Z.IndentBy = 4
	fmt.Printf("%q", Z.InWrap("some\nthat is \n  indented"))
	Z.IndentBy = indent
	Z.Columns = col
}
Output:
"    some\n    that\n    is\n    indented\n"

func InWrapf

func InWrapf(a string, f ...any) string

InWrapf calls fmt.Sprintf on the string before passing it to InWrap.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	in := Z.IndentBy
	col := Z.Columns
	Z.IndentBy = 3
	Z.Columns = 10
	fmt.Println(
		Z.InWrapf("-----\nindented by %v here and wrapped at %v",
			Z.IndentBy, Z.Columns,
		))
	Z.IndentBy = in
	Z.Columns = col
	// -----
	//    indented
	//    by 3
	//    here
	//    and
	//    wrapped
	//    at 10
}

func Indent

func Indent(in string) string

Indent indents the number of spaces set by IndentBy.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	indent := Z.IndentBy
	col := Z.Columns
	Z.Columns = 10
	Z.Columns = 10
	Z.IndentBy = 4
	fmt.Printf("%q", Z.Indent("some\nthat is \n  indented"))
	Z.IndentBy = indent
	Z.Columns = col
}
Output:
"    some\n    that is \n      indented\n"

func Indentf

func Indentf(a string, f ...any) string

Indentf calls fmt.Sprintf on the string before passing it to Indent.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	in := Z.IndentBy
	Z.IndentBy = 3
	fmt.Println(Z.Indentf("-----\nindented by %v here", Z.IndentBy))
	Z.IndentBy = in
}
Output:
-----
   indented by 3 here

func InferredUsage

func InferredUsage(cmd bonzai.Command) string

InferredUsage returns a single line of text summarizing only the Commands (less any Hidden commands), Params, and Aliases. If a Cmd is currently in an invalid state (Params without Call, no Call and no Commands) a string beginning with ERROR and wrapped in braces ({}) is returned instead. The string depends on the current language (see lang.go). Note that aliases does not include package Z.Aliases.

Example (Commands)
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	x := &Z.Cmd{
		Commands: []*Z.Cmd{
			&Z.Cmd{Name: "foo", Aliases: []string{"f"}},
			&Z.Cmd{Name: "bar"},
		},
	}
	fmt.Println(Z.InferredUsage(x))
}
Output:
((f|foo)|bar)
Example (Commands_and_Params)
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	x := &Z.Cmd{
		Params: []string{"p1", "p2"},
		Commands: []*Z.Cmd{
			&Z.Cmd{Name: "foo", Aliases: []string{"f"}},
			&Z.Cmd{Name: "bar"},
		},
		Call: func(_ *Z.Cmd, _ ...string) error { return nil },
	}
	fmt.Println(Z.InferredUsage(x))
}
Output:
((p1|p2)?|((f|foo)|bar))
Example (Error_No_Call_or_Command)
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	x := &Z.Cmd{
		Params: []string{"p1", "p2"},
	}
	fmt.Println(Z.InferredUsage(x))
}
Output:
{ERROR: neither Call nor Commands defined}
Example (Error_Params_without_Call)
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	x := &Z.Cmd{
		Params: []string{"p1", "p2"},
		Commands: []*Z.Cmd{
			&Z.Cmd{Name: "foo", Aliases: []string{"f"}},
			&Z.Cmd{Name: "bar"},
		},
	}
	fmt.Println(Z.InferredUsage(x))
}
Output:
{ERROR: Params without Call: p1, p2}
Example (Min_3_Param)
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	x := &Z.Cmd{
		Params:  []string{"p1", "p2"},
		MinParm: 3,
		Call:    func(_ *Z.Cmd, _ ...string) error { return nil },
	}
	fmt.Println(Z.InferredUsage(x))
}
Output:
(p1|p2){3,}
Example (Min_One_Param)
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	x := &Z.Cmd{
		Params:  []string{"p1", "p2"},
		MinParm: 1,
		Call:    func(_ *Z.Cmd, _ ...string) error { return nil },
	}
	fmt.Println(Z.InferredUsage(x))
}
Output:
(p1|p2)+
Example (Optional_Param)
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	x := &Z.Cmd{
		Params: []string{"p1", "p2"},
		Call:   func(_ *Z.Cmd, _ ...string) error { return nil },
	}
	fmt.Println(Z.InferredUsage(x))
}
Output:
(p1|p2)?

func Lines

func Lines(in string) []string

Lines returns the string converted into a slice of lines.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	fmt.Printf("%q\n", Z.Lines("line one\nline two"))
}
Output:
["line one" "line two"]

func Mark

func Mark(in string) string

Mark parses the input as a string of BonzaiMark, multiple blocks with optional emphasis (see Blocks and Emph) and applies IndentBy and Columns wrapping to it.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {

	in := `
			Must have *another* block before verbatim:

			     Now we can start
			     a Verbatim
			     block.

			     Which can have blank lines, even.

			And back to a paragraph block.

			* **foo**
			* bar

			And a numbered list

			1. Something
			2. here

			That's really it.

			`

	fmt.Println("----------------------")
	fmt.Print(Z.Mark(in))
	fmt.Println("----------------------")

}
Output:
----------------------
       Must have <italic>another<reset> block before verbatim:

           Now we can start
           a Verbatim
           block.

           Which can have blank lines, even.

       And back to a paragraph block.

       * <bold>foo<reset>
       * bar

       And a numbered list

       1. Something
       2. here

       That's really it.

----------------------
Example (Simple)
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {

	fmt.Print(Z.Mark(`**foo**`))

}
Output:
<bold>foo<reset>

func Markf

func Markf(a string, f ...any) string

Markf calls fmt.Sprintf on the string before passing it to Mark.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {

	in := `
			Must have *%v* block before verbatim:

			     Now we can start
			     a Verbatim
			     block.

			     Which can have blank lines, even.

			And back to a %v block.

			* **foo**
			* bar

			And a numbered list

			1. Something
			2. here

			That's really it.

			`

	fmt.Println("----------------------")
	fmt.Print(Z.Markf(in, "another", "paragraph"))
	fmt.Println("----------------------")

}
Output:
----------------------
       Must have <italic>another<reset> block before verbatim:

           Now we can start
           a Verbatim
           block.

           Which can have blank lines, even.

       And back to a paragraph block.

       * <bold>foo<reset>
       * bar

       And a numbered list

       1. Something
       2. here

       That's really it.

----------------------

func Out added in v0.4.3

func Out(args ...string) string

Out returns the standard output of the executed command as a string. Errors are logged but not returned.

func PrintEmph

func PrintEmph(a string)

PrintEmph passes string to Emph and prints it.

Example (Basics)
package main

import (
	Z "github.com/rwxrob/bonzai/z"
	"github.com/rwxrob/term"
)

func main() {

	// Emph observes the rwxrob/term escapes
	// (see package documentation for more)

	term.Italic = `<italic>`
	term.Bold = `<bold>`
	term.BoldItalic = `<bolditalic>`
	term.Under = `<under>`
	term.Reset = `<reset>`

	Z.PrintEmph("*ITALIC*\n")
	Z.PrintEmph("**BOLD**\n")
	Z.PrintEmph("***BOLDITALIC***\n")
	Z.PrintEmph("<UNDER>\n") // keeps brackets

}
Output:
<italic>ITALIC<reset>
<bold>BOLD<reset>
<bolditalic>BOLDITALIC<reset>
<<under>UNDER<reset>>

func PrintEmphf

func PrintEmphf(a string, f ...any)

PrintEmphf calls fmt.Sprintf on the string before passing it to Emph and then printing it.

Example
package main

import (
	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	Z.PrintEmphf(`some *%v* thing`, "italic")
}
Output:
some <italic>italic<reset> thing

func PrintInWrap

func PrintInWrap(a string)

PrintInWrap passes string to InWrap and prints it.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	defer func() { Z.IndentBy = Z.IndentBy }()
	indent := Z.IndentBy
	col := Z.Columns
	Z.Columns = 10
	Z.IndentBy = 4
	fmt.Println("-----")
	Z.PrintInWrap("some\nthat is \n  indented")
	Z.IndentBy = indent
	Z.Columns = col
}
Output:
-----
    some
    that
    is
    indented

func PrintInWrapf

func PrintInWrapf(a string, f ...any)

PrintInWrapf calls fmt.Sprintf on the string before passing it to InWrap and then printing it.

Example
package main

import (
	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	in := Z.IndentBy
	col := Z.Columns
	Z.IndentBy = 3
	Z.Columns = 10
	Z.PrintInWrapf("-----\nindented by %v here and wrapped at %v",
		Z.IndentBy, Z.Columns,
	)
	Z.IndentBy = in
	Z.Columns = col
	// -----
	//    indented
	//    by 3
	//    here
	//    and
	//    wrapped
	//    at 10
}

func PrintIndent

func PrintIndent(a string)

PrintIndent passes string to Indent and prints it.

func PrintIndentf

func PrintIndentf(a string, f ...any)

PrintIndentf calls fmt.Sprintf on the string before passing it to Indent and then printing it.

Example
package main

import (
	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	in := Z.IndentBy
	Z.IndentBy = 3
	Z.PrintIndentf("-----\nindented by %v here", Z.IndentBy)
	Z.IndentBy = in
}
Output:
-----
   indented by 3 here

func PrintMark

func PrintMark(a string)

PrintMark passes string to Mark and prints it.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {

	in := `
			Must have *another* block before verbatim:

			     Now we can start
			     a Verbatim
			     block.

			     Which can have blank lines, even.

			And back to a paragraph block.

			* **foo**
			* bar

			And a numbered list

			1. Something
			2. here

			That's really it.

			`

	fmt.Println("----------------------")
	Z.PrintMark(in)
	fmt.Println("----------------------")

}
Output:
----------------------
       Must have <italic>another<reset> block before verbatim:

           Now we can start
           a Verbatim
           block.

           Which can have blank lines, even.

       And back to a paragraph block.

       * <bold>foo<reset>
       * bar

       And a numbered list

       1. Something
       2. here

       That's really it.

----------------------

func PrintMarkf

func PrintMarkf(a string, f ...any)

PrintMarkf calls fmt.Sprintf on the string before passing it to Mark and then printing it.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {

	in := `
			Must have *%v* block before verbatim:

			     Now we can start
			     a Verbatim
			     block.

			     Which can have blank lines, even.

			And back to a %v block.

			* **foo**
			* bar

			And a numbered list

			1. Something
			2. here

			That's really it.

			`

	fmt.Println("----------------------")
	Z.PrintMarkf(in, "another", "paragraph")
	fmt.Println("----------------------")

}
Output:
----------------------
       Must have <italic>another<reset> block before verbatim:

           Now we can start
           a Verbatim
           block.

           Which can have blank lines, even.

       And back to a paragraph block.

       * <bold>foo<reset>
       * bar

       And a numbered list

       1. Something
       2. here

       That's really it.

----------------------

func PrintWrap

func PrintWrap(a string)

PrintWrap passes string to Wrap and prints it.

Example
package main

import (
	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	col := Z.Columns
	Z.Columns = 10
	Z.PrintWrap(`some thing here that is more than 10 characters`)
	Z.Columns = col
}
Output:
some thing
here that
is more
than 10
characters

func PrintWrapf

func PrintWrapf(a string, f ...any)

PrintWrapf calls fmt.Sprintf on the string before passing it to Wrap and then printing it.

func Run

func Run()

Run infers the name of the command to run from the ExeName looked up in the Commands delegates accordingly, prepending any arguments provided in the Cmd.Run. Run produces an "unmapped multicall command" error if no match is found. This is an alternative to the simpler, direct Cmd.Run method from main where only one possible Cmd will ever be the root and allows for BusyBox (https://www.busybox.net) multicall binaries to be used for such things as very light-weight Linux distributions when used "FROM SCRATCH" in containers.

func SysExec

func SysExec(args ...string) error

SysExec 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). Generally speaking, this is only available on UNIX variations. This is exceptionally faster and cleaner than calling any of the os/exec variations, but it can make your code far be less compatible with different operating systems.

func UsageGroup

func UsageGroup(args []string, min, max int) string

UsageGroup uses Bonzai usage notation, a basic form of regular expressions, to describe the arguments allowed where each argument is a literal string (avoid spaces). The arguments are joined with bars (|) and wrapped with parentheses producing a regex group. The min and max are then applied by adding the following regex decorations after the final parenthesis:

  • min=1 max=1 (exactly one) ? - min=0 max=0 (none or many)
  • - min=1 max=0 (one or more) {min,} - min>0 max=0 (min, no max) {min,max} - min>0 max>0 (min and max) {,max} - min=0 max>0 (max, no min)

An empty args slice returns an empty string. If only one arg, then that arg is simply returned and min and max are ignored. Arguments that are empty strings are ignored. No transformation is done to the string itself (such as removing white space).

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	fmt.Println(Z.UsageGroup([]string{"", "foo", "", "bar", "with space"}, 1, 1))
	fmt.Printf("%q\n", Z.UsageGroup([]string{"", ""}, 1, 1))
	fmt.Println(Z.UsageGroup([]string{"one"}, 1, 1))
}
Output:
(foo|bar|with space)
""
one

func Wrap

func Wrap(in string) string

Wrap wraps to Columns width.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	col := Z.Columns
	Z.Columns = 10
	fmt.Println(Z.Wrap(`some thing here that is more than 10 characters`))
	Z.Columns = col
}
Output:
some thing
here that
is more
than 10
characters

func Wrapf

func Wrapf(a string, f ...any) string

Wrapf calls fmt.Sprintf on the string before passing it to Wrap.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	col := Z.Columns
	Z.Columns = 3
	fmt.Println(Z.Wrapf(`some %v here`, 10))
	Z.Columns = col
}
Output:
some
10
here

Types

type Block

type Block struct {
	T int
	V []byte
}

func Blocks

func Blocks(in string) []*Block

Blocks strips preceding and trailing white space and then checks the first line for indentation (spaces or tabs) and strips that exact indentation string from every line. It then breaks up the input into blocks separated by one or more empty lines and applies basic formatting to each as follows:

If is one of the following leave alone with no wrapping:

* Bulleted List - beginning with *
* Numbered List - beginning with 1.
* Verbatim      - beginning with four spaces

Everything else is considered a "paragraph" and will be unwrapped
into a single long line (which is normally wrapped later).

For now, these blocks are added as is, but plans are to eventually add support for short and long lists much like CommonMark.

Note that because of the nature of Verbatim's block's initial (4 space) token Verbatim blocks must never be first since the entire input buffer is first dedented and the spaces would grouped with the indentation to be stripped. This is never a problem, however, because Verbatim blocks never make sense as the first block in a BonzaiMark document. This simplicity and clarity of 4-space tokens far outweighs the advantages of alternatives (such as fences).

Example (Bulleted)
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	in := `

			* some thing
			* another thing

			* another block
			* here

			*boldnotbullet*

			`

	blocks := Z.Blocks(in)
	fmt.Printf("%v %q\n", blocks[1].T, blocks[1])
	fmt.Printf("%v %q\n", blocks[2].T, blocks[2])

}
Output:
3 "* another block\n* here"
1 "*boldnotbullet*"
Example (Numbered)
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	in := `

			1. some thing
			2. another thing

			1. another block
			2. here

			`

	fmt.Printf("%q\n", Z.Blocks(in)[1])

}
Output:
"1. another block\n2. here"
Example (Paragraph)
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {

	// defer func() { scan.Trace = 0 }()
	// log.SetFlags(0)
	// scan.Trace = 1

	in := `
			Simple paragraph
			here on multiple
			lines.

			And another one here
			with just a bit more.

			`

	fmt.Printf("%q\n", Z.Blocks(in)[0])
	fmt.Printf("%q\n", Z.Blocks(in)[1])

}
Output:
"Simple paragraph here on multiple lines."
"And another one here with just a bit more."
Example (Verbatim)
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {

	// Note that the following begins consistently with three tabs so that
	// dedenting works consistently. There are four spaces before Now and
	// the verbatim block. Notice that even the blank line within the
	// verbatim block must have the exact same indentation and spaced
	// verbatim prefix. (If using Vi/m try set :list to display them.)

	in := `
			Must have another block type first.

			     Now we can start
			     a Verbatim
			     block.
			     
			     Which can have blank lines, even.

			And back to a paragraph block.

			`

	fmt.Printf("%q\n", Z.Blocks(in)[0])
	fmt.Printf("%q\n", Z.Blocks(in)[1])
	fmt.Printf("%q\n", Z.Blocks(in)[2])

}
Output:
"Must have another block type first."
"Now we can start\na Verbatim\nblock.\n\nWhich can have blank lines, even."
"And back to a paragraph block."

func (*Block) String

func (s *Block) String() string

String fulfills the fmt.Stringer interface.

type Cmd

type Cmd struct {

	// main documentation, use Get* for filled template
	Name        string    `json:"name,omitempty"`        // plain
	Aliases     []string  `json:"aliases,omitempty"`     // plain
	Summary     string    `json:"summary,omitempty"`     // template
	Usage       string    `json:"usage,omitempty"`       // template
	Version     string    `json:"version,omitempty"`     // template
	Copyright   string    `json:"copyright,omitempty"`   // template
	License     string    `json:"license,omitempty"`     // template
	Description string    `json:"description,omitempty"` // template
	Other       []Section `json:"other,omitempty"`       // template

	// run-time additions to main documentation (ex: {{ exename }})
	Dynamic template.FuncMap `json:"-"`

	// administrative URLs, "https://" assumed
	Site   string `json:"site,omitempty"`   // template, landing page
	Source string `json:"source,omitempty"` // template, usually git url
	Issues string `json:"issues,omitempty"` // template, have an issue

	// descending tree, completable
	Commands []*Cmd   `json:"commands,omitempty"`
	Params   []string `json:"params,omitempty"`
	Hidden   []string `json:"hidden,omitempty"`

	// standard or custom completer
	Completer bonzai.Completer `json:"-"`

	// where the work happens
	Caller *Cmd   `json:"-"`
	Call   Method `json:"-"`

	// faster than lots of "if" in Call
	MinArgs int  `json:"-"` // minimum number of args required (including parms)
	MinParm int  `json:"-"` // minimum number of params required
	MaxParm int  `json:"-"` // maximum number of params required
	ReqConf bool `json:"-"` // requires Z.Conf be assigned
	ReqVars bool `json:"-"` // requires Z.Var be assigned
	// 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 aliases and adds to Commands returning a reference to the new Cmd. The name must be first.

func (*Cmd) CmdNames

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

CmdNames returns the names of every Command.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

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

func (*Cmd) Fill added in v0.4.0

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

Fill fills out the passed text/template string using the Cmd instance as the data object source for the template. It is called by the Get* family of field accessors but can be called directly as well. Also see markfunc.go for list of predefined template functions.

func (*Cmd) GetAliases added in v0.3.0

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

GetAliases fulfills the bonzai.Command interface. No Fill.

func (*Cmd) GetCaller

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

GetCaller fulfills the bonzai.Command interface.

func (*Cmd) GetCommandNames added in v0.3.0

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

GetCommandNames fulfills the bonzai.Command interface. No Fill.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

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

func (*Cmd) GetCommands

func (x *Cmd) GetCommands() []bonzai.Command

GetCommands fulfills the bonzai.Command interface.

func (*Cmd) GetCompleter

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

GetCompleter fulfills the Command interface.

func (*Cmd) GetCopyright added in v0.3.0

func (x *Cmd) GetCopyright() string

GetCopyright fulfills the bonzai.Command interface. Uses Fill.

func (*Cmd) GetDescription added in v0.3.0

func (x *Cmd) GetDescription() string

GetDescription fulfills the bonzai.Command interface. Uses Fill.

func (*Cmd) GetHidden

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

GetHidden fulfills the bonzai.Command interface.

func (*Cmd) GetIssues added in v0.3.0

func (x *Cmd) GetIssues() string

GetIssues fulfills the bonzai.Command interface. Uses Fill.

func (*Cmd) GetLegal added in v0.6.0

func (x *Cmd) GetLegal() string

GetLegal returns a single line with the combined values of the Name, Version, Copyright, and License. If Version is empty or nil an empty string is returned instead. GetLegal() is used by the version builtin command to aggregate all the version information into a single output.

func (*Cmd) GetLicense added in v0.3.0

func (x *Cmd) GetLicense() string

GetLicense fulfills the bonzai.Command interface. Uses Fill.

func (*Cmd) GetMaxParm added in v0.3.0

func (x *Cmd) GetMaxParm() int

GetMaxParm fulfills the bonzai.Command interface. No Fill.

func (*Cmd) GetMinArgs added in v0.3.0

func (x *Cmd) GetMinArgs() int

GetMinArgs fulfills the bonzai.Command interface. No Fill.

func (*Cmd) GetMinParm added in v0.3.0

func (x *Cmd) GetMinParm() int

GetMinParm fulfills the bonzai.Command interface. No Fill.

func (*Cmd) GetName

func (x *Cmd) GetName() string

GetName fulfills the bonzai.Command interface. No Fill.

func (*Cmd) GetOther

func (x *Cmd) GetOther() []bonzai.Section

GetOther fulfills the bonzai.Command interface. Uses Fill.

func (*Cmd) GetOtherTitles added in v0.3.0

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

GetOtherTitles fulfills the bonzai.Command interface. No Fill.

func (*Cmd) GetParams

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

GetParams fulfills the bonzai.Command interface.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

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

func (*Cmd) GetReqConf added in v0.3.0

func (x *Cmd) GetReqConf() bool

GetReqConf fulfills the bonzai.Command interface. No Fill.

func (*Cmd) GetReqVars added in v0.6.0

func (x *Cmd) GetReqVars() bool

GetReqVars fulfills the bonzai.Command interface. No Fill.

func (*Cmd) GetSite added in v0.3.0

func (x *Cmd) GetSite() string

GetSite fulfills the bonzai.Command interface. Uses Fill.

func (*Cmd) GetSource added in v0.3.0

func (x *Cmd) GetSource() string

GetSource fulfills the bonzai.Command interface. Uses Fill.

func (*Cmd) GetSummary added in v0.3.0

func (x *Cmd) GetSummary() string

GetSummary fulfills the bonzai.Command interface. Uses Fill.

func (*Cmd) GetTitle added in v0.3.0

func (x *Cmd) GetTitle() string

GetTitle fulfills the bonzai.Command interface. No Fill.

func (*Cmd) GetUsage added in v0.3.0

func (x *Cmd) GetUsage() string

GetUsage fulfills the bonzai.Command interface. Uses Fill.

func (*Cmd) GetUsageFunc added in v0.3.0

func (x *Cmd) GetUsageFunc() bonzai.UsageFunc

GetUsageFunc fulfills the bonzai.Command interface. No Fill.

func (*Cmd) GetVersion added in v0.3.0

func (x *Cmd) GetVersion() string

GetVersion fulfills the bonzai.Command interface. Uses Fill.

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) Log

func (x *Cmd) Log(format string, a ...any)

Log is currently short for log.Printf() but may be supplemented in the future to have more fine-grained control of logging.

func (*Cmd) MissingConfig

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) Names

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

Names returns the Name and any Aliases grouped such that the Name is always last.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {

	x := &Z.Cmd{
		Name:    `foo`,
		Aliases: []string{"f", "FOO"},
	}
	fmt.Println(x.Names())

}
Output:
[f FOO foo]

func (*Cmd) OtherTitles

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

OtherTitles returns just the ordered titles from Other.

func (*Cmd) Param

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

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

func (*Cmd) Path added in v0.3.0

func (x *Cmd) Path() string

Path returns a dotted notation of the PathNames including an initial dot (for root). This is compatible yq query expressions and useful for associating configuration and other data specifically with this command.

Example
package main

import (
	"fmt"
	"os"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	Z.ExitOff()

	z := new(Z.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 *Z.Cmd, _ ...string) error {
		fmt.Println(x.Path())
		return nil
	}

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

	z.Run()

}
Output:
.some.thing.deep

func (*Cmd) PathNames added in v0.6.0

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) Q

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

Q is a shorter version of Z.Conf.Query(x.Path()+"."+q) for convenience. Logs the error and returns a blank string if Z.Conf is not defined (see ReqConf).

func (*Cmd) ReqConfError added in v0.3.0

func (x *Cmd) ReqConfError() error

ReqConfError returns stating that the given command requires that Z.Conf be set to something besides null.

func (*Cmd) ReqVarsError added in v0.6.0

func (x *Cmd) ReqVarsError() error

ReqVarsError returns stating that the given command requires that Z.Vars be set to something besides null.

func (*Cmd) Resolve

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

Resolve looks up a given Command by name or alias from Aliases (caching a lookup map of aliases in the process).

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)

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.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {

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

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

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

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

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

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

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

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

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

}
Output:
hi ["there"]
salut []
salut [""]

func (*Cmd) Title

func (x *Cmd) Title() string

Title returns a dynamic field of Name and Summary combined (if exists). If the Name field of the commands is not defined will return a "{ERROR}". Fills template for Summary.

func (*Cmd) Unimplemented

func (x *Cmd) Unimplemented() error

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

func (*Cmd) UsageCmdNames

func (x *Cmd) UsageCmdNames() string

UsageCmdNames returns the Names for each of its Commands joined, if more than one, with usage regex notation.

func (*Cmd) UsageCmdTitles

func (x *Cmd) UsageCmdTitles() string

UsageCmdTitles returns a single string with the titles of each subcommand indented and with a maximum title signature length for justification. Hidden commands are not included. Note that the order of the Commands is preserved (not necessarily alphabetic).

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	x := &Z.Cmd{
		Name:   `cmd`,
		Params: []string{"p1", "p2"},
		Commands: []*Z.Cmd{
			&Z.Cmd{
				Name:    "foo",
				Aliases: []string{"f"},
				Summary: "foo the things",
			},
			&Z.Cmd{
				Name:    "bar",
				Summary: "bar the things",
			},
			&Z.Cmd{
				Name: "nosum",
			},
		},
	}
	fmt.Println(x.UsageCmdTitles())
}
Output:
f|foo - foo the things
bar   - bar the things
nosum

func (*Cmd) UsageError

func (x *Cmd) UsageError() error

UsageError returns an error with a single-line usage string. The word "usage" can be changed by assigning Z.UsageText to something else. The commands own UsageFunc will be used if defined. If undefined, the Z.UsageFunc will be used instead (which can also be assigned to something else if needed).

Example (Commands_with_Aliases)
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	x := &Z.Cmd{
		Name: `cmd`,
		Commands: []*Z.Cmd{
			&Z.Cmd{Name: "foo", Aliases: []string{"f"}},
			&Z.Cmd{Name: "bar"},
		},
	}
	fmt.Println(x.UsageError())
}
Output:
usage: cmd ((f|foo)|bar)
Example (No_Call_nor_Commands)
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	x := &Z.Cmd{
		Name: `cmd`,
	}
	fmt.Println(x.UsageError())
}
Output:
usage: cmd {ERROR: neither Call nor Commands defined}
Example (Params_but_No_Call)
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {
	x := &Z.Cmd{
		Name:   `cmd`,
		Params: []string{"p1", "p2"},
		Commands: []*Z.Cmd{
			&Z.Cmd{Name: "foo", Aliases: []string{"f"}},
			&Z.Cmd{Name: "bar"},
		},
	}
	fmt.Println(x.UsageError())
}
Output:
usage: cmd {ERROR: Params without Call: p1, p2}

func (*Cmd) UsageNames

func (x *Cmd) UsageNames() string

UsageNames returns single name, joined Names with bar (|) and wrapped in parentheses, or empty string if no names.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {

	x := &Z.Cmd{
		Name:    `foo`,
		Aliases: []string{"f", "FOO"},
	}
	fmt.Println(x.UsageNames())

}
Output:
(f|FOO|foo)

func (*Cmd) UsageParams

func (x *Cmd) UsageParams() string

UsageParams returns the Params in UsageGroup notation.

Example
package main

import (
	"fmt"

	Z "github.com/rwxrob/bonzai/z"
)

func main() {

	x := &Z.Cmd{
		Name:   `foo`,
		Params: []string{"p1", "p2"},
	}
	fmt.Println(x.UsageParams())

	x.MinParm = 1
	fmt.Println(x.UsageParams())

	x.MaxParm = 1
	fmt.Println(x.UsageParams())

}
Output:
(p1|p2)?
(p1|p2)+
(p1|p2)

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.

type Section

type Section struct {
	Title string
	Body  string
}

Section contains the Other sections of a command. Composition notation (without Title and Body) is not only supported but encouraged for clarity when reading the source for documentation.

func (Section) GetBody added in v0.3.0

func (s Section) GetBody() string

func (Section) GetTitle added in v0.3.0

func (s Section) GetTitle() string

Jump to

Keyboard shortcuts

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