diff --git a/cmd/create.go b/cmd/create.go index f9f66aa2..26dcfb07 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -8,6 +8,7 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" + "github.com/melkeydev/go-blueprint/cmd/frameworks" "github.com/melkeydev/go-blueprint/cmd/program" "github.com/melkeydev/go-blueprint/cmd/steps" "github.com/melkeydev/go-blueprint/cmd/ui/multiInput" @@ -30,17 +31,22 @@ const logo = ` ` var ( - logoStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#01FAC6")).Bold(true) - tipMsgStyle = lipgloss.NewStyle().PaddingLeft(1).Foreground(lipgloss.Color("190")).Italic(true) - endingMsgStyle = lipgloss.NewStyle().PaddingLeft(1).Foreground(lipgloss.Color("170")).Bold(true) - allowedProjectTypes = []string{"chi", "gin", "fiber", "gorilla/mux", "httprouter", "standard-library", "echo"} + logoStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#01FAC6")).Bold(true) + tipMsgStyle = lipgloss.NewStyle().PaddingLeft(1).Foreground(lipgloss.Color("190")).Italic(true) + endingMsgStyle = lipgloss.NewStyle().PaddingLeft(1).Foreground(lipgloss.Color("170")).Bold(true) ) func init() { + var frameworkFlag frameworks.Framework + rootCmd.AddCommand(createCmd) createCmd.Flags().StringP("name", "n", "", "Name of project to create") - createCmd.Flags().StringP("framework", "f", "", fmt.Sprintf("Framework to use. Allowed values: %s", strings.Join(allowedProjectTypes, ", "))) + createCmd.Flags().VarP(&frameworkFlag, "framework", "f", fmt.Sprintf("Framework to use. Allowed values: %s", strings.Join(frameworks.AllowedProjectTypes, ", "))) + + if err := createCmd.RegisterFlagCompletionFunc("framework", frameworks.FrameworkCompletion); err != nil { + log.Fatal(err) + } } // createCmd defines the "create" command for the CLI @@ -59,19 +65,15 @@ var createCmd = &cobra.Command{ isInteractive := !utils.HasChangedFlag(cmd.Flags()) flagName := cmd.Flag("name").Value.String() - flagFramework := cmd.Flag("framework").Value.String() - if flagFramework != "" { - isValid := isValidProjectType(flagFramework, allowedProjectTypes) - if !isValid { - cobra.CheckErr(fmt.Errorf("Project type '%s' is not valid. Valid types are: %s", flagFramework, strings.Join(allowedProjectTypes, ", "))) - } - } + // VarP always returns a valid option, if the option is not + // valid the program errors out + flagFramework := frameworks.Framework(cmd.Flag("framework").Value.String()) project := &program.Project{ - FrameworkMap: make(map[string]program.Framework), + FrameworkMap: make(map[frameworks.Framework]program.Framework), ProjectName: flagName, - ProjectType: strings.ReplaceAll(flagFramework, "-", " "), + ProjectType: flagFramework, } steps := steps.InitSteps(&options) @@ -86,7 +88,7 @@ var createCmd = &cobra.Command{ project.ExitCLI(tprogram) project.ProjectName = options.ProjectName.Output - cmd.Flag("name").Value.Set(project.ProjectName) + _ = cmd.Flag("name").Value.Set(project.ProjectName) } if project.ProjectType == "" { @@ -101,8 +103,8 @@ var createCmd = &cobra.Command{ *step.Field = s.Choice } - project.ProjectType = strings.ToLower(options.ProjectType) - cmd.Flag("framework").Value.Set(project.ProjectType) + project.ProjectType = options.ProjectType + _ = cmd.Flag("framework").Value.Set(project.ProjectType.String()) } currentWorkingDir, err := os.Getwd() @@ -130,14 +132,3 @@ var createCmd = &cobra.Command{ } }, } - -// isValidProjectType checks if the inputted project type matches -// the currently supported list of project types -func isValidProjectType(input string, allowedTypes []string) bool { - for _, t := range allowedTypes { - if input == t { - return true - } - } - return false -} diff --git a/cmd/frameworks/frameworkFlag.go b/cmd/frameworks/frameworkFlag.go new file mode 100644 index 00000000..71132c32 --- /dev/null +++ b/cmd/frameworks/frameworkFlag.go @@ -0,0 +1,56 @@ +package frameworks + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" +) + +type Framework string + +// These are all the current frameworks supported, if you want to add one you +// can simply copy and past a line here. Do not forget to also add it into the +// AllowedProjectTypes slice too! +const ( + Chi Framework = "chi" + Gin Framework = "gin" + Fiber Framework = "fiber" + GorillaMux Framework = "gorilla/mux" + HttpRouter Framework = "httprouter" + StandardLibrary Framework = "standard-library" + Echo Framework = "echo" +) + +var AllowedProjectTypes = []string{string(Chi), string(Gin), string(Fiber), string(GorillaMux), string(HttpRouter), string(StandardLibrary), string(Echo)} + +// This interface is required on a type to make it useable as a flag +// +// type Value interface { +// String() string +// Set(string) error +// Type() string +// } +func (f Framework) String() string { + return string(f) +} + +func (f *Framework) Type() string { + return "Framework" +} + +func (f *Framework) Set(value string) error { + switch value { + case Chi.String(), Gin.String(), Fiber.String(), GorillaMux.String(), HttpRouter.String(), StandardLibrary.String(), Echo.String(): + *f = Framework(value) + return nil + default: + return fmt.Errorf("Framework to use. Allowed values: %s", strings.Join(AllowedProjectTypes, ", ")) + } +} + +// This function returns the options that are shown to the user on shell +// completions for the -f or --framework flag +func FrameworkCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return AllowedProjectTypes, cobra.ShellCompDirectiveDefault +} diff --git a/cmd/program/program.go b/cmd/program/program.go index 414caded..3698a26d 100644 --- a/cmd/program/program.go +++ b/cmd/program/program.go @@ -8,7 +8,9 @@ import ( "log" "os" "strings" + tea "github.com/charmbracelet/bubbletea" + "github.com/melkeydev/go-blueprint/cmd/frameworks" tpl "github.com/melkeydev/go-blueprint/cmd/template" "github.com/melkeydev/go-blueprint/cmd/utils" "github.com/spf13/cobra" @@ -20,8 +22,8 @@ type Project struct { ProjectName string Exit bool AbsolutePath string - ProjectType string - FrameworkMap map[string]Framework + ProjectType frameworks.Framework + FrameworkMap map[frameworks.Framework]Framework } // A Framework contains the name and templater for a @@ -66,37 +68,37 @@ func (p *Project) ExitCLI(tprogram *tea.Program) { // createFrameWorkMap adds the current supported // Frameworks into a Project's FrameworkMap func (p *Project) createFrameworkMap() { - p.FrameworkMap["chi"] = Framework{ + p.FrameworkMap[frameworks.Chi] = Framework{ packageName: chiPackage, templater: tpl.ChiTemplates{}, } - p.FrameworkMap["standard library"] = Framework{ + p.FrameworkMap[frameworks.StandardLibrary] = Framework{ packageName: []string{}, templater: tpl.StandardLibTemplate{}, } - p.FrameworkMap["gin"] = Framework{ + p.FrameworkMap[frameworks.Gin] = Framework{ packageName: ginPackage, templater: tpl.GinTemplates{}, } - p.FrameworkMap["fiber"] = Framework{ + p.FrameworkMap[frameworks.Fiber] = Framework{ packageName: fiberPackage, templater: tpl.FiberTemplates{}, } - p.FrameworkMap["gorilla/mux"] = Framework{ + p.FrameworkMap[frameworks.GorillaMux] = Framework{ packageName: gorillaPackage, templater: tpl.GorillaTemplates{}, } - p.FrameworkMap["httprouter"] = Framework{ + p.FrameworkMap[frameworks.HttpRouter] = Framework{ packageName: routerPackage, templater: tpl.RouterTemplates{}, } - p.FrameworkMap["echo"] = Framework{ + p.FrameworkMap[frameworks.Echo] = Framework{ packageName: echoPackage, templater: tpl.EchoTemplates{}, } @@ -138,7 +140,7 @@ func (p *Project) CreateMainFile() error { } // Install the correct package for the selected framework - if p.ProjectType != "standard library" { + if p.ProjectType != frameworks.StandardLibrary { err = utils.GoGetPackage(projectPath, p.FrameworkMap[p.ProjectType].packageName) if err != nil { log.Printf("Could not install go dependency for the chosen framework %v\n", err) diff --git a/cmd/steps/steps.go b/cmd/steps/steps.go index 23015b9d..3b32795d 100644 --- a/cmd/steps/steps.go +++ b/cmd/steps/steps.go @@ -2,7 +2,10 @@ // each step of the CLI package steps -import textinput "github.com/melkeydev/go-blueprint/cmd/ui/textinput" +import ( + "github.com/melkeydev/go-blueprint/cmd/frameworks" + textinput "github.com/melkeydev/go-blueprint/cmd/ui/textinput" +) // A StepSchema contains the data that is used // for an individual step of the CLI @@ -27,18 +30,19 @@ type Item struct { // Options contains the name and type of the created project type Options struct { ProjectName *textinput.Output - ProjectType string + ProjectType frameworks.Framework } // InitSteps initializes and returns the *Steps to be used in the CLI program func InitSteps(options *Options) *Steps { + projectType := options.ProjectType.String() steps := &Steps{ []StepSchema{ { StepName: "Go Project Framework", Options: []Item{ { - Title: "Standard library", + Title: "Standard-library", Desc: "The built-in Go standard library HTTP package", }, { @@ -61,12 +65,13 @@ func InitSteps(options *Options) *Steps { Title: "HttpRouter", Desc: "HttpRouter is a lightweight high performance HTTP request router for Go", }, - {Title: "Echo", - Desc: "High performance, extensible, minimalist Go web framework", + { + Title: "Echo", + Desc: "High performance, extensible, minimalist Go web framework", }, }, Headers: "What framework do you want to use in your Go project?", - Field: &options.ProjectType, + Field: &projectType, }, }, }