cli

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jul 3, 2020 License: Apache-2.0 Imports: 11 Imported by: 0

README

KnutZuidema

Cli-X is a command line library for Go, inspired by spf13/cobra.

  • 📦 struct based API: Similar to cobra, KnutZuidema/cli features a struct based API for easy composition and discovery of available options.
  • 🚸 Subcommands: cli.Command can be nested for a git like experience.
  • 📌 Flags: Every command has it's own set of flags. POSIX compliant using spf13/pflag.
  • 👥 Aliases: Commands can have multiple names, so breaking changes can easily be mitigated.
  • 🎯 Go based completion: <TAB> completion is supported for bash, zsh and fish. But you can generate suggestions using Go code!

Getting started

Add the library to your project:

$ go get github.com/KnutZuidema/cli

Then set up your root command:

package main

import (
    "fmt"
    "github.com/KnutZuidema/cli"
)

func main() {
    // create the root command
    rootCmd := cli.Command{
        Use: "greet",
        Short: "print a message",
        Run: func(cmd *cli.Command, args []string) error {
            fmt.Println("Hello from Cli-X!")
        },
    }

    // run and check for errors
    if err := rootCmd.Execute(); err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

Subcommands

Every command may have children:

// use a func to return a Command instead of
// a global variable and `init()`
func applyCmd() *cli.Command {
    cmd := &cli.Command{
        Use: "apply",
        Short: "apply the changes"
    }

    cmd.Run = func(cmd *cli.Command, args []string) error {
        fmt.Println("applied", args[0])
    }
    
    return cmd
}

func main() {
    rootCmd := &cli.Comand{
        Use: "kubectl",
        Short: "Kubernetes management tool",
    }

    // add the child command
    rootCmd.AddChildren(
       applyCmd(),
    )

    // run and check for errors
    if err := rootCmd.Execute(); err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

Note: Do not use the init() function for setting up your commands. Create constructors as shown above!

Flags

A pflag.FlagSet can be accessed per command using *Command.Flags():

func applyCmd() *cli.Command {
    cmd := &cli.Command{
        Use: "apply",
        Short: "apply the changes"
    }

    force := cmd.Flags().BoolP("force", "f", false, "skip checks")

    cmd.Run = func(cmd *cli.Command, args []string) error {
        fmt.Println("applied", args[0])
        if *force {
            fmt.Println("The force was with us.")
        }
    }
    return cmd
}

Another pflag.FlagSet can be accessed using *Command.PersistentFlags(). Contrary to the basic flags, flags set via the persistent flag set will be passed down to the children of the command.

func applyCmd() *cli.Command {
    cmd := &cli.Command{
        Use: "apply",
        Short: "apply the changes"
    }
    force := cmd.PersistentFlags().BoolP("force", "f", false, "skip checks")
    childCmd := &cli.Command{
        Use: "now"
        Short: "do it now"
    }
    cmd.AddCommand(childCmd)
    childCmd.Run = func(cmd *cli.Command, args []string) error {
        fmt.Println("applied now", args[0])
        if *force {
            fmt.Println("The force was with us.")
        }
    }
}

Aliases

To make the apply subcommand also available as make and do:

func applyCmd() *cli.Command {
    cmd := &cli.Command{
        Use: "apply",
        Aliases: []string{"make", "do"},
        Short: "apply the changes"
    }
}

Keep in mind that in --help outputs the command will still be called apply.

Completion

Cli-X has a very powerful cli completion system, based on posener/complete.

It is powerful, because suggestions come directly from the Go application, not from a bash script or similar.

Command and Flag names are automatically suggested, custom suggestions can be implemented for positional arguments and flag values:

Flag Values

Custom suggestions for flag values can be set up using Command.Predictors.

To do so, you need to add a complete.Predictor for that flag. Cli-X has a number of predefined ones:

  • PredictAny(): Predicts available files and directories, like bash does when having no better idea
  • PredictNone(): Predicts literally nothing. No suggestions will be shown.
  • PredictSet(...string): Suggests from a predefined slice of options

If that's not sufficient, use PredictFunc(func(complete.Args) []string) to create your own.

import (
    "github.com/posener/complete"
    "github.com/KnutZuidema/cli"
)

func logsCmd() *cli.Command {
    cmd := &cli.Command{
        Use: "logs",
        Short: "show logs of a pod",
        Predictors: map[string]complete.Predictor{
            "output": cli.PredictSet("lines", "json", "logfmt"),
        },
    }

    output := cmd.Flags().StringP("output", "o", "lines", "one of [lines, json, logfmt]")
}
Positional Arguments

For positional arguments, Cli-X uses a slightly different interface, to allow validation as well:

interface {
    Validate(args []string) error
    Predict(Args) []string
}

Predefined options are also available:

  • ArgsAny(): accepts any number args, predicts files and directories
  • ArgsExact(n): accepts exactly n arguments. Predicts files and directories.
  • ArgsRange(n, m): accepts between n and m arguments (inclusive). Predicts files and directories.
  • ArgsNone(): accepts no args and predicts nothing.
  • ArgsSet(...string): Accepts one argument, which MUST be included in the given set. Predicts the values from the set.
import (
    "github.com/posener/complete"
    "github.com/KnutZuidema/cli"
)

func applyCmd() *cli.Command {
    cmd := &cli.Command{
        Use: "logs",
        Short: "show logs of a pod",
        // we expect one argument which can be anything
        Args: cli.ArgsExact(1),
    }
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func PredictAny

func PredictAny() complete.Predictor

PredictAny predicts any files/directories

func PredictNone

func PredictNone() complete.Predictor

PredictNone predicts exactly nothing

func PredictSet

func PredictSet(set ...string) complete.Predictor

PredictSet predicts the values from the given set

Types

type Args

type Args struct {
	Validator
	complete.Predictor
}

Args bundles user-supplied implementations of the respective interfaces into an Arguments implementation.

type Arguments

type Arguments interface {
	Validator
	complete.Predictor
}

Arguments is used to validate and complete positional arguments. Use `Args()` to create an instance from functions.

func ArgsAny

func ArgsAny() Arguments

ArgsAny allows any number of arguments with any value

func ArgsExact

func ArgsExact(n int) Arguments

ArgsExact checks for exactly n arguments, predicting anything

func ArgsNone

func ArgsNone() Arguments

ArgsNone checks that no arguments were given, and disables predictions.

func ArgsRange

func ArgsRange(n, m int) Arguments

ArgsRange checks for between n and m arguments (inclusive), predicting anything

func ArgsSet

func ArgsSet(set ...string) Arguments

ArgsSet check the given argument is in the predefined set of options. Only these options are predicted. Only a single argument is assumed.

type Command

type Command struct {
	// Usage line. First word must be the command name, everything else is
	// displayed as-is.
	Use string
	// Aliases define alternative names for a command
	Aliases []string

	// Short help text, used for overviews
	Short string
	// Long help text, used for full help pages. `Short` is used as a fallback
	// if unset.
	Long string

	// Version of the application. Only used on the root command
	Version string

	// Run is the action that is run when this command is invoked.
	// The error is returned as-is from `Execute()`.
	Run func(cmd *Command, args []string) error

	// Validation + Completion
	//
	// Predict contains Predictors for flags. Defaults to
	// `complete.PredictSomething` if unset.
	// Use the flags name (not shorthand) as the key.
	Predictors map[string]complete.Predictor
	// Args is used to validate and complete positional arguments
	Args Arguments
	// contains filtered or unexported fields
}

Command represents a (sub)command of the application. Either `Run()` must be defined, or subcommands added using `AddCommand()`. These are also mutually exclusive.

func (*Command) AddCommand

func (c *Command) AddCommand(children ...*Command)

AddCommand adds the supplied commands as subcommands. Persistent flags are passed down to the child. This command is set as the parent of the new children.

func (*Command) Execute

func (c *Command) Execute() error

Execute runs the application. It should be run on the most outer level command. The error return value is used for both, application errors but also help texts.

func (*Command) Flags

func (c *Command) Flags() *pflag.FlagSet

Flags returns the `*pflag.FlagSet` of this command

func (*Command) Name

func (c *Command) Name() string

Name of this command. The first segment of the `Use` field.

func (*Command) PersistentFlags

func (c *Command) PersistentFlags() *pflag.FlagSet

PersistentFlags returns the `*pflag.FlagSet` with the persistent flags of this command. Persistent flags are passed to subcommands.

func (*Command) Usage

func (c *Command) Usage() string

Usage string

type ErrHelp

type ErrHelp struct {
	Message string
	// contains filtered or unexported fields
}

ErrHelp wraps an actual error, showing the usage of the command afterwards

func (ErrHelp) Error

func (e ErrHelp) Error() string

type PredictFunc

type PredictFunc = complete.PredictFunc

PredictFunc allows to use an ordinary func as an Predictor

type ValidateFunc

type ValidateFunc func(args []string) error

ValidateFunc allows to use an ordinary func as an Validator

func ValidateAny

func ValidateAny() ValidateFunc

ValidateAny always approves

func ValidateExact

func ValidateExact(n int) ValidateFunc

ValidateExact checks that exactly n arguments were given

func ValidateNone

func ValidateNone() ValidateFunc

ValidateNone checks for no arguments at all

func ValidateRange

func ValidateRange(n, m int) ValidateFunc

ValidateRange checks that between n and m arguments (inclusive) were given

func ValidateSet

func ValidateSet(set ...string) ValidateFunc

ValidateSet checks that the given single argument is part of the set.

func (ValidateFunc) Validate

func (v ValidateFunc) Validate(args []string) error

Validate wrap the underlying function

type Validator

type Validator interface {
	// Validate receives the arguments of the command (without flags) and shall
	// return an error if they are unexpected.
	Validate(args []string) error
}

Validator checks that arguments have the expected form

Directories

Path Synopsis
_examples

Jump to

Keyboard shortcuts

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