Skip to content

Commit

Permalink
Simplify API
Browse files Browse the repository at this point in the history
  • Loading branch information
bep committed Apr 30, 2023
1 parent 2c8df91 commit 883e148
Show file tree
Hide file tree
Showing 2 changed files with 242 additions and 131 deletions.
161 changes: 89 additions & 72 deletions cobrakai.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,50 +6,93 @@ import (
"github.com/spf13/cobra"
)

// Executer is the execution entry point.
// The args are usually filled with os.Args[1:].
type Executer interface {
Execute(ctx context.Context, args []string) (*Commandeer, error)
}

// Commander is the interface that must be implemented by all commands.
type Commander interface {
// The name of the command.
Name() string

// The command execution.
Run(ctx context.Context, args []string) error

// Init called on all commands in this tree, before execution, starting from the root.
// This is the place to evaluate flags and set up the command.
Init(*Commandeer) error

// WithCobraCommand is called when the cobra command is created.
// This is where the flags, short and long description etc. are added.
WithCobraCommand(*cobra.Command) error

// Commands returns the sub commands, if any.
Commands() []Commander
}

type root struct {
c *Commandeer
// Executer is the execution entry point.
// The args are usually filled with os.Args[1:].
type Executer interface {
Execute(ctx context.Context, args []string) (*Commandeer, error)
}

func (r *root) Execute(ctx context.Context, args []string) (*Commandeer, error) {
r.c.CobraCommand.SetArgs(args)
cobraCommand, err := r.c.CobraCommand.ExecuteContextC(ctx)
if err != nil {
return nil, err
// New creates a new Executer from the command tree in Commander.
func New(rootCmd Commander) (Executer, error) {
rootCd := &Commandeer{
Command: rootCmd,
}
// Find the commandeer that was executed.
var find func(*cobra.Command, *Commandeer) *Commandeer
find = func(what *cobra.Command, in *Commandeer) *Commandeer {
if in.CobraCommand == what {
return in
rootCd.Root = rootCd

// Add all commands recursively.
var addCommands func(cd *Commandeer, cmd Commander)
addCommands = func(cd *Commandeer, cmd Commander) {
cd2 := &Commandeer{
Root: rootCd,
Parent: cd,
Command: cmd,
}
for _, in2 := range in.commandeers {
if found := find(what, in2); found != nil {
return found
}
cd.commandeers = append(cd.commandeers, cd2)
for _, c := range cmd.Commands() {
addCommands(cd2, c)
}
return nil

}
return find(cobraCommand, r.c), nil

for _, cmd := range rootCmd.Commands() {
addCommands(rootCd, cmd)
}

if err := rootCd.compile(); err != nil {
return nil, err
}

return &root{c: rootCd}, nil

}

// Commandeer holds the state of a command and its subcommands.
type Commandeer struct {
Command Commander
CobraCommand *cobra.Command
commandeers []*Commandeer

Root *Commandeer
Parent *Commandeer
commandeers []*Commandeer
}

func (c *Commandeer) init() error {
// Start from the root and initialize all commands recursively.
// root is always set.
cd := c.Root
var initc func(*Commandeer) error
initc = func(cd *Commandeer) error {
if err := cd.Command.Init(cd); err != nil {
return err
}
for _, cc := range cd.commandeers {
if err := initc(cc); err != nil {
return err
}
}
return nil
}
return initc(cd)
}

func (c *Commandeer) compile() error {
Expand All @@ -58,6 +101,9 @@ func (c *Commandeer) compile() error {
RunE: func(cmd *cobra.Command, args []string) error {
return c.Command.Run(cmd.Context(), args)
},
PreRunE: func(cmd *cobra.Command, args []string) error {
return c.init()
},
}

// This is where the flags, short and long description etc. are added
Expand All @@ -74,57 +120,28 @@ func (c *Commandeer) compile() error {
return nil
}

// WithCommandeer allows chaining of commandeers.
type WithCommandeer func(*Commandeer)
type root struct {
c *Commandeer
}

// R creates the execution entry poing given a root command and a chain of nested commands.
func R(command Commander, wcs ...WithCommandeer) (Executer, error) {
c := &Commandeer{
Command: command,
}
for _, wc := range wcs {
wc(c)
}
if err := c.compile(); err != nil {
func (r *root) Execute(ctx context.Context, args []string) (*Commandeer, error) {
r.c.CobraCommand.SetArgs(args)
cobraCommand, err := r.c.CobraCommand.ExecuteContextC(ctx)
if err != nil {
return nil, err
}
return &root{c: c}, nil
}

// C creates nested commands.
func C(command Commander, wcs ...WithCommandeer) WithCommandeer {
return func(parent *Commandeer) {
cd := &Commandeer{
Command: command,
// Find the commandeer that was executed.
var find func(*cobra.Command, *Commandeer) *Commandeer
find = func(what *cobra.Command, in *Commandeer) *Commandeer {
if in.CobraCommand == what {
return in
}
parent.commandeers = append(parent.commandeers, cd)
for _, wc := range wcs {
wc(cd)
for _, in2 := range in.commandeers {
if found := find(what, in2); found != nil {
return found
}
}
return nil
}
}

// SimpleCommand creates a simple command that does not take any flags.
func SimpleCommand(name string, run func(ctx context.Context, args []string) error) Commander {
return &simpleCommand{
name: name,
run: run,
}
}

type simpleCommand struct {
name string
run func(ctx context.Context, args []string) error
}

func (c *simpleCommand) Name() string {
return c.name
}

func (c *simpleCommand) Run(ctx context.Context, args []string) error {
return c.run(ctx, args)
}

func (c *simpleCommand) WithCobraCommand(cmd *cobra.Command) error {
return nil
return find(cobraCommand, r.c), nil
}
Loading

0 comments on commit 883e148

Please sign in to comment.