Fix command name collision on generator

When creating new commands via `cobra add`, it is not possible to add
commands with the same name to different parents. They will just
overwrite each other.

With this patch, the command's object identifier is templated
`parentCommandCmd`, the `Use` field `command` and the file name
`parent_command.go`.

Fixes #1059
Fixes #1131

Signed-off-by: Carlos Marx <me@carlosmarx.com>
This commit is contained in:
Carlos Marx 2021-07-08 11:14:40 -03:00
parent de187e874d
commit b45934648f
4 changed files with 83 additions and 12 deletions

View file

@ -46,10 +46,14 @@ Example: cobra add server -> resulting in a new cmd/server.go`,
wd, err := os.Getwd() wd, err := os.Getwd()
cobra.CheckErr(err) cobra.CheckErr(err)
commandName := validateCmdName(args[0]) commandUse := validateCmdName(args[0])
commandName, commandFileName := generateCmdFileName(commandUse, parentName)
command := &Command{ command := &Command{
CmdName: commandName, CmdName: commandName,
CmdParent: parentName, CmdUse: commandUse,
CmdFileName: commandFileName,
CmdParent: parentName,
Project: &Project{ Project: &Project{
AbsolutePath: wd, AbsolutePath: wd,
Legal: getLicense(), Legal: getLicense(),
@ -122,3 +126,39 @@ func validateCmdName(source string) string {
} }
return output return output
} }
// generateCmdFileName returns a lowerCamelCase command name and a snake_case
// file name, combining the given command name and the parent command name.
func generateCmdFileName(cmd, parent string) (string, string) {
var cmdName string
var cmdFileName string
if parent == "rootCmd" {
return cmd, cmd
}
l := len(parent)
if parent[l-3:] == "Cmd" {
parent = parent[:l-3]
}
upperCmd := string(unicode.ToUpper(rune(cmd[0]))) + cmd[1:]
lowerParent := string(unicode.ToLower(rune(parent[0]))) + parent[1:]
cmdName = lowerParent + upperCmd
l = len(cmdName)
cmdFileName = string(cmdName[0])
for i := 1; i < l; i++ {
if unicode.IsUpper(rune(cmdName[i])) {
cmdFileName += "_"
}
cmdFileName += string(unicode.ToLower(rune(cmdName[i])))
}
l = len(cmdFileName)
if cmdFileName[l-5:] == "_test" {
cmdFileName += "cmd"
}
return cmdName, cmdFileName
}

View file

@ -8,9 +8,11 @@ import (
func TestGoldenAddCmd(t *testing.T) { func TestGoldenAddCmd(t *testing.T) {
command := &Command{ command := &Command{
CmdName: "test", CmdName: "test",
CmdParent: parentName, CmdUse: "test",
Project: getProject(), CmdFileName: "test",
CmdParent: parentName,
Project: getProject(),
} }
defer os.RemoveAll(command.AbsolutePath) defer os.RemoveAll(command.AbsolutePath)
@ -48,3 +50,30 @@ func TestValidateCmdName(t *testing.T) {
} }
} }
} }
func TestGenerateCmdFileName(t *testing.T) {
testCases := []struct {
inputCmd string
inputParent string
expectedCmd string
expectedFile string
}{
{"cmdname", "parent", "parentCmdname", "parent_cmdname"},
{"cmdname", "parentCmd", "parentCmdname", "parent_cmdname"},
{"CmdName", "ParentCmd", "parentCmdName", "parent_cmd_name"},
{"cmdname", "granpaParentCmd", "granpaParentCmdname", "granpa_parent_cmdname"},
{"test", "granpaParentCmd", "granpaParentTest", "granpa_parent_testcmd"},
{"cmdname", "rootCmd", "cmdname", "cmdname"},
}
for _, testCase := range testCases {
got1, got2 := generateCmdFileName(testCase.inputCmd, testCase.inputParent)
if testCase.expectedCmd != got1 || testCase.expectedFile != got2 {
t.Errorf(
"Expected %q and %q, got %q and %q",
testCase.expectedCmd, testCase.expectedFile,
got1, got2,
)
}
}
}

View file

@ -21,8 +21,10 @@ type Project struct {
} }
type Command struct { type Command struct {
CmdName string CmdName string
CmdParent string CmdUse string
CmdFileName string
CmdParent string
*Project *Project
} }
@ -83,7 +85,7 @@ func (p *Project) createLicenseFile() error {
} }
func (c *Command) Create() error { func (c *Command) Create() error {
cmdFile, err := os.Create(fmt.Sprintf("%s/cmd/%s.go", c.AbsolutePath, c.CmdName)) cmdFile, err := os.Create(fmt.Sprintf("%s/cmd/%s.go", c.AbsolutePath, c.CmdFileName))
if err != nil { if err != nil {
return err return err
} }

View file

@ -113,9 +113,9 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// {{ .CmdName }}Cmd represents the {{ .CmdName }} command // {{ .CmdName }}Cmd represents the {{ .CmdUse }} command
var {{ .CmdName }}Cmd = &cobra.Command{ var {{ .CmdName }}Cmd = &cobra.Command{
Use: "{{ .CmdName }}", Use: "{{ .CmdUse }}",
Short: "A brief description of your command", Short: "A brief description of your command",
Long: ` + "`" + `A longer description that spans multiple lines and likely contains examples Long: ` + "`" + `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example: and usage of using your command. For example:
@ -124,7 +124,7 @@ Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files This application is a tool to generate the needed files
to quickly create a Cobra application.` + "`" + `, to quickly create a Cobra application.` + "`" + `,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
fmt.Println("{{ .CmdName }} called") fmt.Println("{{ .CmdUse }} called")
}, },
} }