Skip to content
/ climate Public

"CLI Mate" autogenerates CLIs from structs / functions (nested subcommands, global / local flags, help generation, typo suggestions, shell completion etc.)

License

Notifications You must be signed in to change notification settings

avamsi/climate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Climate

Climate "CLI Mate" aims to make creating CLIs in Go easy (and fun!), similar to python-fire.
It's also built on top of Cobra and so comes with "batteries included" (help, shell completion etc.).

Usage

// Exported struct fields are automatically declared as flags --
// 1. Field names are converted to kebab-case and are used as flag names.
// That said, users can pass flags in camelCase, PascalCase, snake_case or
// SCREAMING_SNAKE_CASE and everything just works (thanks to normalization).
// 2. Field types are used as flag types (string, bool, int, etc.).
// 3. "short" subfield tags (under the "cli" tags) are used as short flag names
// (as is). It's also possible to omit the value, in which case the first
// letter of the field name is used.
// 4. "default" field tags are used as default values (of course, with
// automatic type conversion from raw string to the actual field type).
// 5. Field docs / comments are used* as flag usage strings (as is).
// 6. "required" subfield tags (under the "cli" tags) are used to mark the
// flags as required (i.e., the command is errored out without these flags).
type greetOptions struct {
Greeting string `cli:"short" default:"Hello"` // greeting to use
Name string `cli:"short=n" default:"World"` // name to greet
Times int `cli:"short,required"` // number of times to greet
}
// Func is automatically converted to a command --
// 1. Param names are converted to kebab-case and used* as part of the usage
// string ("command [opts] [args]", for example).
// 2. (Optional) First argument if a struct pointer, is used to declare flags.
// 3. (Optional) Next argument if a string slice is used to collect args.
// 4. Doc is used* as long help string (as is).
// 5. Usage directive is used* to explicitly set the usage string.
// Greet someone.
func greet(opts *greetOptions) {
for i := 0; i < opts.Times; i++ {
fmt.Printf("%v, %v!\n", opts.Greeting, opts.Name)
}
}
// * These only work if you generate and pass along "metadata" like below --
//go:generate go run github.com/avamsi/climate/cmd/cligen --out=md.cli
//go:embed md.cli
var md []byte
func main() {
climate.RunAndExit(climate.Func(greet), climate.WithMetadata(md))
}

$ greet --help

Greet someone.

Usage:
  greet [opts]

Flags:
  -g, --greeting string (default Hello)  greeting to use
  -n, --name     string (default World)  name to greet
  -t, --times    int                     number of times to greet
  -h, --help                             help for greet
  -v, --version                          version for greet

Subcommands

// See ../greet/main.go first for some details that are not covered here.
// Struct is automatically converted to a command --
// 1. Struct names are converted to lowercase and used as the command name.
// 2. Struct fields are automatically declared as "global" flags.
// 3. Struct methods are automatically converted to subcommands --
// 1. Method names are converted to lowercase and used as the command name.
// 2. Method docs are truncated and are used* as short help strings.
// 3. Method directives are used* to declare aliases or explicitly set the
// short help strings (//cli:aliases, for example).
// 4. "Sub-structs" are automatically converted to subcommands, recursively.
// Jujutsu (an experimental VCS).
type jj struct {
Repository string `cli:"short=R"` // `path` to the repo to operate on
IgnoreWorkingCopy bool // don't snapshot / update the working copy
}
// Create a new repo in the given directory.
func (j *jj) Init(ctx context.Context, dir *string) {
fmt.Println("init", ctx, j, dir)
}
type squashOptions struct {
Revision string `cli:"short" default:"@"`
Interactive bool `cli:"short"` // interactively choose which parts to squash
}
// Move changes from a revision into its parent.
//
// After moving the changes into the parent, the child revision will have the
// same content state as before. If that means that the change is now empty
// compared to its parent, it will be abandoned. Without `--interactive`, the
// child change will always be empty.
//
//cli:aliases am, amend
func (j *jj) Squash(opts *squashOptions, paths [5]string) {
fmt.Println("squash", j, opts, paths)
}
// Commands for working with the underlying Git repo.
type git struct {
J *jj
}
// Manage Git remotes.
func (g *git) Remote() error {
return errors.New("not implemented")
}
// Update the underlying Git repo with changes made in the repo.
func (g *git) Export() {
fmt.Println("export", g.J)
}
//go:generate go run github.com/avamsi/climate/cmd/cligen --out=md.cli
//go:embed md.cli
var md []byte
func main() {
// Note the recursive struct embedding below, which lets us create "deep"
// subcommands like this (indentation implies subcommand) --
//
// jj
// init
// squash
// git
// remote
// export
// util
// completion
p := climate.Struct[jj](climate.Struct[git](), climate.Struct[util.Util]())
climate.RunAndExit(p, climate.WithMetadata(md))
}

$ jj --help

Jujutsu (an experimental VCS).

Usage:
  jj [command]

Available Commands:
  completion  Generate the autocompletion script for the specified shell
  git         Commands for working with the underlying Git repo
  help        Help about any command
  init        Create a new repo in the given directory
  squash      Move changes from a revision into its parent
  util        Infrequently used commands such as for generating shell completions
  version     Display jj's version information

Flags:
  -R, --repository          path  path to the repo to operate on
      --ignore-working-copy       don't snapshot / update the working copy
  -h, --help                      help for jj
  -v, --version                   version for jj

Use "jj [command] --help" for more information about a command.
$ jj git --help

Commands for working with the underlying Git repo.

Usage:
  jj git [command]

Available Commands:
  export      Update the underlying Git repo with changes made in the repo
  remote      Manage Git remotes

Flags:
  -h, --help  help for git

Global Flags:
      --ignore-working-copy       don't snapshot / update the working copy
  -R, --repository          path  path to the repo to operate on

Use "jj git [command] --help" for more information about a command.

About

"CLI Mate" autogenerates CLIs from structs / functions (nested subcommands, global / local flags, help generation, typo suggestions, shell completion etc.)

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages