//go:build dev /* Package cmd Copyright © 2024 Shane C. */ package cmd import ( "bytes" "egtyl.xyz/omnibill/linux" "egtyl.xyz/omnibill/tui/confirmation" "egtyl.xyz/omnibill/tui/textinput" "errors" "fmt" "github.com/spf13/cobra" "go/format" "go/parser" "go/token" "golang.org/x/mod/modfile" "golang.org/x/text/cases" "golang.org/x/text/language" "io" "io/fs" "log" "os" "path/filepath" "strings" "text/template" ) type templateData struct { PackagePath string ModulePath string UpperName string Name string Path string RequireAuth bool GetView bool GetNoView bool Post bool Put bool Delete bool Options bool Head bool Patch bool } // handlerCmd represents the handler command var handlerCmd = &cobra.Command{ Use: "handler", Short: "A brief description of your command", Run: func(cmd *cobra.Command, args []string) { tmplFile, err := templates.ReadFile("templates/handler.go.tmpl") if err != nil { log.Fatalln(err) } viewFile, err := templates.ReadFile("templates/view.go.tmpl") if err != nil { log.Fatalln(err) } importsFile, err := templates.ReadFile("templates/imports.go.tmpl") if err != nil { log.Fatalln(err) } handlerTempl, err := template.New("handler").Parse(string(tmplFile)) if err != nil { log.Fatalln(err) } viewTempl, err := template.New("view").Parse(string(viewFile)) if err != nil { log.Fatalln(err) } importsTempl, err := template.New("imports").Parse(string(importsFile)) if err != nil { log.Fatalln(err) } tmplData := templateData{} cwd, err := os.Getwd() if err != nil { log.Fatalln(err) } handlerDir := filepath.Join(cwd, "web/handlers") viewDir := filepath.Join(cwd, "web/views") modFile, err := os.ReadFile(cwd + "/go.mod") if err != nil { log.Fatalln(err) } modulePath := modfile.ModulePath(modFile) tmplData.ModulePath = modulePath inputHandlerPath, err := textinput.New(textinput.InputData{ Question: "Path of handler?", }) if err != nil { log.Fatalln(err) } pathSplit := strings.Split(*inputHandlerPath, "/") tmplData.UpperName = cases.Title(language.AmericanEnglish).String(pathSplit[len(pathSplit)-1]) tmplData.Name = pathSplit[len(pathSplit)-1] tmplData.Path = *inputHandlerPath if len(pathSplit) > 1 { tmplData.PackagePath = pathSplit[len(pathSplit)-2] } else { tmplData.PackagePath = strings.Join(pathSplit, "") } hasView, err := confirmation.New(confirmation.InputData{ Question: "Does this handler need a view?", }) if err != nil { log.Fatalln(err) } if *hasView { tmplData.GetView = true } else { hasGetView, err := confirmation.New(confirmation.InputData{ Question: "Does this handler need a GET handler?", }) if err != nil { log.Fatalln(err) } tmplData.GetNoView = *hasGetView } hasAuth, err := confirmation.New(confirmation.InputData{ Question: "Does this handler need a AUTH handler?", }) if err != nil { log.Fatalln(err) } tmplData.RequireAuth = *hasAuth hasPost, err := confirmation.New(confirmation.InputData{ Question: "Does this handler need a POST handler?", }) if err != nil { log.Fatalln(err) } tmplData.Post = *hasPost hasPut, err := confirmation.New(confirmation.InputData{ Question: "Does this handler need a PUT handler?", }) if err != nil { log.Fatalln(err) } tmplData.Put = *hasPut hasDelete, err := confirmation.New(confirmation.InputData{ Question: "Does this handler need a DELETE handler?", }) if err != nil { log.Fatalln(err) } tmplData.Delete = *hasDelete hasOptions, err := confirmation.New(confirmation.InputData{ Question: "Does this handler need a OPTIONS handler?", }) if err != nil { log.Fatalln(err) } tmplData.Options = *hasOptions hasHead, err := confirmation.New(confirmation.InputData{ Question: "Does this handler need a HEAD handler?", }) if err != nil { log.Fatalln(err) } tmplData.Head = *hasHead hasPatch, err := confirmation.New(confirmation.InputData{ Question: "Does this handler need a PATCH handler?", }) if err != nil { log.Fatalln(err) } tmplData.Patch = *hasPatch if *hasView { for i, _ := range pathSplit { isLast := i == len(pathSplit)-1 path := filepath.Join(append([]string{viewDir}, pathSplit[0:i+1]...)...) if _, err := os.Lstat(path); err != nil && !errors.Is(err, fs.ErrNotExist) { log.Fatalln(err) } else if err != nil && errors.Is(err, fs.ErrNotExist) { if err := os.MkdirAll(path, 0740); err != nil { log.Fatalln(err) } } if isLast { viewFileOut, err := os.Create(path + "/show.templ") if err != nil { log.Fatalln(err) } var buffer bytes.Buffer if err := viewTempl.Execute(&buffer, tmplData); err != nil { log.Fatalln(err) } if _, err := viewFileOut.Write(buffer.Bytes()); err != nil { log.Fatalln(err) } viewFileOut.Close() } } } templCommand, err := linux.NewCommand(linux.CommandOptions{ Env: map[string]string{ "PATH": os.Getenv("PATH"), }, Command: "templ", Args: []string{"generate"}, Shell: "/bin/bash", }) if err != nil { log.Fatalln(err) } if err := templCommand.Run(); err != nil { log.Fatalln(err) } if len(pathSplit) == 1 { tmplData.PackagePath = "handlers" } for i, _ := range pathSplit { isLast := i == len(pathSplit)-1 path := filepath.Join(append([]string{handlerDir}, pathSplit[0:i+1]...)...) if _, err := os.Lstat(path + ".go"); err != nil && !errors.Is(err, fs.ErrNotExist) { log.Fatalln(err) } else if err == nil { if err := os.MkdirAll(path, 0740); err != nil { log.Fatalln(err) } if err := os.Rename(path+".go", path+"/index.go"); err != nil { log.Fatalln(err) } file, err := os.OpenFile(path+"/index.go", os.O_RDWR|os.O_CREATE, 0600) if err != nil { log.Fatalln(err) } fset := token.NewFileSet() node, err := parser.ParseFile(fset, path+"/index.go", file, parser.ParseComments) if err != nil { log.Fatalln(err) } node.Name.Name = tmplData.PackagePath if err := file.Truncate(0); err != nil { log.Fatalln(err) } _, err = file.Seek(0, io.SeekStart) if err != nil { log.Fatalf("Failed to seek to beginning: %v", err) } if err := format.Node(file, fset, node); err != nil { log.Fatalln(err) } file.Close() } if _, err := os.Lstat(path); err != nil && !errors.Is(err, fs.ErrNotExist) { log.Fatalln(err) } else if err != nil && errors.Is(err, fs.ErrNotExist) && !isLast { if err := os.MkdirAll(path, 0740); err != nil { log.Fatalln(err) } } else if err != nil && errors.Is(err, fs.ErrNotExist) && isLast { handlerFileOut, err := os.Create(path + ".go") if err != nil { log.Fatalln(err) } var buffer bytes.Buffer if err := handlerTempl.Execute(&buffer, tmplData); err != nil { log.Fatalln(err) } if _, err := handlerFileOut.Write(buffer.Bytes()); err != nil { log.Fatalln(err) } handlerFileOut.Close() } else if err == nil && isLast { handlerFileOut, err := os.Create(path + "/index.go") if err != nil { log.Fatalln(err) } var buffer bytes.Buffer if err := handlerTempl.Execute(&buffer, tmplData); err != nil { log.Fatalln(err) } if _, err := handlerFileOut.Write(buffer.Bytes()); err != nil { log.Fatalln(err) } handlerFileOut.Close() } } fmt.Println("Generating Imports") var imports []string if err := filepath.WalkDir(handlerDir, func(path string, d fs.DirEntry, err error) error { if strings.HasSuffix(d.Name(), ".go") { folder := filepath.Dir(path) isFound := false if folder == handlerDir { return nil } output, _ := strings.CutPrefix(folder, cwd) for _, handlerImport := range imports { if handlerImport == modulePath+output { isFound = true } } if !isFound { imports = append(imports, modulePath+output) } } return nil }); err != nil { log.Fatalln(err) } if len(imports) != 0 { importFileOut, err := os.Create(handlerDir + "/imports.go") if err != nil { log.Fatalln(err) } defer importFileOut.Close() var buffer bytes.Buffer if err := importsTempl.Execute(&buffer, map[string]interface{}{ "Imports": imports, }); err != nil { log.Fatalln(err) } if _, err := importFileOut.Write(buffer.Bytes()); err != nil { log.Fatalln(err) } importFileOut.Close() } fmt.Println("Go formatting") fmtCommand, err := linux.NewCommand(linux.CommandOptions{ Command: "go", Args: []string{"fmt", "./..."}, Shell: "/bin/bash", }) if err != nil { log.Fatalln(err) } if err := fmtCommand.Run(); err != nil { log.Fatalln(err) } }, } func init() { generateCmd.AddCommand(handlerCmd) }