clif

package module
v0.0.0-...-df36acc Latest Latest
Warning

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

Go to latest
Published: Feb 18, 2019 License: MIT Imports: 21 Imported by: 0

README

Build Status GoDoc

logo-large

Command line interface framework

Go framework for rapid command line application development.

Example

demo

package main

import "gopkg.in/ukautz/clif.v1"

func main() {
	clif.New("My App", "1.0.0", "An example application").
		New("hello", "The obligatory hello world", func(out clif.Output) {
			out.Printf("Hello World\n")
		}).
		Run()
}


Install

$ go get gopkg.in/ukautz/clif.v1

Getting started

On the one side, CLIF's builder-like API can be easily used for rapid development of small, single purpose tools. On the other side, CLIF is designed with complex console applications in mind.

Commands

Commands must have a unique name and can have additional arguments and options.

cmd1 := clif.NewCommand("name", "A description", callBackFunction)
cmd2 := clif.NewCommand("other", "Another description", callBackFunction2)

The name is used from the command line to call the command:

$ ./app name
$ ./app other
Callback functions

Callback functions can have arbitrary parameters. CLIF uses a small, built-in (signatur) injection container which allows you to register any kind of object (struct or interface) beforehand.

So you can register any object (interface{}, struct{} .. and anything else, see below) in your bootstrap and then "require" those instances by simply putting them in the command callback signature:

// Some type definition
type MyFoo struct {
    X int
}

func main() {
    // init cli
    cli := clif.New("My App", "1.0.0", "An example application")

    // register object instance with container
    foo := &MyFoo{X: 123}
    cli.Register(foo)

    // Create command with callback using the peviously registered instance
    cli.NewCommand("foo", "Call foo", func (foo *MyFoo) {
        // do something with foo
    })

    cli.Run()
}

Using interfaces is possible as well, but a bit less elegant:

// Some interface
type MyBar interface {
    Bar() string
}

// Some type
type MyFoo struct {
}

// implement interface
func (m *MyFoo) Bar() string {
    return "bar"
}

func main() {
    // init cli
    cli := clif.New("My App", "1.0.0", "An example application")

    // create object, which implements MyBar:
    foo := &MyFoo{}
    t := reflect.TypeOf((*MyBar)(nil)).Elem()
    cli.RegisterAs(t.String(), foo)

    // Register command with callback using the type
    cli.NewCommand("bar", "Call bar", func (bar MyBar) {
        // do something with bar
    })

    cli.Run()
}
Named

Everything works great if you only have a single instance of any object of a specific type. However, if you need more than one instance (which might often be the case for primitive types, such as int or string) you can use named registering:

// Register abitrary objects under unique name
cli.RegisterNamed("foo", new(MyFoo)).
    RegisterNamed("bar", 123).
    RegisterNamed("baz", "bla")

// Register command with callback named container
cli.NewCommand("bar", "Call bar", func (named clif.NamedParameters) {
    asMap := map[string]interface{}(named)
    fmt.Println(asMap["baz"].(string))
})

Note: If you want to use the named feature, you cannot Register() any NamedParameters instance yourself, since "normally" registered objects are evaluated before named.

Default objects

CLIF pre-populates the dependency container with a couple of built-in objects:

  • The Output (formatted output helper, see below), eg func (out clif.Output) { .. }
  • The Input (input helper, see below), eg func (in clif.Input) { .. }
  • The *Cli instance itself, eg func (c *clif.Cli) { .. }
  • The current *Command instance, eg func (o *clif.Command) { .. }
Arguments and Options

CLIF can deal with arguments and options. The difference being:

  • Arguments come after the command name. They are identified by their position.
  • Options have no fixed position. They are identified by their --opt-name (or alias, eg -O)

Of course you can use arguments and options at the same time..

Arguments

Arguments are additional command line parameters which come after the command name itself.

cmd := clif.NewCommand("hello", "A description", callBackFunction)
	.NewArgument("name", "Name for greeting", "", true, false)

arg := cmd.NewAgument("other", "Something ..", "default", false, true)
cmd.AddArgument(arg)

Arguments consist of a name, a description, an optional default value a required flag and a multiple flag.

$ ./my-app hello the-name other1 other2 other3
#            ^      ^       ^       ^     ^
#            |      |       |       |     |
#            |      |       |       | third "other" arg
#            |      |       |  second "other" arg
#            |      |  first "other" arg
#            |  the "name" arg
#        command name

Position of arguments matters. Make sure you add them in the right order. And: required arguments must come before optional arguments (makes sense, right?). There can be only one multiple argument at all and, of course, it must be the last (think: variadic).

You can access the arguments by injecting the command instance *clif.Command into the callback and calling the Argument() method. You can choose to interpret the argument as String(), Int(), Float(), Bool(), Time() or Json(). Multiple arguments can be accessed with Strings(), Ints() .. and so on. Count() gives the amount of (provided) multiple arguments and Provided() returns bool for optional arguments. Please see parameter.go for more.

func callbackFunctionI(c *clif.Command) {
	// a single
	name := c.Argument("name").String()

	// a multiple
	others := c.Argument("other").Strings()

	// .. do something ..
}
Options

Options have no fixed position, meaning ./app --foo --bar and ./app --bar --foo are equivalent. Options are referenced by their name (eg --name) or alias (eg -n). Unless the option is a flag (see below) it must have a value. The value must immediately follow the option. Valid forms are: --name value, --name=value, -n value and -n=value.

Options must come before the command, unless they use the = separator. For example: ./app command --opt value is valid, ./app --opt=value command is valid but ./app --opt value command is not valid (since it becomes impossible to distinguish between command and value).

cmd := clif.NewCommand("hello", "A description", callBackFunction)
	.NewOption("name", "n", "Name for greeting", "", true, false)

arg := cmd.NewOption("other", "O", "Something ..", "default", false, true)
cmd.AddOption(arg)

Now:

$ ./my-app hello --other bar -n Me -O foo
#                       ^       ^    ^
#                       |       |    |
#                       |       |  second other opt with value
#                       |   name opt with value
#                  first other opt with value

You can access options the same way as arguments, just use Option() instead.

func callbackFunctionI(c *clif.Command) {
	name := c.Option("name").String()
	others := c.Option("other").Strings()
	// .. do something ..
}
Flags

There is a special kind of option, which does not expect a parameter: the flag. As options, their position is arbitrary.

// shorthand
flag := clif.NewFlag("my-flag", "f", "Something ..", false)
// which would just do:
flag = clif.NewOption("my-flag", "f", "Something ..", "", false, false).IsFlag()
cmd := clif.NewCommand("hello", "A description", callBackFunction).AddOption(flag)

When using the option, you dont need to (nor can you) provide an argument:

$ ./my-app hello --my-flag

You want to use Bool() to check if a flag is provided:

func callbackFunctionI(c *clif.Command) {
	if c.Option("my-flag").Bool() {
		// ..
	}
}
Validation & (Parsing | Transformation)

You can validate/parse/transform the input using the Parse attribute of options or arguments. It can be (later on) set using the SetParse() method:

// Validation example
arg := clif.NewArgument("my-int", "An integer", "", true, false).
    SetParse(func(name, value string) (string, error) {
        if _, err := strconv.Atoi(value); err != nil {
            return "", fmt.Errorf("Oops: %s is not an integer: %s", name, err)
        } else {
            return value, nil
        }
    })

// Transformation example
opt := clif.NewOption("client-id", "c", "The client ID", "", true, false).
    SetParse(func(name, value string) (string, error) {
        if strings.Index(value, "#") != 0 {
            return fmt.Sprintf("#%s", value), nil
        } else {
            return value, nil
        }
    })

There are a few built-in validators you can use out of the box:

  • clif.IsInt - Checks for integer, eg clif.NewOption(..).SetParse(clif.IsInt)
  • clif.IsFloat - Checks for float, eg clif.NewOption(..).SetParse(clif.IsFloat)

See validators.go.

Environment variables & default

The argument and option constructors (NewArgument, NewOption) already allow you to set a default. In addition you can set the name of an environment variable, which will be used, if the parameter is not provided.

opt := clif.NewOption("client-id", "c", "The client ID", "", true, false).SetEnv("CLIENT_ID")

The order is:

  1. Provided, eg --config /path/to/config
  2. Environment variable, eg CONFIG_FILE
  3. Default value, as provided in constructor or set via SetDefault()

Note: A required parameter must have a value, but it does not care whether it came from input, via environment variable or as a default value.

Default options

