diff --git a/cobra/cmd/add.go b/cobra/cmd/add.go index b89d4c47..88257758 100644 --- a/cobra/cmd/add.go +++ b/cobra/cmd/add.go @@ -14,6 +14,7 @@ package cmd import ( + "path" "fmt" "path/filepath" "strings" @@ -48,7 +49,17 @@ Example: cobra add server -> resulting in a new cmd/server.go er("add needs a name for the command") } guessProjectPath() - createCmdFile(args[0]) + + // Iterate over command parts delimited by "/". + cmdParts := strings.Split(args[0], "/") + for i := len(cmdParts) - 1; i >= 0; i-- { + if i > 0 { + pName = cmdParts[i-1] + } else { + pName = "RootCmd" + } + createCmdFile(cmdParts[i], buildPath(cmdParts, i - 1)) + } }, } @@ -64,22 +75,24 @@ func parentName() string { return pName } -func createCmdFile(cmdName string) { +func createCmdFile(cmdName, cmdPath string) { lic := getLicense() template := `{{ comment .copyright }} {{ comment .license }} -package cmd +package {{ .packageName }} import ( "fmt" - + {{ if .importpath }} + "{{ .importpath }}" + {{ end -}} "github.com/spf13/cobra" ) -// {{.cmdName}}Cmd represents the {{.cmdName}} command -var {{ .cmdName }}Cmd = &cobra.Command{ +// {{ .cmdName | title }}Cmd represents the {{ .cmdName }} command +var {{ .cmdName | title }}Cmd = &cobra.Command{ Use: "{{ .cmdName }}", Short: "A brief description of your command", Long: ` + "`" + `A longer description that spans multiple lines and likely contains examples @@ -95,23 +108,34 @@ to quickly create a Cobra application.` + "`" + `, } func init() { - {{ .parentName }}.AddCommand({{ .cmdName }}Cmd) + {{ if eq .parentName "RootCmd" -}} + {{ .parentName }}.AddCommand({{ .cmdName | title }}Cmd) + {{ end -}} + {{ $cmdName := .cmdName -}} + {{ range $child := .children -}} + {{ $cmdName | title }}Cmd.AddCommand({{ $cmdName }}.{{ $child | title }}Cmd) + {{ end -}} // Here you will define your flags and configuration settings. // Cobra supports Persistent Flags which will work for this command // and all subcommands, e.g.: - // {{.cmdName}}Cmd.PersistentFlags().String("foo", "", "A help for foo") + // {{ .cmdName | title }}Cmd.PersistentFlags().String("foo", "", "A help for foo") // Cobra supports local flags which will only run when this command // is called directly, e.g.: - // {{.cmdName}}Cmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") - + // {{ .cmdName | title }}Cmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") } ` - var data map[string]interface{} - data = make(map[string]interface{}) + data := make(map[string]interface{}) + + data["packageName"] = "cmd" + + // Determine the package name based on the command file path. + if cmdPath != "" { + data["packageName"] = path.Base(cmdPath) + } data["copyright"] = copyrightLine() data["license"] = lic.Header @@ -120,9 +144,27 @@ func init() { data["parentName"] = parentName() data["cmdName"] = cmdName - err := writeTemplateToFile(filepath.Join(ProjectPath(), guessCmdDir()), cmdName+".go", template, data) - if err != nil { - er(err) + path := filepath.Join(ProjectPath(), guessCmdDir(), cmdPath) + filename := cmdName + ".go" + + children := getChildNames(path, cmdName) + if len(children) > 0 { + data["importpath"] = filepath.Join( + guessImportPath(), + guessCmdDir(), + cmdPath, + cmdName, + ) + } + data["children"] = children + + // Check if the file exists before trying to create it. + if doesExist, err := exists(filepath.Join(path, filename)); !doesExist { + if err = writeTemplateToFile(path, filename, template, data); err != nil { + er(err) + } + fmt.Println(cmdName, "created at", path) + } else { + fmt.Println(cmdName, "already exists at", path) } - fmt.Println(cmdName, "created at", filepath.Join(ProjectPath(), guessCmdDir(), cmdName+".go")) } diff --git a/cobra/cmd/helpers.go b/cobra/cmd/helpers.go index 6989bd78..75c1426a 100644 --- a/cobra/cmd/helpers.go +++ b/cobra/cmd/helpers.go @@ -221,21 +221,15 @@ func dirExists(path string) (bool, error) { return false, err } -func writeTemplateToFile(path string, file string, template string, data interface{}) error { +func writeTemplateToFile(path, file, template string, data interface{}) error { filename := filepath.Join(path, file) r, err := templateToReader(template, data) - if err != nil { return err } - err = safeWriteToDisk(filename, r) - - if err != nil { - return err - } - return nil + return safeWriteToDisk(filename, r) } func writeStringToFile(path, file, text string) error { @@ -251,7 +245,10 @@ func writeStringToFile(path, file, text string) error { } func templateToReader(tpl string, data interface{}) (io.Reader, error) { - tmpl := template.New("") + tmpl := template.New("").Funcs(template.FuncMap{ + "title": strings.Title, + "eq": func(a, b interface{}) bool { return a == b }, + }) tmpl.Funcs(funcMap) tmpl, err := tmpl.Parse(tpl) @@ -310,3 +307,25 @@ func commentifyString(in string) string { } return strings.Join(newlines, "\n") } + +func buildPath(parts []string, endIndex int) string { + if endIndex > -1 { + return filepath.Join(buildPath(parts, endIndex - 1), parts[endIndex]) + } + return "" +} + +func getChildNames(path, name string) []string { + childNames := []string{} + fileNames := []string{} + if file, err := os.Open(filepath.Join(path, name)); err == nil { + fileNames, err = file.Readdirnames(0) + } + for _, name := range fileNames { + // Only consider files with the .go extension to be child commands. + if ext := filepath.Ext(name); ext == ".go" { + childNames = append(childNames, strings.TrimSuffix(name, ext)) + } + } + return childNames +} diff --git a/cobra/cmd/init.go b/cobra/cmd/init.go index b77c9864..f969efb3 100644 --- a/cobra/cmd/init.go +++ b/cobra/cmd/init.go @@ -233,8 +233,8 @@ func initConfig() { data["viper"] = viper.GetBool("useViper") - err := writeTemplateToFile(ProjectPath()+string(os.PathSeparator)+guessCmdDir(), "root.go", template, data) - if err != nil { + path := ProjectPath() + string(os.PathSeparator) + guessCmdDir() + if err := writeTemplateToFile(path, "root.go", template, data); err != nil { er(err) }