The next release will be the version 2.0.0. I want to push the development of the subcommands feature due to the most significant feedback I've received:
Docli should allow nested commands, what's the point otherwise?
Quite frankly, I agree! Docli can't compete with other Go libraries at the same level because of this significant disadvantage.
The current API 👎
Docli was designed to allow embedded structs to provide a way to group related arguments and create abstractions like commands
and flags
:
package main
import (
"log"
"github.com/celicoo/docli"
)
var doc = `
Usage: terraform [--version] [--help] <command> [args]
...
Common commands:
apply Builds or changes infrastructure
console Interactive console for Terraform interpolations
`
type Terraform struct {
CommonCommands
}
type CommonCommands struct {
Apply,
Console bool
}
func main() {
args, err := docli.Parse(doc)
if err != nil {
log.Fatal(err)
}
var terraform Terraform
args.Bind(&terraform)
if terraform.CommonCommands.Apply {
// ...
} else if terraform.CommonCommands.Console {
// ...
}
}
The problems with this approach are obvious now:
- The program's complexity grows proportionally to the number of arguments, making it harder to test, maintain and even understand it.
- Misplaced responsibility - validation and parsing errors return on the same call
- Weird API -
docli.Parse(doc string) (Args, error)
The new API 👍
The new API aims to solve the problems pointed above, add the subcommands
feature.
1st change: the function Parse(doc string) (Args, error)
is going to be replaced with the function Args() Args
:
var c MyCommand
args := docli.Args()
args.Bind(&c)
The second returning value (error) is going to be removed. See 2nd and 3rd changes to understand the reason.
2nd change: the struct passed to Bind
will need to implement the Command
interface:
type command interface {
// Doc returns the docstring of a command.
Doc() string
// Run is called when the fields of a command are bound with matching
// command-line arguments.
Run()
// Help is called when a user runs "<command> help".
Help()
// Error is called when unregistered command-line arguments are passed.
Error(error)
}
3rd change: parsing errors will panic.
But wait, what about the subcommands feature?
The concrete command
type can have fields that are themselves concrete Commands
. You can check a simple example here if you didn't get it.
Feedback
Please don't hesitate to comment on this Issue. I will love to know your thoughts about the changes that are coming.
feedback:discussion feedback:rfc