Z

package
v0.20.10 Latest Latest
Warning

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

Go to latest
Published: Dec 16, 2022 License: Apache-2.0 Imports: 24 Imported by: 176

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

AllowPanic disables TrapPanic stopping it from cleaning panic errors.

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

CallDummy is useful for testing when non-nil Call function needed.

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. 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 Cmds to lookup when executing with Z.Run in "multicall" mode. Each value of the slice keyed to the name must begin with a *Cmd and the rest will be assumed to be string arguments to prepend. This allows the table of Commands to not only associate with a specific Cmd, but to also provide different arguments to that command. The name Commands is similar to Cmd.Commands. See Run.

View Source
var Comp = compcmd.New()

Comp may be optionally assigned any implementation of bonzai.Completer and will be used as the default if a Command does not provide its own. Comp is assigned compcmd.Completer by default. This can be overriden by Bonzai tree developers through simple assignment to their own preference. However, for consistency, this default is strongly recommended, at least for all branch commands (as opposed to leafs). See z.Cmd.Run for documentation on completion mode.

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 Dynamic = 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":     exepath,
	"exename":     exename,
	"execachedir": execachedir,
	"execonfdir":  execonfdir,
	"cachedir":    cachedir,
	"confdir":     confdir,
	"homedir":     homedir,
	"pathsep":     func() string { return string(os.PathSeparator) },
	"pathjoin":    filepath.Join,
}

Dynamic contains the package global default template language 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.Dynamic (which takes higher priority). Use Z.Dynamic when a shared template language is to be used across all Cmds within a single Bonzai tree or branch. This allows powerful, template-driven applications to work well with one another.

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 NoPager bool

NoPager disables all paging.

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.

Vars may be optionally assigned any implementation of a bonzai.Vars but this is normally assigned at init() time by a bonzai.Vars driver module. 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.Vars (and usually assign themselves to Z.Vars at init() time), commands must never require a specific implementation of bonzai.Vars. 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.Vars 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). Returns empty string if error.

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
input

with blank line
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
input

with blank line

func Emph

func Emph[T string | []byte | []rune](buf T) string

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

*Italic*
**Bold**
***BoldItalic***
<Under> (keeping brackets)
`Code`

See Mark for block formatting and 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 term escapes
	// (see package documentation for more)

	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>>
Example (Bold)
package main

