Skip to content
This repository was archived by the owner on Oct 17, 2021. It is now read-only.

Support subcommand aliases #12

Merged
merged 1 commit into from
Jul 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ func (c *subcmd) Spec() cli.CommandSpec {
return cli.CommandSpec{
Name: "sub",
Usage: "",
Aliases: []string{"s"},
Desc: `This is a simple subcommand.`,
}
}
Expand Down Expand Up @@ -134,5 +135,5 @@ Usage: subcommand [flags]
This is a simple example of subcommands.

Commands:
sub This is a simple subcommand.
s,sub - This is a simple subcommand.
```
8 changes: 7 additions & 1 deletion command.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,23 @@ type CommandSpec struct {
// RawArgs indicates that flags should not be parsed, and they should be deferred
// to the command.
RawArgs bool

// Hidden indicates that this command should not show up in it's parent's
// subcommand help.
Hidden bool
// Aliases contains a list of alternative names that can be used for a particular command.
Aliases []string
}

// ShortDesc returns the first line of Desc.
func (c CommandSpec) ShortDesc() string {
return strings.Split(c.Desc, "\n")[0]
}

// HasAliases evaluates whether particular command has any alternative names.
func (c CommandSpec) HasAliases() bool {
return len(c.Aliases) > 0
}

// Command describes a command or subcommand.
type Command interface {
// Spec returns metadata about the command.
Expand Down
105 changes: 105 additions & 0 deletions command_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package cli

import (
"bytes"
"os"
"testing"

"cdr.dev/slog/sloggers/slogtest/assert"
"github.com/spf13/pflag"
)

const (
success = "test successful"

expectedParentCmdHelpOutput = `Usage: mockParentCmd Mock parent command usage.

Mock parent command description.

Commands:
s,sc,sub,mockSubCmd - A simple mock subcommand with aliases.
`
)

var subCmd = &mockSubCmd{buf: new(bytes.Buffer)}

type (
mockParentCmd struct{}
mockSubCmd struct{ buf *bytes.Buffer }
)

func (c *mockParentCmd) Run(fl *pflag.FlagSet) {}

func (c *mockParentCmd) Subcommands() []Command { return []Command{subCmd} }

func (c *mockParentCmd) Spec() CommandSpec {
return CommandSpec{
Name: "mockParentCmd",
Usage: "Mock parent command usage.",
Desc: "Mock parent command description.",
}
}

func (c *mockSubCmd) Run(fl *pflag.FlagSet) {
c.buf = new(bytes.Buffer)
if _, err := c.Write([]byte(success)); err != nil {
fl.Usage()
}
}

func (c *mockSubCmd) Write(b []byte) (int, error) { return c.buf.Write(b) }

func (c *mockSubCmd) Spec() CommandSpec {
return CommandSpec{
Name: "mockSubCmd",
Usage: "Test a subcommand.",
Aliases: []string{"s", "sc", "sub"},
Desc: "A simple mock subcommand with aliases.",
}
}

func TestSubCmdAliases(t *testing.T) {
for _, test := range []struct {
name, expected string
}{
{
name: "s",
expected: success,
},
{
name: "sc",
expected: success,
},
{
name: "sub",
expected: success,
},
} {
t.Run(test.name, func(t *testing.T) {
// Since the alias is the name of the test
// we can just pass it as the alias arg.
os.Args = []string{"mockParentCmd", test.name}
// Based on os.Args, when splitArgs is invoked,
// it should be able to deduce the subcommand we want
// based on the new alias map it's being passed.
RunRoot(&mockParentCmd{})
// The success const is never written into the buffer
// if the subcommand fails to be invoked by alias.
got := string(subCmd.buf.Bytes())
assert.Equal(t, test.name, test.expected, got)
})
}
}

func TestSubcmdAliasesInParentCmdHelpOutput(t *testing.T) {
buf := new(bytes.Buffer)
cmd := &mockParentCmd{}
name := cmd.Spec().Name
fl := pflag.NewFlagSet(name, pflag.ExitOnError)
// If the help output is not written to the buffer
// in the format we expect then the test will fail.
renderHelp(name, cmd, fl, buf)
got := string(buf.Bytes())
expected := expectedParentCmdHelpOutput
assert.Equal(t, "display_subcmd_aliases_in_parentcmd_help_output", expected, got)
}
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@ module go.coder.com/cli

go 1.12

require github.com/spf13/pflag v1.0.3
require (
cdr.dev/slog v1.3.0
github.com/spf13/pflag v1.0.3
github.com/stretchr/testify v1.6.1 // indirect
)
Loading