panel/cmd/handler.go

399 lines
8.8 KiB
Go

//go:build dev
/*
Package cmd
Copyright © 2024 Shane C. <shane@scaffoe.com>
*/
package cmd
import (
"bytes"
"errors"
"fmt"
"github.com/spf13/cobra"
"gitlab.com/omnibill/linux"
"gitlab.com/omnibill/tui/confirmation"
"gitlab.com/omnibill/tui/textinput"
"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)
}