import (
	"fmt"

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

func main() {
	term.Bold = `<bold>`
	term.Reset = `<reset>`
	fmt.Println(Z.Emph("**Bold**"))
	fmt.Println(Z.Emph("** Bold **"))
}
Output:

<bold>Bold<reset>
** Bold **
Example (BoldItalic)
package main

import (
	"fmt"

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

func main() {
	term.BoldItalic = `<bolditalic>`
	term.Reset = `<reset>`
	fmt.Println(Z.Emph("***BoldItalic***"))
}
Output:

<bolditalic>BoldItalic<reset>
Example (Code)
package main

import (
	"fmt"

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

func main() {
	term.Under = `<code>`
	term.Reset = `<reset>`
	fmt.Println(Z.Emph("`Code`"))
	fmt.Println(Z.Emph("` Code `"))
	fmt.Println(Z.Emph("`.git`"))
}
Output:

<code>Code<reset>
` Code `
<code>.git<reset>
Example (Italic)
package main

import (
	"fmt"

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

func main() {
	term.Italic = `<italic>`
	term.Reset = `<reset>`
	fmt.Println(Z.Emph("*Italic*"))
	fmt.Println(Z.Emph("* Italic *"))
}
Output:

<italic>Italic<reset>
* Italic *
Example (Under)
package main

import (
	"fmt"

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

func main() {
	term.Under = `<under>`
	term.Reset = `<reset>`
	fmt.Println(Z.Emph("<UNDER>"))
	fmt.Println(Z.Emph("< UNDER >"))
}
Output:

<<under>UNDER<reset>>
< UNDER >

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 with exec.Command.Run 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. See exec.Command.Run for more about distinguishing ExitErrors from others.

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 FindPager added in v0.20.0

func FindPager() string

FindPager returns a full path to a pager binary if it can find one on the system:

  • $PAGER
  • pager (in PATH)

If neither is found returns an empty string.

func FixPagerEnv added in v0.20.2

func FixPagerEnv()

FixPagerEnv sets environment variables for different pagers to get them to support color ANSI escapes. FRX is added to LESS and LV is set to -c. (These are the same fixes used by the git diff command.)

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
}
Output:

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 Page added in v0.20.0

func Page[T any](buf T) error

Page writes the buf to a temporary file and passes it as first argument to the detected system pager. Anything that to.String accepts can be passed. Just prints output without paging if a pager cannot be found.

func PageFile added in v0.20.0

func PageFile(path string) error

PageFile looks up the system pager and passes the first argument as a file argument to it. Just prints file if no pager can be found.

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 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
}
Output:

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 and delegates accordingly, prepending any arguments provided. This allows for BusyBox-like (https://www.busybox.net) multicall binaries to be used for such things as very light-weight Linux distributions when used "FROM SCRATCH" in containers. Although it shares the same name Z.Run should not confused with Cmd.Run. In general, Z.Run is for "multicall" and Cmd.Run is for "monoliths". Run may exit with the following errors:

* MultiCallCmdNotFound * MultiCallCmdNotCmd * MultiCallCmdNotCmd * MultiCallCmdArgNotString

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 ArgMap added in v0.12.0

type ArgMap map[string][]string

ArgMap is a map keyed to individual arguments that should be expanded by being replaced with the slice of strings. See Cmd.Shortcuts.

func (ArgMap) Keys added in v0.12.0

func (m ArgMap) Keys() []string

Keys returns only the key names.

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.

If no blocks are parsed returns an empty slice of Block pointers ([] *Block).

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

PEGN Specification

      Grammar     <-- Block*
      Block       <-- Bulleted / Numbered / Verbatim / Paragraph
      Bulleted    <-- '* ' (!EOB unipoint)* EOB
      Numbered    <-- '1. ' (!EOB unipoint)* EOB
      Verbatim    <-- '    ' (!EOB unipoint)* EOB
      Paragraph   <-- (!EOB unipoint)* EOB
      EOB          <- LF{2} / EOD
      EOD          <- # end of data stream
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.Println(blocks)
	fmt.Printf("%q\n", blocks[0])
	fmt.Printf("%q\n", blocks[1])

}
Output:

"* some thing\n* another thing"
"* another block\n* here"
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

			`

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

}
Output:

"1. some thing\n2. another thing"
"1. another block\n2. here"
Example (Paragraph)
package main

import (
	"fmt"

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

func main() {

	in := `
			Simple paragraph
			here on multiple
			lines.

			And another   one here
			with just a bit more.

			`

	blocks := Z.Blocks(in)
	//fmt.Println(len(blocks))
	//fmt.Printf("%v", blocks)
	fmt.Printf("%q\n", blocks[0])
	fmt.Printf("%q\n", blocks[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 another bulleted list.

			`

	blocks := Z.Blocks(in)
	//fmt.Println(len(blocks))
	//fmt.Println(blocks)
	fmt.Printf("%q\n", blocks[0])
	fmt.Printf("%q\n", blocks[1])
	fmt.Printf("%q\n", blocks[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 another bulleted list."

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
	Shortcuts   ArgMap    `json:"shortcuts,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

	// for use with text/template for run-time additions to main
	// documentation and embedded file output (ex: {{ exename }})
	Dynamic template.FuncMap `json:"-"`

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

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

	// default values for Var variables, also for initialization
	VarDefs VarVals `json:"vardefs,omitempty"`

	// standard or custom completer, usually of form compfoo.New()
	Comp bonzai.Completer `json:"-"`

	// where the work happens
	Caller *Cmd   `json:"-"`
	Call   Method `json:"-"`
	Init   Method `json:"-"` // before Commands/Call, good for validation

	// pass bulk input efficiently (when args won't do)
	Input io.Reader

	// faster than lots of "if" in Call
	MinArgs  int  `json:"-"` // minimum number of args required (including parms)
	MaxArgs  int  `json:"-"` // maximum number of args required (including parms)
	NumArgs  int  `json:"-"` // exact number of args required (including parms)
	NoArgs   bool `json:"-"` // must not have any args
	MinParm  int  `json:"-"` // minimum number of params required
	MaxParm  int  `json:"-"` // maximum number of params required
	UseConf  bool `json:"-"` // requires Z.Conf be assigned
	UseVars  bool `json:"-"` // requires Z.Var be assigned
	ConfVars bool `json:"-"` // vars default to conf values before VarDefs
	// 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) C added in v0.7.0

func (x *Cmd) C(q string) (string, error)

C is a shorter version of strings.TrimSpace(Z.Conf.Query(x.Path()+"."+q)) for convenience. The yq YAML/JSON queries unreliably add line returns sometimes and other times not. If a line return is wanted, the caller will need to use fmt.Println. Also see UseConf.

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) Del added in v0.13.0

func (x *Cmd) Del(key string) error

Del is a shorter version of Z.Vars.Del(x.Path()+"."+key.val) for convenience. Logs the error Z.Vars is not defined (see UseVars).

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) Get added in v0.7.0

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

Get is a shorter version of Z.Vars.Get(x.Path()+"."+key) for convenience but also observes VarDefsFromConf if set. Also see UseVars.

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) GetComp added in v0.8.0

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

GetComp 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) GetShortcuts added in v0.12.0

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

GetShortcuts fulfills the bonzai.Command interface. No Fill.

func (*Cmd) GetShortcutsMap added in v0.12.0

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

GetShortcutsMap 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 x.Usage if not empty. Otherwise, calls UsageFunc to get usage, then uses Fill.

func (*Cmd) GetUseConf added in v0.10.0

func (x *Cmd) GetUseConf() bool

GetUseConf fulfills the bonzai.Command interface. No Fill.

func (*Cmd) GetUseVars added in v0.10.0

func (x *Cmd) GetUseVars() bool

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

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

Names returns the Name and any Aliases grouped such that the Name is always last. Any alias beginning with anything but a letter (L) is omitted.

Example
package main

import (
	"fmt"

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

func main() {

	x := &Z.Cmd{
		Name:    `foo`,
		Aliases: []string{"-f", "@f", "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(more ...string) 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. If any arguments are passed then will be added with dots between them.

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())
		fmt.Println(x.Path(`and`, `some`, `more`))
		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
.some.thing.deep.and.some.more

func (*Cmd) PathCmds added in v0.8.0

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

Run method resolves Shortcuts and Aliases and seeks the leaf Cmd. It then calls the leaf's first-class 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. Exiting can be controlled, however, by calling ExitOn or ExitOff (primarily for testing). 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 MinArgs, MaxArgs, NumArgs, etc.) which is normally done by Run. Use a high-level branch pkg instead (which is idiomatic for good Bonzai branch development).

Handling Completion

Since Run is the main execution entry point for all Bonzai command trees it is also responsible for handling completion (tab or otherwise). Therefore, all Run methods have two modes: delegation and completion (both are executions of the Bonzai binary command tree). Delegation is the default mode. Completion mode is triggered by the detection of the COMP_LINE environment variable.

COMP_LINE

When COMP_LINE is set, Run prints a list of possible completions to standard output by calling its Comp.Complete function (default Z.Comp). Each Cmd therefore manages its own completion and can draw from a rich ecosystem of Completers or assign its own custom one. This enables very powerful completions including dynamic completions that query the network or the local execution environment. Since Go can run on pretty much every device architecture right now, that's a lot of possibilities. Even a line-based calculator can be implemented as a Completer. AI completers are also fully supported by this approach. Intelligent completion eliminates the need for overly complex and error-prone (getopt) argument signatures for all Bonzai commands.

Why COMP_LINE?

Setting COMP_LINE has been a bash shell standard for more than a few decades. (Unfortunately, zsh dubiously chose to not support it for no good reason.) COMP_LINE completion, therefore, is the only planned method of detecting completion context. Enabling it in bash for any command becomes a simple matter of "complete -C foo foo" (rather than forcing users to evaluate thousands of lines of shell code to enable completion for even minimally complex command trees as other "commanders" require). Any code will work that sets COMP_LINE before calling Cmd.Run and receives a list of lines to standard output with completion candidates.

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. Cmd.Shortcuts are expanded.

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) Set added in v0.7.0

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

Set is a shorter version of Z.Vars.Set(x.Path()+"."+key.val) for convenience. Logs the error Z.Vars is not defined (see UseVars).

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) 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) UsageCmdShortcuts added in v0.12.1

func (x *Cmd) UsageCmdShortcuts() string

UsageCmdShortcuts returns a single string with the Shortcuts indented and with a maximum title signature length for justification.

Example
package main

import (
	"fmt"

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

func main() {
	x := &Z.Cmd{
		Name: `cmd`,
		Shortcuts: Z.ArgMap{
			"foo": {"a", "long", "way", "to", "foo"},
			"bar": {"a", "long", "long", "way", "to", "bar"},
		},
	}
	fmt.Println(x.UsageCmdShortcuts())
}
Output:

foo - a long way to foo
bar - a long long way to bar

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. Aliases that begin with anything but a letter (L) 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"},
		Hidden: []string{"hidden"},
		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",
			},
			&Z.Cmd{
				Name:    "hidden",
				Summary: "not listed, but works",
			},
		},
	}
	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 IncorrectUsage for self.

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(Z.IncorrectUsage{x})
}
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(Z.IncorrectUsage{x})
}
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(Z.IncorrectUsage{x})
}
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 DefCmdReqCall added in v0.10.0

type DefCmdReqCall struct {
	Cmd *Cmd
}

func (DefCmdReqCall) Error added in v0.10.0

func (e DefCmdReqCall) Error() string

type IncorrectUsage added in v0.10.0

type IncorrectUsage struct {
	Cmd *Cmd
}

func (IncorrectUsage) Error added in v0.10.0

func (e IncorrectUsage) Error() 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.

type MissingConf added in v0.10.0

type MissingConf struct {
	Path string
}

func (MissingConf) Error added in v0.10.0

func (e MissingConf) Error() string

type MissingVar added in v0.15.1

type MissingVar struct {
	Path string
}

func (MissingVar) Error added in v0.15.1

func (e MissingVar) Error() string

type MultiCallCmdArgNotString added in v0.10.0

type MultiCallCmdArgNotString struct {
	CmdName string
	It      any
}

func (MultiCallCmdArgNotString) Error added in v0.10.0

func (e MultiCallCmdArgNotString) Error() string

type MultiCallCmdNotCmd added in v0.10.0

type MultiCallCmdNotCmd struct {
	CmdName string
	It      any
}

func (MultiCallCmdNotCmd) Error added in v0.10.0

func (e MultiCallCmdNotCmd) Error() string

type MultiCallCmdNotFound added in v0.10.0

type MultiCallCmdNotFound struct {
	CmdName string
}

func (MultiCallCmdNotFound) Error added in v0.10.0

func (e MultiCallCmdNotFound) Error() string

type NoCallNoCommands added in v0.10.0

type NoCallNoCommands struct {
	Cmd *Cmd
}

func (NoCallNoCommands) Error added in v0.10.0

func (e NoCallNoCommands) Error() string

type NotEnoughArgs added in v0.10.0

type NotEnoughArgs struct {
	Count int
	Min   int
}

func (NotEnoughArgs) Error added in v0.10.0

func (e NotEnoughArgs) Error() string

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

type TooManyArgs added in v0.10.0

type TooManyArgs struct {
	Count int
	Max   int
}

func (TooManyArgs) Error added in v0.10.0

func (e TooManyArgs) Error() string

type UsesConf added in v0.10.0

type UsesConf struct {
	Cmd *Cmd
}

func (UsesConf) Error added in v0.10.0

func (e UsesConf) Error() string

type UsesVars added in v0.10.0

type UsesVars struct {
	Cmd *Cmd
}

func (UsesVars) Error added in v0.10.0

func (e UsesVars) Error() string

type VarVals added in v0.15.0

type VarVals map[string]string

VarVals is a map keyed to individual variable keys from Vars. See Cmd.ConfVars and Cmd.Get.

type WrongNumArgs added in v0.10.0

type WrongNumArgs struct {
	Count int
	Num   int
}

func (WrongNumArgs) Error added in v0.10.0

func (e WrongNumArgs) Error() string

Jump to

Keyboard shortcuts

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