Often you need one or multiple options on every or most commands. The usual --verbose or --config /path.. are common examples. CLIF provides two ways to deal with those.

  1. Modifying/extending clif.DefaultOptions (it's pre-filled with the --help option, which is clif.DefaultHelpOption)
  2. Calling AddDefaultOptions() or NewDefaultOption() on an instance of clif.Cli

The former is global (for any instance of clif.Cli) and assigned to any new command (created by the NewCommand constructor). The latter is applied when Run() is called and is in the scope of a single clif.Cli instance.

Note: A helpful patterns is combining default options and the injection container/registry. Following an example parsing a config file, which can be set on any command with --config /path.. or as an environment variable and has a default path.


type Conf struct {
    Foo string
    Bar string
}

func() main {

    // init new cli app
    cli := clif.New("my-app", "1.2.3", "My app that does something")

    // register default option, which fills injection container with config instance
    configOpt := clif.NewOption("config", "c", "Path to config file", "/default/config/path.json", true, false).
        SetEnv("MY_APP_CONFIG").
        SetParse(function(name, value string) (string, error) {
            conf := new(Conf)
            if raw, err := ioutil.ReadFile(value); err != nil {
                return "", fmt.Errorf("Could not read config file %s: %s", value, err)
            } else if err = json.Unmarshal(raw, conf); err != nil {
                return "", fmt.Errorf("Could not unmarshal config file %s: %s", value, err)
            } else if conf.Foo == "" {
                return "", fmt.Errorf("Config %s is missing \"foo\"", value)
            } else {
                // register *Conf
                cli.Register(conf)
                return value, nil
            }
        })
    cli.AddDefaultOptions(configOpt)

    // Since *Conf was registered it can be used in any callback
    cli.New("anything", "Does anything", func(conf *Conf) {
        // do something with conf
    })

    cli.Run()
}

Input & Output

Of course, you can just use fmt and os.Stdin, but for convenience (and fancy output) there are clif.Output and clif.Input.

Input

You can inject an instance of the clif.Input interface into your command callback. It provides small set of often used tools.

input

Ask & AskRegex

Just ask the user a question then read & check the input. The question will be asked until the check/requirement is satisfied (or the user exits out with ctrl+c):

func callbackFunctionI(in clif.Input) {
	// Any input is OK
	foo := in.Ask("What is a foo", nil)

	// Validate input
	name := in.Ask("Who are you? ", func(v string) error {
		if len(v) > 0 {
			return nil
		} else {
			return fmt.Errorf("Didn't catch that")
		}
	})

	// Shorthand for regex validation
	count := in.AskRegex("How many? ", regexp.MustCompile(`^[0-9]+$`))

	// ..
}

See clif.RenderAskQuestion for customization.

Confirm

Confirm() ask the user a question until it is answered with yes (or y) or no (or n) and returns the response as bool.

func callbackFunctionI(in clif.Input) {
	if in.Confirm("Let's do it?") {
		// ..
	}
}

See clif.ConfirmRejection, clif.ConfirmYesRegex and clif.ConfirmNoRegex for customization.

Choose

Choose() is like a select in HTML and provides a list of options with descriptions to the user. The user then must choose (type in) one of the options. The choices will be presented to the user until a valid choice (one of the options) is provided.

func callbackFunctionI(in clif.Input) {
	father := in.Choose("Who is your father?", map[string]string{
		"yoda":  "The small, green guy",
		"darth": "The one with the smoker voice and the dark cape!",
		"obi":   "The old man with the light thingy",
	})

	if father == "darth" {
		// ..
	}
}

See clif.RenderChooseQuestion, clif.RenderChooseOption and clif.RenderChooseQuery for customization.

Output & formatting

The clif.Output interface can be injected into any callback. It relies on a clif.Formatter, which does the actual formatting (eg colorizing) of the text.

Output themes

Per default, the clif.DefaultInput via clif.NewColorOutput() is used. It uses clif.DefaultStyles, which look like the screenshots you are seeing in this readme.

You can change the output like so:

cli := clif.New(..)
cli.SetOutput(clif.NewColorOutput().
    SetFormatter(clif.NewDefaultFormatter(clif.SunburnStyles))
Styles

Styles are applied by parsing (replacing) tokens like <error>, which would be substitude with \033[31;1m (using the default styles) resulting in a red coloring. Another example is <reset>, which is replaced with \033[0m leading to reset all colorings & styles.

There three built-in color styles (of course, you can extend them or add your own):

  1. DefaultStyles - as you can see on this page
  2. SunburnStyles - more yellow'ish
  3. WinterStyles - more blue'ish
Table

Table rendering is a neat tool for CLIs. CLIF supports tables out of the box using the Output interface.

Features:

  • Multi-line columns
  • Column auto fit
  • Formatting (color) within columns
  • Automatic stretch to max size (unless specifcied otherwise)

Example:

var (
    headers := []string{"Name", "Age", "Force"}
    rows = [][]string{
        {
            "<important>Yoda<reset>",
            "Very, very old",
            "Like the uber guy",
        },
        {
            "<important>Luke Skywalker<reset>",
            "Not that old",
            "A bit, but not that much",
        },
        {
            "<important>Anakin Skywalker<reset>",
            "Old dude",
            "He is Lukes father! Was kind of stronger in 1-3, but still failed to" +
                " kill Jar Jar Binks. Not even tried, though. What's with that?",
        },
	}
)

func callbackFunction(out clif.Output) {
	table := out.Table(headers)
	table.AddRows(rows)
	fmt.Println(table.Render())
}

Would print the following:

table-1

There are currently to styles available: ClosedTableStyle (above), ClosedTableStyleLight, OpenTableStyle (below) and OpenTableStyleLight:

func callbackFunction(out clif.Output) {
	table := out.Table(headers, clif.OpenTableStyle)
	table.AddRows(rows)
	fmt.Println(table.Render())
}

Would print the following:

table-2

Progress bar

Another often required tool is the progress bar. Hence CLIF provides one out of the box:

func cmdProgress(out clif.Output) error {
	pbs := out.ProgressBars()
	pb, _ := pbs.Init("default", 200)
	pbs.Start()
	var wg sync.WaitGroup
	wg.Add(1)
	go func(b clif.ProgressBar) {
		defer wg.Done()
		for i := 0; i < 200; i++ {
			b.Increment()
			<-time.After(time.Millisecond * 100)
		}
	}(pb)
	wg.Wait()
	<-pbs.Finish()
}

Would output this:

progress-1

Multiple bars are also possible, thanks to Greg Osuri's library:

func cmdProgress(out clif.Output) error {
	pbs := out.ProgressBars()
	pbs.Start()
	var wg sync.WaitGroup
	for i := 0; i < 3; i++ {
		wg.Add(1)
		pb, _ := pbs.Init(fmt.Sprintf("bar-%d", i+1), 200)
		go func(b clif.ProgressBar, ii int) {
			defer wg.Done()
			for i := 0; i < 200; i++ {
				b.Increment()
				<-time.After(time.Millisecond * time.Duration(100 * ii))
			}
		}(pb, i)
	}
	wg.Wait()
	<-pbs.Finish()
}

Would output this:

progress-2

You prefer those ASCII arrows? Just set pbs.SetStyle(clif.ProgressBarStyleAscii) and:

progress-3

Real-life example

To provide you a usful'ish example, I've written a small CLI application called repos.

See also

There are a lot of other approaches you should have a look at.

Documentation

Overview

Console Line Input Framework for rapid development of small or large scale CLI applications.

See the Github page for a full documentation with examples and patterns (https://github.com/ukautz/clif).

clif's design was influenced by Symfony Console (http://symfony.com/doc/current/components/console/introduction.html).

Determine terminal width which can come in handy for rendering tables and somesuch.

DISCLAIMER: The code contents of all term*.go files is PROUDLY STOLEN FROM https://github.com/cheggaaa/pb which sadly does not export this nicely written functions and to whom all credits should go. Only slight modifications.

Index

Constants

View Source
const (
	PROGRESS_BAR_ADDON_OFF progressBarAddon = iota
	PROGRESS_BAR_ADDON_PREPEND
	PROGRESS_BAR_ADDON_APPEND
)
View Source
const (
	TERM_TIOCGWINSZ     = 0x5413
	TERM_TIOCGWINSZ_OSX = 1074295912
	TERM_DEFAULT_WIDTH  = 78
)
View Source
const (
	LINE_BREAK = "\n"
)
View Source
const (
	PROGRESS_BAR_DEFAULT_REFRESH = time.Millisecond * 50
)

Variables

View Source
var (
	RenderAskQuestion = func(question string) string {
		return "<query>" + strings.TrimRight(question, " ") + "<reset> "
	}
	RenderInputRequiredError = fmt.Errorf("Input required")
)
View Source
var (
	DefaultOutputTableHeaderRenderer = func(out Output) func(string) string {
		return func(content string) string {
			return strings.Join(SplitFormattedString(out.Sprintf("<headline>%s<reset>", content)), "\n")
		}
	}
	DefaultOutputTableContentRenderer = func(out Output) func(string) string {
		return func(content string) string {
			from := strings.Replace(content, "\n", "<BR>", -1)
			from = rxControlCharacters.ReplaceAllString(from, "<CTRL>")

			return strings.Join(SplitFormattedString(out.Sprintf(content)), "\n")
		}
	}
)
View Source
var (
	// ProgressBarDefaultRenderElapsed
	ProgressBarDefaultRenderCount = func(pos, max int, bar ProgressBar) string {
		l := len(fmt.Sprintf("%d", max))
		return fmt.Sprintf("%"+fmt.Sprintf("%d", l)+"d/%d", pos, max)
	}
	ProgressBarDefaultRenderElapsed = func(elapsed time.Duration, bar ProgressBar) string {
		return fmt.Sprintf("@%s", RenderFixedSizeDuration(elapsed))
	}
	ProgressBarDefaultRenderEstimated = func(forecast time.Duration, bar ProgressBar) string {
		return fmt.Sprintf("~%s", RenderFixedSizeDuration(forecast))
	}
	ProgressBarDefaultRenderPercentage = func(percent float32, bar ProgressBar) string {
		if percent > 99.99 {
			return " 100%"
		} else {
			return fmt.Sprintf("%4.1f%%", percent)
		}
	}
	ProgressBarDefaultRenderPrefix = func(count, elapsed, estimate, percentage string) string {
		out := []string{}
		for _, s := range []string{count, elapsed, estimate, percentage} {
			if s != "" {
				out = append(out, s)
			}
		}
		if len(out) == 0 {
			return ""
		}
		return strings.Join(out, " / ") + " "
	}
	ProgressBarDefaultRenderSuffix = func(count, elapsed, estimate, percentage string) string {
		out := []string{}
		for _, s := range []string{count, elapsed, estimate, percentage} {
			if s != "" {
				out = append(out, s)
			}
		}
		if len(out) == 0 {
			return ""
		}
		return " " + strings.Join(out, " / ")
	}

	// ProgressBarStyleAscii is an ASCII encoding based style for rendering the progress bar
	ProgressBarStyleAscii = &ProgressBarStyle{
		Empty:       '-',
		Progress:    '=',
		Rightmost:   '>',
		None:        '-',
		LeftBorder:  '[',
		RightBorder: ']',
		Percentage:  PROGRESS_BAR_ADDON_APPEND,
		Elapsed:     PROGRESS_BAR_ADDON_PREPEND,
	}

	// ProgressBarStyleUtf8 is an UTF-8 encoding based style for rendering the progress bar
	ProgressBarStyleUtf8 = &ProgressBarStyle{
		Empty:       ' ',
		Progress:    '█',
		Rightmost:   '▓',
		None:        '░',
		LeftBorder:  '▕',
		RightBorder: '▏',
		Percentage:  PROGRESS_BAR_ADDON_APPEND,
		Elapsed:     PROGRESS_BAR_ADDON_PREPEND,
	}
)
View Source
var (
	ClosedTableStyle = &TableStyle{
		Bottom:          "─",
		ContentRenderer: func(content string) string { return content },
		CrossBottom:     "┴",
		CrossInner:      "┼",
		CrossLeft:       "├",
		CrossRight:      "┤",
		CrossTop:        "┬",
		HeaderRenderer:  func(content string) string { return fmt.Sprintf("\033[1;4m%s\033[0m", content) },
		InnerHorizontal: "─",
		InnerVertical:   "│",
		Left:            "│",
		LeftBottom:      "└",
		LeftTop:         "┌",
		Prefix:          " ",
		Right:           "│",
		RightBottom:     "┘",
		RightTop:        "┐",
		Suffix:          " ",
		Top:             "─",
	}
	ClosedTableStyleLight = &TableStyle{
		Bottom:          "\033[38;5;234m─\033[0m",
		ContentRenderer: func(content string) string { return content },
		CrossBottom:     "\033[38;5;234m┴\033[0m",
		CrossInner:      "\033[38;5;234m┼\033[0m",
		CrossLeft:       "\033[38;5;234m├\033[0m",
		CrossRight:      "\033[38;5;234m┤\033[0m",
		CrossTop:        "\033[38;5;234m┬\033[0m",
		HeaderRenderer:  func(content string) string { return fmt.Sprintf("\033[1;4m%s\033[0m", content) },
		InnerHorizontal: "\033[38;5;234m─\033[0m",
		InnerVertical:   "\033[38;5;234m│\033[0m",
		Left:            "\033[38;5;234m│\033[0m",
		LeftBottom:      "\033[38;5;234m└\033[0m",
		LeftTop:         "\033[38;5;234m┌\033[0m",
		Prefix:          " ",
		Right:           "\033[38;5;234m│\033[0m",
		RightBottom:     "\033[38;5;234m┘\033[0m",
		RightTop:        "\033[38;5;234m┐\033[0m",
		Suffix:          " ",
		Top:             "\033[38;5;234m─\033[0m",
	}
	OpenTableStyle = &TableStyle{
		Bottom:          "",
		ContentRenderer: func(content string) string { return content },
		CrossBottom:     "┴",
		CrossInner:      "┼",
		CrossLeft:       "",
		CrossRight:      "",
		CrossTop:        "┬",
		HeaderRenderer:  func(content string) string { return fmt.Sprintf("\033[1m%s\033[0m", content) },
		InnerHorizontal: "─",
		InnerVertical:   "│",
		Left:            "",
		LeftBottom:      "",
		LeftTop:         "",
		Prefix:          " ",
		Right:           "",
		RightBottom:     "",
		RightTop:        "",
		Suffix:          " ",
		Top:             "",
	}
	OpenTableStyleLight = &TableStyle{
		Bottom:          "",
		ContentRenderer: func(content string) string { return content },
		CrossBottom:     "\033[38;5;234m┴\033[0m",
		CrossInner:      "\033[38;5;234m┼\033[0m",
		CrossLeft:       "",
		CrossRight:      "",
		CrossTop:        "\033[38;5;234m┬\033[0m",
		HeaderRenderer:  func(content string) string { return fmt.Sprintf("\033[1m%s\033[0m", content) },
		InnerHorizontal: "\033[38;5;234m─\033[0m",
		InnerVertical:   "\033[38;5;234m│\033[0m",
		Left:            "",
		LeftBottom:      "",
		LeftTop:         "",
		Prefix:          " ",
		Right:           "",
		RightBottom:     "",
		RightTop:        "",
		Suffix:          " ",
		Top:             "",
	}
	DefaultTableStyle *TableStyle
)
View Source
var (

	// TermWidthCall is the callback returning the terminal width
	TermWidthCall func() (int, error)

	// TermWidthCurrent contains the current terminal width from the last
	// call of `TerminalWidth()` (which is called in `init()`)
	TermWidthCurrent = TERM_DEFAULT_WIDTH
)
View Source
var ConfirmNoRegex = regexp.MustCompile(`^(?i)no?$`)

ConfirmNoRegex is the regular expression used to check if the user replied negative

View Source
var ConfirmRejection = "<warn>Please respond with \"yes\" or \"no\"<reset>\n\n"

ConfirmRejection is the message replied to the user if she does not answer with "yes", "y", "no" or "n" (case insensitive)

View Source
var ConfirmYesRegex = regexp.MustCompile(`^(?i)y(es)?$`)

ConfirmYesRegex is the regular expression used to check if the user replied positive

View Source
var Dbg = func(msg string, args ...interface{}) {
	if v := os.Getenv("DEBUG_CLIF"); v != "1" {
		return
	}
	if dbgFh == nil {
		dbgFh, _ = os.OpenFile(filepath.Join(os.TempDir(), "debug.clif"), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	}
	dbgFh.Write([]byte(fmt.Sprintf("[%s] %s\n", time.Now(), fmt.Sprintf(msg, args...))))
}
View Source
var DebugStyles = map[string]string{
	"error":     "E:",
	"warn":      "W:",
	"info":      "I:",
	"success":   "S:",
	"debug":     "D:",
	"headline":  "H:",
	"subline":   "U:",
	"important": "P:",
	"query":     "Q:",
	"reset":     "R:",
}
View Source
var DefaultFormatterEscape = func(msg string) string {
	return DefaultFormatterTokenRegex.ReplaceAllStringFunc(msg, func(token string) string {
		return `\` + token
	})
}

DefaultFormatterEscape is a callback to replace all tokens with escaped versions

View Source
var DefaultFormatterPost = func(msg string) string {
	return strings.Replace(msg, "~~~~#~~~~", "<", -1)
}

DefaultFormatterPost is a post-callback, after DefaultFormatterTokenRegex is used

View Source
var DefaultFormatterPre = func(msg string) string {
	return strings.Replace(msg, `\<`, "~~~~#~~~~", -1)
}

DefaultFormatterPre is a pre-callback, before DefaultFormatterTokenRegex is used

View Source
var DefaultFormatterTokenRegex = regexp.MustCompile(`(<[^>]+>)`)

DefaultFormatterTokenRegex is a regex to find all tokens, used by the DefaultFormatter

View Source
var DefaultHelpOption = &Option{
	parameter: parameter{
		Name:        "help",
		Usage:       "Display this help message",
		Description: "Display this help message",
	},
	Alias: "h",
	Flag:  true,
}

DefaultHelpOption is the "--help" option, which is (per default) added to any command.

View Source
var DefaultOptions = []*Option{DefaultHelpOption}

DefaultOptions are prepended to any newly created command. Will be added immediately in the `NewCommand` call. See also `cli.NewDefaultOption()` and `cli.AddDefaultOptions()`.

View Source
var DefaultStyles = map[string]string{
	"error":     "\033[31;1m",
	"warn":      "\033[33m",
	"info":      "\033[34m",
	"success":   "\033[32m",
	"debug":     "\033[30;1m",
	"headline":  "\033[4;1m",
	"subline":   "\033[4m",
	"important": "\033[47;30;1m",
	"query":     "\033[36m",
	"reset":     "\033[0m",
}
View Source
var DescribeCli = func(c *Cli) string {

	line := "<headline>" + c.Name + "<reset>"
	if c.Version != "" {
		line += " <debug>(" + c.Version + ")<reset>"
	}
	lines := []string{line}

	if c.Description != "" {
		lines = append(lines, "<info>"+c.Description+"<reset>\n")
	}

	prog := filepath.Base(os.Args[0])
	lines = append(lines, fmt.Sprintf("<subline>Usage:<reset>\n  %s command [arg ..] [--opt val ..]\n", prog))

	lines = append(lines, "<subline>Available commands:<reset>")
	max := 0
	ordered := make(map[string][]*Command)
	prefices := make([]string, 0)
	for _, cmd := range c.Commands {
		if l := len(cmd.Name); l > max {
			max = l
		}
		prefix := ""
		if i := strings.Index(cmd.Name, ":"); i > -1 {
			prefix = cmd.Name[0:i]
		}
		if ordered[prefix] == nil {
			prefices = append(prefices, prefix)
			ordered[prefix] = make([]*Command, 0)
		}
		ordered[prefix] = append(ordered[prefix], cmd)
	}
	sort.Strings(prefices)
	for _, prefix := range prefices {
		if prefix != "" {
			lines = append(lines, fmt.Sprintf(" <subline>%s<reset>", prefix))
		}
		sort.Sort(CommandsSort(ordered[prefix]))
		for _, cmd := range ordered[prefix] {
			lines = append(lines, fmt.Sprintf("  <info>%-"+fmt.Sprintf("%d", max)+"s<reset>  %s", cmd.Name, cmd.Usage))
		}
	}

	return strings.Join(lines, "\n") + "\n"
}

DescribeCli command implements the string rendering of a cli which help uses. Can be overwritten at users discretion.

View Source
var DescribeCommand = func(c *Command) string {
	lines := []string{"Command: <headline>" + c.Name + "<reset>"}

	if c.Description != "" {
		lines = append(lines, []string{"<info>" + c.Description + "<reset>", ""}...)
	} else if c.Usage != "" {
		lines = append(lines, []string{"<info>" + c.Usage + "<reset>", ""}...)
	}

	lines = append(lines, "<subline>Usage:<reset>")
	usage := []string{c.Name}
	args := make([][]string, 0)
	argMax := 0
	opts := make([][]string, 0)
	optMax := 0
	for _, p := range c.Arguments {
		var short string
		usg := p.Usage
		short = p.Name
		usgInfo := []string{}
		if p.Multiple {
			short = short + " ..."
			usgInfo = append(usgInfo, `<debug>mult<reset>`)
		}
		if p.Required {
			usgInfo = append(usgInfo, `<important>req<reset>`)
		} else {
			short = fmt.Sprintf("[%s]", short)
		}
		if p.Env != "" {
			usgInfo = append(usgInfo, fmt.Sprintf(`env: <debug>%s<reset>`, p.Env))
		}
		if p.Default != "" {
			usgInfo = append(usgInfo, fmt.Sprintf(`default: <debug>"%s"<reset>`, p.Default))
		}
		if l := len(p.Name); l > argMax {
			argMax = l
		}
		usage = append(usage, short)
		if len(usgInfo) > 0 {
			usg += " (" + strings.Join(usgInfo, ", ") + ")"
		}
		args = append(args, []string{p.Name, usg})
	}

	for _, p := range c.Options {
		short := fmt.Sprintf("--%s", p.Name)
		if p.Alias != "" {
			short += "|-" + p.Alias
		}
		if !p.Flag {
			short += " val"
		}
		long := short
		usg := p.Usage
		usgInfo := []string{}
		if p.Multiple {
			short = short + " ..."
			usgInfo = append(usgInfo, `<debug>mult<reset>`)
		}
		if !p.Required {
			short = "[" + short + "]"
		} else {
			usgInfo = append(usgInfo, `<important>req<reset>`)
		}
		if p.Env != "" {
			usgInfo = append(usgInfo, fmt.Sprintf(`env: <debug>%s<reset>`, p.Env))
		}
		if p.Default != "" {
			usgInfo = append(usgInfo, fmt.Sprintf(`default: <debug>"%s"<reset>`, p.Default))
		}
		if l := len(long); l > optMax {
			optMax = l
		}
		usage = append(usage, short)
		if len(usgInfo) > 0 {
			usg += " (" + strings.Join(usgInfo, ", ") + ")"
		}
		opts = append(opts, []string{long, usg})
	}
	lines = append(lines, "  "+strings.Join(usage, " "))
	lines = append(lines, "")

	if len(args) > 0 {
		lines = append(lines, "<subline>Arguments:<reset>")
		for _, l := range args {
			lines = append(lines, fmt.Sprintf("  <info>%-"+fmt.Sprintf("%d", argMax)+"s<reset>  %s", l[0], l[1]))
		}
		lines = append(lines, "")
	}

	if len(opts) > 0 {
		lines = append(lines, "<subline>Options:<reset>")
		for _, l := range opts {
			lines = append(lines, fmt.Sprintf("  <info>%-"+fmt.Sprintf("%d", optMax)+"s<reset>  %s", l[0], l[1]))
		}
		lines = append(lines, "")
	}

	return strings.Join(lines, "\n") + "\n"
}

DescribeCommand implements the string rendering of a command which help uses. Can be overwritten at users discretion.

View Source
var Die = func(msg string, args ...interface{}) {
	NewColorOutput(os.Stderr).Printf("<error>"+msg+"<reset>\n", args...)
	Exit(1)
}

Die is the default function executed on die. It can be used as a shorthand via `clif.Die("foo %s", "bar")` and can be overwritten to change the failure exit handling CLI-wide.

View Source
var (
	ErrHeadersNotSetYet = fmt.Errorf("Cannot add/set data when headers are not set")
)
View Source
var Exit = func(s int) {
	os.Exit(s)
}

Exit is wrapper for os.Exit, so it can be overwritten for tests or edge use cases

View Source
var (
	// PbOutOfBoundError is returned when increasing, adding or setting the position
	// with a value which is below 0 or beyond size of the progress bar
	PbOutOfBoundError = fmt.Errorf("Position is out of bounds")
)
View Source
var RenderChooseOption = func(key, value string, size int) string {
	return fmt.Sprintf("  <query>%-"+fmt.Sprintf("%d", size+1)+"s<reset> %s\n", key+")", value)
}

RenderChooseOption is the method used by default input `Choose()` method to to render a singular choice into a string. Can be overwritten at users discretion.

View Source
var RenderChooseQuery = func() string {
	return "Choose: "
}

RenderChooseQuery is the method used by default input `Choose()` method to to render the query prompt choice (after the choices) into a string. Can be overwritten at users discretion.

View Source
var RenderChooseQuestion = func(question string) string {
	return question + "\n"
}

RenderChooseQuestion is the method used by default input `Choose()` method to to render the question (displayed before listing the choices) into a string. Can be overwritten at users discretion.

View Source
var SunburnStyles = map[string]string{
	"error":     "\033[97;48;5;196;1m",
	"warn":      "\033[30;48;5;208;2m",
	"info":      "\033[38;5;142;2m",
	"success":   "\033[38;5;2;2m",
	"debug":     "\033[38;5;242;2m",
	"headline":  "\033[38;5;226;1m",
	"subline":   "\033[38;5;228;1m",
	"important": "\033[38;5;15;2;4m",
	"query":     "\033[38;5;77m",
	"reset":     "\033[0m",
}

http://misc.flogisoft.com/bash/tip_colors_and_formatting#colors1

View Source
var (
	TableColWrapper = func(limit int) *Wrapper {
		wrapper := NewWrapper(uint(limit))
		wrapper.WhitespaceMode = WRAP_WHITESPACE_CONTRACT
		wrapper.TrimMode = WRAP_TRIM_RIGHT
		wrapper.BreakWords = true
		return wrapper
	}
)
View Source
var WinterStyles = map[string]string{
	"error":     "\033[97;48;5;89;1m",
	"warn":      "\033[30;48;5;97;2m",
	"info":      "\033[38;5;69;2m",
	"success":   "\033[38;5;45;1m",
	"debug":     "\033[38;5;239;2m",
	"headline":  "\033[38;5;21;1m",
	"subline":   "\033[38;5;27;1m",
	"important": "\033[38;5;15;2;4m",
	"query":     "\033[38;5;111m",
	"reset":     "\033[0m",
}

http://misc.flogisoft.com/bash/tip_colors_and_formatting#colors1

Functions

func InputAny

func InputAny(s string) error

func InputEmptyOk

func InputEmptyOk(s string) error

func IsCallError

func IsCallError(err error) bool

func IsControlCharStart

func IsControlCharStart(c byte) bool

IsControlCharStart returns bool whether character is "\033" aka "\e"

func IsFloat

func IsFloat(name, value string) (string, error)

IsFloat checks if value is float

func IsInt

func IsInt(name, value string) (string, error)

IsInt checks if value is an integer

func RenderFixedSizeDuration

func RenderFixedSizeDuration(dur time.Duration) string

func SplitFormattedString

func SplitFormattedString(str string) []string

SplitFormattedString splits formatted string into multiple lines while making sure that control characters end at line end and possibly re-start at next line

func StringLength

func StringLength(str string) int

StringLength returns the length of an UTF8 string without any control characters

func TermWidth

func TermWidth() (int, error)

TerminalWidth returns the terminal width in amount of characters

func Wrap

func Wrap(s string, limit uint) string

Types

type Argument

type Argument struct {
	// contains filtered or unexported fields
}

Arguments must be provided immediately after the command in the order they were added. Non required arguments must be ordered after required arguments. Only one argument is allowed to contain multiple values and it needs to be the last one.

func NewArgument

func NewArgument(name, usage, _default string, required, multiple bool) *Argument

NewArgument constructs a new argument

func (*Argument) Assign

func (this *Argument) Assign(val string) error

Assign tries to add value to parameter and returns error if it fails due to invalid format or invalid amount (single vs multiple parameters)

func (*Argument) Bool

func (this *Argument) Bool() bool

Bool representation of the value (will be false, if not given or not parsable)

func (*Argument) Bools

func (this *Argument) Bools() []bool

Bools returns values as bool array (values will be false, if not parsable to float64)

func (*Argument) Count

func (this *Argument) Count() int

Provided returns amount of values provided

func (*Argument) Float

func (this *Argument) Float() float64

Float representation of the value (will be 0.0, if not given or not parsable)

func (*Argument) Floats

func (this *Argument) Floats() []float64

Floats returns values as float64 array (values will be 0.0, if not parsable to float64)

func (*Argument) Int

func (this *Argument) Int() int

Int representation of the value (will be 0, if not given or not parsable)

func (*Argument) Ints

func (this *Argument) Ints() []int

Ints returns values as int array (values will be 0, if not parsable to int)

func (*Argument) Json

func (this *Argument) Json() (map[string]interface{}, error)

Json assumes the input is a JSON string and parses into a standard map[string]interface{} Returns error, if not parsable (or eg array JSON).

Helpful to allow complex inputs: `my-app do --foo '{"bar": "baz"}'`

func (*Argument) Jsons

func (this *Argument) Jsons() ([]map[string]interface{}, error)

Jsons returns values as individual JSON strings. See `Json()` above.

func (*Argument) Provided

func (this *Argument) Provided() bool

Provided returns bool whether argument was provided

func (*Argument) SetDefault

func (this *Argument) SetDefault(v string) *Argument

SetDefault is a builder method to set default value. Default value is used if the argument is not provided (after environment variable).

func (*Argument) SetDescription

func (this *Argument) SetDescription(v string) *Argument

SetDescription is a builder method to sets argument description. Description is an elaborate explanation which is used in help generation.

func (*Argument) SetEnv

func (this *Argument) SetEnv(v string) *Argument

SetEnv is a builder method to set environment variable name, from which to take the value, if not provided (before default).

func (*Argument) SetParse

func (this *Argument) SetParse(v ParseMethod) *Argument

SetParse is a builder method to set setup call on value. The setup call must return a replace value (can be unchanged) or an error, which stops evaluation and returns error to user.

func (*Argument) SetRegex

func (this *Argument) SetRegex(r *regexp.Regexp) *Argument

SetRegex is a builder method to set regular expression which is used to check the the argument input (in case of multiple: each will be checked)

func (*Argument) SetUsage

func (this *Argument) SetUsage(v string) *Argument

SetUsage is builder method to set the usage description. Usage is a short account of what the argument is used for, for help generaiton.

func (*Argument) String

func (this *Argument) String() string

String representation of the value (can be empty string)

func (*Argument) Strings

func (this *Argument) Strings() []string

Strings returns values as array of strings

func (*Argument) Time

func (this *Argument) Time(format ...string) (*time.Time, error)

Time is a date time representation of the value with a provided format. If no format is provided, then `2006-01-02 15:04:05` will be used

func (*Argument) Times

func (this *Argument) Times(format ...string) ([]time.Time, error)

Times returns array for `time.Time` values, parsed from provided format. See `Time()`.

type CallError

type CallError struct {
	// contains filtered or unexported fields
}

func NewCallError

func NewCallError(err error) *CallError

func (*CallError) Error

func (this *CallError) Error() string

type CallMethod

type CallMethod interface{}

CallMethod is the interface for functions used as command callbacks. `interface{}` is used so that those functions can have arbitrary input and output signatures. However, they still must be functions.

type Cli

type Cli struct {

	// Name is the name of the console application used in the generated help
	Name string

	// Version is used in the generated help
	Version string

	// Description is used in the generated help
	Description string

	// Commands contain all registered commands and can be manipulated directly
	Commands map[string]*Command

	// Heralds contain list of command-create-callbacks which will be executed on `Run()`
	Heralds []HeraldCallback

	// Registry is a container holding objects for injection
	Registry *Registry

	// DefaultOptions contains options which are added to all commands early in the `Run()` call.
	DefaultOptions []*Option

	// DefaultCommand contains name of the command which is executed if non is given. Defaults to "list"
	DefaultCommand string

	// PreCall is executed before the chosen command is called, if defined
	PreCall func(c *Command) error
	// contains filtered or unexported fields
}

Cli is a command line interface object

func New

func New(name, version, desc string) *Cli

New constructs new cli

func (*Cli) Add

func (this *Cli) Add(cmd ...*Command) *Cli

Add is a builder method for adding a new command

func (*Cli) AddDefaultOptions

func (this *Cli) AddDefaultOptions(opts ...*Option) *Cli

AddDefaultOptions adds a list of options to default options.

func (*Cli) Call

func (this *Cli) Call(c *Command) ([]reflect.Value, error)

Call executes command by building all input parameters based on objects registered in the container and running the callback.

func (*Cli) Herald

func (this *Cli) Herald(cmd ...HeraldCallback) *Cli

Herald registers command constructors, which will be executed in `Run()`.

func (*Cli) Named

func (this *Cli) Named(n string) interface{}

Named returns a named parameter of registry or nil

func (*Cli) New

func (this *Cli) New(name, usage string, call CallMethod) *Cli

New creates and adds a new command

func (*Cli) NewDefaultOption

func (this *Cli) NewDefaultOption(name, alias, usage, _default string, required, multiple bool) *Cli

NewDefaultOption creates and adds a new option to default list.

func (*Cli) Output

func (this *Cli) Output() Output

Output is shorthand for currently registered output

func (*Cli) Register

func (this *Cli) Register(v interface{}) *Cli

RegisterAs is builder method and registers object in registry

func (*Cli) RegisterAs

func (this *Cli) RegisterAs(n string, v interface{}) *Cli

RegisterAs is builder method and registers object under alias in registry

func (*Cli) RegisterNamed

func (this *Cli) RegisterNamed(n string, v interface{}) *Cli

RegisterNamed registers a parameter for injecting in a named map[string]inteface{}

func (*Cli) Run

func (this *Cli) Run()

Run with OS command line arguments

func (*Cli) RunWith

func (this *Cli) RunWith(args []string)

RunWith runs the cli with custom list of arguments

func (*Cli) SeparateArgs

func (this *Cli) SeparateArgs(args []string) (string, []string)

SeparateArgs takes (command line) args and tries to separate command name and the actual args & options

func (*Cli) SetDefaultCommand

func (this *Cli) SetDefaultCommand(v string) *Cli

SetDefaultCommand is builder method and overwrites the default command ("list") with something else

func (*Cli) SetDescription

func (this *Cli) SetDescription(v string) *Cli

SetDescription is builder method and sets description

func (*Cli) SetInput

func (this *Cli) SetInput(in Input) *Cli

SetOutput is builder method and replaces current input

func (*Cli) SetOnInterrupt

func (this *Cli) SetOnInterrupt(cb func() error) *Cli

SetOnInterrupt sets callback for interrupt signal (ctrl+c)

func (*Cli) SetOutput

func (this *Cli) SetOutput(out Output) *Cli

SetOutput is builder method and replaces current output

func (*Cli) SetPreCall

func (this *Cli) SetPreCall(cb func(c *Command) error) *Cli

SetPreCall is builder method and sets a prepare method, which is called before any command is run

type Command

type Command struct {

	// Cli back-references the Cli in which the command is registered
	Cli *Cli

	// Name is the unique (within Cli scope) call-name of the command
	Name string

	// Usage is a shorthand description of what the command does. Used in help output.
	Usage string

	// Description is a long elaboration on what the command does. Used in help output.
	Description string

	// Options contain all the registered options of the command.
	Options []*Option

	// Arguments contain all the registered arguments of the command.
	Arguments []*Argument

	// Call holds reflections of the callback.
	Call reflect.Value
	// contains filtered or unexported fields
}

Command represents a named callback with a set of arguments and options

func NewCommand

func NewCommand(name, usage string, call CallMethod) *Command

NewCommand constructs a new command

func NewHelpCommand

func NewHelpCommand() *Command

NewHelpCommand returns the default help command

func NewListCommand

func NewListCommand() *Command

NewListCommand returns the default help command

func (*Command) AddArgument

func (this *Command) AddArgument(v *Argument) *Command

AddArgument is builder method to add a new argument

func (*Command) AddOption

func (this *Command) AddOption(v *Option) *Command

AddOption is builder method to add a new option

func (*Command) Argument

func (this *Command) Argument(name string) *Argument

Argument provides access to registered, named arguments.

func (*Command) Input

func (this *Command) Input() map[string][]string

Input returns map containing whole input values (of all options, all arguments)

func (*Command) NewArgument

func (this *Command) NewArgument(name, usage, _default string, required, multiple bool) *Command

NewArgument is builder method to construct and add a new argument

func (*Command) NewFlag

func (this *Command) NewFlag(name, alias, usage string, multiple bool) *Command

NewFlag adds a new flag option

func (*Command) NewOption

func (this *Command) NewOption(name, alias, usage, _default string, required, multiple bool) *Command

NewOption is builder method to construct and add a new option

func (*Command) Option

func (this *Command) Option(name string) *Option

Option provides access to registered, named options.

func (*Command) Parse

func (this *Command) Parse(args []string) error

Parse extracts options and arguments from command line arguments

func (*Command) SetCli

func (this *Command) SetCli(c *Cli) *Command

SetCli is builder method and sets the Cli back-reference

func (*Command) SetDescription

func (this *Command) SetDescription(desc string) *Command

SetDescription is builder method setting description

func (*Command) SetPostCall

func (this *Command) SetPostCall(call CallMethod) *Command

func (*Command) SetPreCall

func (this *Command) SetPreCall(call CallMethod) *Command

type CommandsSort

type CommandsSort []*Command

CommandSort implements the `sort.Sortable` interface for commands, based on the command `Name` attribute

func (CommandsSort) Len

func (this CommandsSort) Len() int

func (CommandsSort) Less

func (this CommandsSort) Less(i, j int) bool

func (CommandsSort) Swap

func (this CommandsSort) Swap(i, j int)

type DefaultFormatter

type DefaultFormatter struct {
	// contains filtered or unexported fields
}

DefaultFormatter strips all formatting from the output message

func NewDefaultFormatter

func NewDefaultFormatter(styles map[string]string) *DefaultFormatter

NewDefaultFormatter constructs a new constructor with the given styles

func (*DefaultFormatter) Escape

func (this *DefaultFormatter) Escape(msg string) string

func (*DefaultFormatter) Format

func (this *DefaultFormatter) Format(msg string) string

type DefaultInput

type DefaultInput struct {
	// contains filtered or unexported fields
}

DefaultInput is the default used input implementation

func NewDefaultInput

func NewDefaultInput(in io.Reader, out Output) *DefaultInput

NewDefaultInput constructs a new default input implementation on given io reader (if nil, fall back to `os.Stdin`). Requires Output for issuing questions to user.

func (*DefaultInput) Ask

func (this *DefaultInput) Ask(question string, check func(string) error) string

func (*DefaultInput) AskRegex

func (this *DefaultInput) AskRegex(question string, rx *regexp.Regexp) string

func (*DefaultInput) Choose

func (this *DefaultInput) Choose(question string, choices map[string]string) string

func (*DefaultInput) Confirm

func (this *DefaultInput) Confirm(question string) bool

type DefaultOutput

type DefaultOutput struct {
	// contains filtered or unexported fields
}

DefaultOutput is the default used output type

func NewColorOutput

func NewColorOutput(io io.Writer) *DefaultOutput

NewColoredOutput returns default output (on `os.Stdout`, if io is nil) using a formatter which applies the default color styles to style tokens on output.

func NewDebugOutput

func NewDebugOutput(io io.Writer) *DefaultOutput

NewDebugOutput is used for debugging the color formatter

func NewMonochromeOutput

func NewMonochromeOutput(io io.Writer) *DefaultOutput

NewMonochromeOutput returns default output (on `os.Stdout`, if io is nil) using a formatter which strips all style tokens (hence rendering non-colored, plain strings)

func NewOutput

func NewOutput(io io.Writer, f Formatter) *DefaultOutput

NewOutput generates a new (default) output with provided io writer (if nil then `os.Stdout` is used) and a formatter

func (*DefaultOutput) Escape

func (this *DefaultOutput) Escape(msg string) string

func (*DefaultOutput) Printf

func (this *DefaultOutput) Printf(msg string, args ...interface{})

func (*DefaultOutput) ProgressBars

func (this *DefaultOutput) ProgressBars() ProgressBarPool

func (*DefaultOutput) SetFormatter

func (this *DefaultOutput) SetFormatter(f Formatter) Output

func (*DefaultOutput) Sprintf

func (this *DefaultOutput) Sprintf(msg string, args ...interface{}) string

func (*DefaultOutput) Table

func (this *DefaultOutput) Table(headers []string, style ...*TableStyle) *Table

func (*DefaultOutput) Writer

func (this *DefaultOutput) Writer() io.Writer

type Formatter

type Formatter interface {

	// Escape escapes a string, so that no formatter tokens will be interpolated (eg `<foo>` -> `\<foo>`)
	Escape(msg string) string

	// Format renders message for output by applying <style> tokens
	Format(msg string) string
}

Formatter is used by Output for rendering. It supports style directives in the form <info> or <end> or suchlike

type HeraldCallback

type HeraldCallback func(*Cli) *Command

HeraldCallback is type for creator method, which can be registered with the `Herald()` method.

type Input

type Input interface {

	// Ask prints question to user and then reads user input and returns as soon
	// as it's non empty or queries again until it is
	Ask(question string, check func(string) error) string

	// AskRegex prints question to user and then reads user input, compares it
	// against regex and return if matching or queries again until it does
	AskRegex(question string, rx *regexp.Regexp) string

	// Choose renders choices for user and returns what was choosen
	Choose(question string, choices map[string]string) string

	// Confirm prints question to user until she replies with "yes", "y", "no" or "n"
	Confirm(question string) bool
}

Input is an interface for input helping. It provides shorthand methods for often used CLI interactions.

type NamedParameters

type NamedParameters map[string]interface{}

NamedParameters ...

type Option

type Option struct {

	// Alias can be a shorter name
	Alias string

	// If is a flag, then no value can be assigned (if present, then bool true)
	Flag bool
	// contains filtered or unexported fields
}

Option is a user input which is initialized with a single or double dash (eg "--foo" or "-f"). It may not be followed by a value, in which case it is considered a flag (see `IsFlag`). Options can be multiple inputs (no restrictions as there are for Arguments). An option can be required or optional. Options do not need to have any particular order.

func NewFlag

func NewFlag(name, alias, usage string, multiple bool) *Option

NewFlag constructs new flag option

func NewOption

func NewOption(name, alias, usage, _default string, required, multiple bool) *Option

NewOption constructs new option

func (*Option) Assign

func (this *Option) Assign(val string) error

Assign tries to add value to parameter and returns error if it fails due to invalid format or invalid amount (single vs multiple parameters)

func (*Option) Bool

func (this *Option) Bool() bool

Bool representation of the value (will be false, if not given or not parsable)

func (*Option) Bools

func (this *Option) Bools() []bool

Bools returns values as bool array (values will be false, if not parsable to float64)

func (*Option) Count

func (this *Option) Count() int

Provided returns amount of values provided

func (*Option) Float

func (this *Option) Float() float64

Float representation of the value (will be 0.0, if not given or not parsable)

func (*Option) Floats

func (this *Option) Floats() []float64

Floats returns values as float64 array (values will be 0.0, if not parsable to float64)

func (*Option) Int

func (this *Option) Int() int

Int representation of the value (will be 0, if not given or not parsable)

func (*Option) Ints

func (this *Option) Ints() []int

Ints returns values as int array (values will be 0, if not parsable to int)

func (*Option) IsFlag

func (this *Option) IsFlag() *Option

IsFlag marks an option as a flag. A Flag does not have any values. If it exists (eg "--verbose"), then it is automatically initialized with the string "true", which then can be checked with the `Bool()` method for actual `bool`

func (*Option) Json

func (this *Option) Json() (map[string]interface{}, error)

Json assumes the input is a JSON string and parses into a standard map[string]interface{} Returns error, if not parsable (or eg array JSON).

Helpful to allow complex inputs: `my-app do --foo '{"bar": "baz"}'`

func (*Option) Jsons

func (this *Option) Jsons() ([]map[string]interface{}, error)

Jsons returns values as individual JSON strings. See `Json()` above.

func (*Option) Provided

func (this *Option) Provided() bool

Provided returns bool whether argument was provided

func (*Option) SetDefault

func (this *Option) SetDefault(v string) *Option

SetDefault is a builder method to set default value. Default value is used if the option is not provided (after environment variable).

func (*Option) SetDescription

func (this *Option) SetDescription(v string) *Option

SetDescription is a builder method to sets option description. Description is an elaborate explanation which is used in help generation.

func (*Option) SetEnv

func (this *Option) SetEnv(v string) *Option

SetEnv is a builder method to set environment variable name, from which to take the value, if not provided (before default).

func (*Option) SetParse

func (this *Option) SetParse(v ParseMethod) *Option

SetParse is a builder method to set setup call on value. The setup call must return a replace value (can be unchanged) or an error, which stops evaluation and returns error to user.

func (*Option) SetRegex

func (this *Option) SetRegex(r *regexp.Regexp) *Option

SetRegex is a builder method to set regular expression which is used to check the the option input (in case of multiple: each will be checked)

func (*Option) SetUsage

func (this *Option) SetUsage(v string) *Option

SetUsage is builder method to set the usage description. Usage is a short account of what the option is used for, for help generaiton.

func (*Option) String

func (this *Option) String() string

String representation of the value (can be empty string)

func (*Option) Strings

func (this *Option) Strings() []string

Strings returns values as array of strings

func (*Option) Time

func (this *Option) Time(format ...string) (*time.Time, error)

Time is a date time representation of the value with a provided format. If no format is provided, then `2006-01-02 15:04:05` will be used

func (*Option) Times

func (this *Option) Times(format ...string) ([]time.Time, error)

Times returns array for `time.Time` values, parsed from provided format. See `Time()`.

type Output

type Output interface {

	// Escape escapes a string, so that no formatter tokens will be interpolated (eg `<foo>` -> `\<foo>`)
	Escape(s string) string

	// Printf applies format (renders styles) and writes to output
	Printf(msg string, args ...interface{})

	// ProgressBars returns the pool of progress bars
	ProgressBars() ProgressBarPool

	// Sprintf applies format (renders styles) and returns as string
	Sprintf(msg string, args ...interface{}) string

	// SetFormatter is builder method and replaces current formatter
	SetFormatter(f Formatter) Output

	// Table creates a table object
	Table(header []string, style ...*TableStyle) *Table

	// Writer returns the `io.Writer` used by this output
	Writer() io.Writer
}

Output is interface for

type ParseMethod

type ParseMethod func(name, value string) (string, error)

SetupMethod is type for callback on Setup of Argument or Option. The return string replaces the original input. Called on each value (in case of multiple).

func IsAll

func IsAll(v ...ParseMethod) ParseMethod

IsAll joins a set of validators methods and returns true if ALL of them match

func IsAny

func IsAny(v ...ParseMethod) ParseMethod

IsAny joins a set of validator methods and returns true if ANY of them matches

type ProgressBar

type ProgressBar interface {

	// Done returns whether progress bar is done (position == size)
	Done() bool

	// Finish sets progress to max and
	Finish()

	// Increase adds given amount to progress
	Increase(amount int) error

	// Increment increases progress by one
	Increment() error

	// Position returns the progress position
	Position() int

	// Render returns the rendered progress bar in it's current position
	Render() string

	// RenderWidth returns the width of the rendered output (amount of chars (rune))
	RenderWidth() int

	// Reset sets position to zero and unsets all timers
	Reset()

	// Set moves the progress to given position
	Set(position int) error

	// SetRenderWidth sets total width of rendered output to given amount of characters (runes)
	SetRenderWidth(v int) ProgressBar

	// SetSize sets the total size of the progress bar
	SetSize(v int) ProgressBar

	// SetStyle sets the style of the progress bar
	SetStyle(style *ProgressBarStyle) ProgressBar

	// Style sets the
	Style() *ProgressBarStyle
}

type ProgressBarPool

type ProgressBarPool interface {

	// Finish sets all progress to max and stops
	Finish() chan bool

	// Has return whether a bar with name exists or not
	Has(bar string) bool

	// Init adds and initializes a new, named progress bar of given size and returns it
	Init(name string, size int) (ProgressBar, error)

	// Start initializes rendering of all registered bars
	Start()

	// Style sets style of registered progress bars
	Style(style *ProgressBarStyle) error

	// With set's output width of registered progress bars
	Width(width int) error
}

func NewProgressBarPool

func NewProgressBarPool(style ...*ProgressBarStyle) ProgressBarPool

type ProgressBarSimple

type ProgressBarSimple struct {
	// contains filtered or unexported fields
}

func NewProgressBar

func NewProgressBar(size int) *ProgressBarSimple

func (*ProgressBarSimple) Done

func (this *ProgressBarSimple) Done() bool

Done returns bool whether progress bar is done (at 100%)

func (*ProgressBarSimple) Finish

func (this *ProgressBarSimple) Finish()

Finish ends progress by skipping to the end

func (*ProgressBarSimple) Increase

func (this *ProgressBarSimple) Increase(amount int) error

Add increases progress by given amount

func (*ProgressBarSimple) Increment

func (this *ProgressBarSimple) Increment() error

Increase adds a one to progress

func (*ProgressBarSimple) Position

func (this *ProgressBarSimple) Position() int

Position returns the current position

func (*ProgressBarSimple) Render

func (this *ProgressBarSimple) Render() string

Render returns rendered progress bar in current progress position

func (*ProgressBarSimple) RenderWidth

func (this *ProgressBarSimple) RenderWidth() int

Render returns rendered progress bar in current progress position

func (*ProgressBarSimple) Reset

func (this *ProgressBarSimple) Reset()

func (*ProgressBarSimple) Set

func (this *ProgressBarSimple) Set(position int) error

Set moves progress to given position (must be between 0 and size)

func (*ProgressBarSimple) SetRenderWidth

func (this *ProgressBarSimple) SetRenderWidth(v int) ProgressBar

SetRenderWidth is builder method to set render width (defaults to PB_DEFAULT_RENDER_WIDTH)

func (*ProgressBarSimple) SetSize

func (this *ProgressBarSimple) SetSize(v int) ProgressBar

SetSize is builder method to set size (i.e. max length) of progress

func (*ProgressBarSimple) SetStyle

func (this *ProgressBarSimple) SetStyle(v *ProgressBarStyle) ProgressBar

SetStyle sets the rendering (output) style (i.e. the characters used to print the progress bar)

func (*ProgressBarSimple) Style

func (this *ProgressBarSimple) Style() *ProgressBarStyle

Style returns the currently used style

type ProgressBarStyle

type ProgressBarStyle struct {
	Empty            rune
	Progress         rune
	Rightmost        rune
	None             rune
	LeftBorder       rune
	RightBorder      rune
	Count            progressBarAddon
	Elapsed          progressBarAddon
	Estimate         progressBarAddon
	Percentage       progressBarAddon
	RenderCount      func(pos, max int, bar ProgressBar) string
	RenderElapsed    func(elapsed time.Duration, bar ProgressBar) string
	RenderEstimate   func(forecast time.Duration, bar ProgressBar) string
	RenderPercentage func(percent float32, bar ProgressBar) string
	RenderPrefix     func(count, elapsed, estimate, percentage string) string
	RenderSuffix     func(count, elapsed, estimate, percentage string) string
}

ProgressBarStyle

func CloneProgressBarStyle

func CloneProgressBarStyle(from *ProgressBarStyle) *ProgressBarStyle

type ReduceMethod

type ReduceMethod func(name string, value interface{}) bool

ReduceMethod is type of the callback of the `Reduce` and `ReduceAsync` methods

type Registry

type Registry struct {
	Container map[string]reflect.Value
}

Registry is a small container holding objects which are injected into command calls.

func NewRegistry

func NewRegistry() *Registry

NewRegistry constructs new, empty registry

func (*Registry) Alias

func (this *Registry) Alias(alias string, v interface{})

Alias registers an object under a different name. Eg for interfaces.

func (*Registry) Get

func (this *Registry) Get(s string) reflect.Value

Get returns registered object by their type name (`reflect.TypeOf(..).String()`)

func (*Registry) Has

func (this *Registry) Has(s string) bool

Has checks whether a requested type is registered

func (*Registry) Names

func (this *Registry) Names() []string

Names returns sorted (asc) list of registered names

func (*Registry) Reduce

func (this *Registry) Reduce(cb ReduceMethod) []interface{}

Reduce calls a bool-returning function on all registered objects in alphanumerical order and returns all selected as a slice

func (*Registry) ReduceAsync

func (this *Registry) ReduceAsync(cb ReduceMethod) chan interface{}

ReduceAsync calls a bool-returning function on all registered objects concurrently and writes all selected objects in a return channel

func (*Registry) Register

func (this *Registry) Register(v interface{})

Register adds new object to registry. An existing object of same type would be replaced.

type Table

type Table struct {

	// AllowEmptyFill decides whether SetRow() and SetColumn() with row indices
	// bigger than the amount of rows creates additional, empty rows in between.
	// USE WITH CAUTION!
	AllowEmptyFill bool

	// set of output headers
	Headers *TableRow

	// set of row, col, lines
	Rows []*TableRow
	// contains filtered or unexported fields
}

func NewTable

func NewTable(headers []string, style ...*TableStyle) *Table

NewTable constructs new Table with optional list of headers

func (*Table) AddRow

func (this *Table) AddRow(cols []string) error

AddRow adds another row to the table. Headers must be set beforehand.

func (*Table) AddRows

func (this *Table) AddRows(rows [][]string) error

AddRows adds multiple row to the table. Headers must be set beforehand.

func (*Table) Render

func (this *Table) Render(maxWidth ...int) string

Render prints the table into a string

func (*Table) Reset

func (this *Table) Reset()

Reset clears all (row) data of the table

func (*Table) SetColumn

func (this *Table) SetColumn(rowIdx, colIdx int, content string) error

SetColumn sets the contents of a specific column in a specific row. See `SetRow` for limitations on the row index. The column index must be within the bounds of the column amount.

func (*Table) SetHeaders

func (this *Table) SetHeaders(headers []string) error

SetHeaders sets the headers of the table. Can only be called before any data has been added.

func (*Table) SetRow

func (this *Table) SetRow(idx int, cols []string) error

SetRow sets columns in a specific row.

If `AllowEmptyFill` is true, then the row index can be arbitrary and empty columns will be automatically created, if needed. Otherwise the row index must be within the bounds of existing data or an error is returned.

func (*Table) SetStyle

func (this *Table) SetStyle(style *TableStyle)

SetStyle changes the table style

type TableCol

type TableCol struct {
	// contains filtered or unexported fields
}

func NewTableCol

func NewTableCol(row *TableRow, content string) *TableCol

NewTableCol creates a new table column object from given content

func (*TableCol) Content

func (this *TableCol) Content(maxWidth ...int) string

Content returns the string content of the column. If maxWidth is not given or 0 then the original content is returned. Otherwise the returned content is wrapped to the maxWidth limitation. Words will be split if they exceed the maxWidth value.

func (*TableCol) ContentPrefixed

func (this *TableCol) ContentPrefixed(prefix string, maxWidth ...int) string

ContentPrefixed

func (*TableCol) LineCount

func (this *TableCol) LineCount(maxWidth ...int) int

LineCount returns the amount of lines of the content. If a `maxWidth` value is provided then the returned line count is calculated based on the existing content.

Example 1:

maxWidth not given
content = "foo bar baz"
line count = 1

Example 2:

maxWidth  = 3
content = "foo bar baz"
line count = 3

func (*TableCol) Render

func (this *TableCol) Render(maxWidth int) (content string, width, lineCount int)

Render returns wrapped content, max content line width and content line count. See `LineCount()`, `Width()` and `Content()` for more informations.

func (*TableCol) SetContent

func (this *TableCol) SetContent(content string) *TableCol

func (*TableCol) SetRenderer

func (this *TableCol) SetRenderer(renderer func(string) string) *TableCol

func (*TableCol) SetRow

func (this *TableCol) SetRow(row *TableRow) *TableCol

func (*TableCol) Width

func (this *TableCol) Width(maxWidth ...int) int

type TableRow

type TableRow struct {
	// MaxLineCount is the maximum amount of
	MaxLineCount int
	ColAmount    int
	Cols         []*TableCol
	// contains filtered or unexported fields
}

func NewTableRow

func NewTableRow(cols []string) *TableRow

func (*TableRow) CalculateWidths

func (this *TableRow) CalculateWidths(totalWidth int) (colSize []int)

func (*TableRow) Render

func (this *TableRow) Render(totalWidth int) (rendered []string, maxLineCount int)

func (*TableRow) RenderWithWidths

func (this *TableRow) RenderWithWidths(colWidths []int) (rendered []string, maxLineCount int)

func (*TableRow) SetCol

func (this *TableRow) SetCol(idx int, col *TableCol) error

func (*TableRow) SetRenderer

func (this *TableRow) SetRenderer(renderer func(string) string) *TableRow

func (*TableRow) SetTable

func (this *TableRow) SetTable(table *Table) *TableRow

func (*TableRow) Width

func (this *TableRow) Width(maxWidth ...int) int

type TableStyle

type TableStyle struct {
	LeftTop         string
	Left            string
	LeftBottom      string
	Right           string
	RightTop        string
	RightBottom     string
	CrossInner      string
	CrossTop        string
	CrossBottom     string
	CrossLeft       string
	CrossRight      string
	InnerHorizontal string
	InnerVertical   string
	Top             string
	Bottom          string
	Prefix          string
	Suffix          string
	HeaderRenderer  func(content string) string
	ContentRenderer func(content string) string
}

func CopyTableStyle

func CopyTableStyle(from *TableStyle) *TableStyle

func NewDefaultTableStyle

func NewDefaultTableStyle() *TableStyle

func (*TableStyle) CalculateColWidths

func (this *TableStyle) CalculateColWidths(table *Table, totalTableWidth int) []int

CalculateColWidths returns the widths of the cols of the table, for given max width

func (*TableStyle) Render

func (this *TableStyle) Render(table *Table, mw ...int) string

Render renders given table with a given max width. Max width can not be lower than waste per row (see `Waste()`) and at least one character per column.

func (*TableStyle) Waste

func (this *TableStyle) Waste(colCount int) int

Waste returns the amount of "wasted" characters (table render characters + prefix and suffix whitespaces) for given amount of columns

type WrapTrimMode

type WrapTrimMode int

WrapTrimMode defines whether and how wrapped lines are trimmed. See WRAP_TRIM_*

const (
	// WRAP_TRIM_NONE keeps "  foo bar baz  " as is
	WRAP_TRIM_NONE WrapTrimMode = iota

	// WRAP_TRIM_RIGHT transforms "  foo bar baz  " to "foo bar baz  "
	WRAP_TRIM_RIGHT

	// WRAP_TRIM_LEFT transforms "  foo bar baz  " to "  foo bar baz"
	WRAP_TRIM_LEFT

	// WRAP_TRIM_BOTH transforms "  foo bar baz  " to "foo bar baz"
	WRAP_TRIM_BOTH
)

type WrapWhitespaceMode

type WrapWhitespaceMode int

WrapWhitespaceMode defines whether in-sentence whitespaces are reduced contracted or not. See WRAP_WHITESPACE_*

const (
	// WRAP_WHITESPACE_CONTRACT contracts "foo   bar   baz" to "foo bar baz"
	WRAP_WHITESPACE_CONTRACT WrapWhitespaceMode = iota

	// WRAP_WHITESPACE_KEEP keeps "foo   bar   baz" as is
	WRAP_WHITESPACE_KEEP
)

type Wrapper

type Wrapper struct {

	// BreakWords controls whether over-length words can be broken or not.
	// Example: Wrapping "foo barrr" with limit of 3 and NO wrapping will
	// return "foo\nbarrr" and with wrapping will return "foo\nbar\nrr"
	BreakWords bool

	// KeepEmptyLines controls whether empty lines are removed or not. Defaults
	// to false. If set to true then "foo\n\nbar" stays "foo\n\n"bar". Otherwise
	// it becomes "foo\nbar"
	KeepEmptyLines bool

	// Limit is the max length per wrapped line
	Limit uint

	// TrimMode defines whether/how wrapped lines are trimmed or not
	TrimMode WrapTrimMode

	// WhitespaceMode defines whether in-sentence whitespaces are reduced
	// or not. Eg with disabled contracting
	WhitespaceMode WrapWhitespaceMode
}

Wrapper transforms multi- or single-line strings into multi-line strings of maximum line length.

func NewWrapper

func NewWrapper(limit uint) *Wrapper

func (*Wrapper) Wrap

func (this *Wrapper) Wrap(s string) string

WrapString wraps the given string within lim width in characters. Code is partially stolen from https://raw.githubusercontent.com/mitchellh/go-wordwrap/master/wordwrap.go

Jump to

Keyboard shortcuts

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