Skip to content

Commit

Permalink
feat: add support for functional commands (#12)
Browse files Browse the repository at this point in the history
* feat: add support for functional commands

* update readme

* remove unused line
  • Loading branch information
LucasCarioca authored Nov 7, 2021
1 parent 781495c commit b2cd8fd
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 6 deletions.
31 changes: 29 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func main() {
you can now validate that your cli is working

```shell
go build new-app-func.go -o mycli
go build main.go -o mycli
mycli hello
# You should then get the output "Hello World"
```
Expand All @@ -80,7 +80,34 @@ func main() {
Let's test it again

```shell
go build new-app-func.go -o mycli
go build main.go -o mycli
mycli
# You should then get the output "Hello World"
```

You can also create commands even easier by creating `FunctionalCommands`.

```go
import "github.com/LucasCarioca/gocli/cli"

func main() {
app := &cli.NewApp(func() error {
fmt.Println("this is a simple functional command")
})

app.AddCommand("another", func() error {
fmt.Println("this is another simple functional command")
})
app.Run()
}
```

Let's test it again

```shell
go build main.go -o mycli
mycli
# You should then get the output "this is a simple functional command"
mycli another
# You should then get the output "this is another simple functional command"
```
14 changes: 10 additions & 4 deletions cli/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import (

//AppInterface basic interface for GoCLI cli applications
type AppInterface interface {
AddCommand(name string, command Command)
AddCommand(name string, command interface{})
Run() error
}

//NewApp creates a cli app with a provided default command
func NewApp(defaultCommand Command) AppInterface {
func NewApp(defaultCommand interface{}) AppInterface {
app := &App{
commands: map[string]Command{
"version": &VersionCommand{},
Expand All @@ -28,11 +28,17 @@ type App struct {
}

//AddCommand adds a command to the cli with a given command name
func (a *App) AddCommand(name string, command Command) {
//can handle both regular commands (Command type) or functional commands (FunctionalCommand type)
func (a *App) AddCommand(name string, command interface{}) {
if a.commands == nil {
a.commands = make(map[string]Command)
}
a.commands[name] = command
switch c := command.(type) {
case Command:
a.commands[name] = c
case func() error:
a.commands[name] = &FunctionalCommandWrapper{c}
}
}

//Run executes any command associated with the first argument passed to the application (after the application name itself)
Expand Down
21 changes: 21 additions & 0 deletions cli/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,25 @@ func Test_app(t *testing.T) {
assert.Equalf(t, 0, cmd.getCalls(), "Should not have executed default command")
assert.Equalf(t, 1, newCmd.getCalls(), "Should have executed test command")
})

t.Run("Should support functional commands", func(t *testing.T) {
functionalCommand := func() error {
cmd.Run()
return nil
}
app := NewApp(functionalCommand)
app.AddCommand("test", functionalCommand)
cmd.resetCalls()
cmd.setError(nil)
assert.NotNil(t, app, "Should return a proper app")
assert.Equalf(t, 0, cmd.getCalls(), "Should not have executed the command")
MockCLICall("app")
app.Run()
assert.Equalf(t, 1, cmd.getCalls(), "Should have executed the command")
cmd.resetCalls()
assert.Equalf(t, 0, cmd.getCalls(), "Should not have executed the command")
MockCLICall("app test")
app.Run()
assert.Equalf(t, 1, cmd.getCalls(), "Should have executed the command")
})
}
14 changes: 14 additions & 0 deletions cli/functional-command-wrapper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package cli

//FunctionalCommand is any simple function that returns an optional error
type FunctionalCommand func() error

//FunctionalCommandWrapper an adapter to support running functional commands
type FunctionalCommandWrapper struct {
Command FunctionalCommand
}

//Run executes the wrapped functional command
func (c *FunctionalCommandWrapper) Run() error {
return c.Command()
}
26 changes: 26 additions & 0 deletions demos/functional-command/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package main

import (
"fmt"
"github.com/LucasCarioca/gocli/cli"
)

func functionalCommand() error {
fmt.Println("This is the default functional command")
return nil
}

func functionalCommand2() error {
fmt.Println("This is another functional command")
return nil
}

func main() {
app := cli.NewApp(functionalCommand)
app.AddCommand("hello", functionalCommand2)
app.AddCommand("inline", func() error {
fmt.Println("This command is created inline")
return nil
})
app.Run()
}

0 comments on commit b2cd8fd

Please sign in to comment.