package main import ( "fmt" "os" "strings" "text/template" "time" ) // HandlerData contains template data for handler generation type HandlerData struct { Name string NameLower string NamePlural string ModuleName string HasGet bool HasPost bool HasPut bool HasDelete bool HasParam bool HasRequest bool HasResponse bool Timestamp string } func main() { if len(os.Args) < 2 { fmt.Println("Usage: go run generate-handler.go [methods]") fmt.Println("Example: go run generate-handler.go user get post put delete") fmt.Println("Methods: get, post, put, delete (optional, default: get post)") os.Exit(1) } handlerName := strings.Title(os.Args[1]) methods := []string{"get", "post"} if len(os.Args) > 2 { methods = os.Args[2:] } // Convert to lowercase for file names handlerLower := strings.ToLower(handlerName) handlerPlural := handlerLower + "s" data := HandlerData{ Name: handlerName, NameLower: handlerLower, NamePlural: handlerPlural, ModuleName: "api-service", Timestamp: time.Now().Format("2006-01-02 15:04:05"), } // Check which methods are requested for _, method := range methods { switch strings.ToLower(method) { case "get": data.HasGet = true case "post": data.HasPost = true case "put": data.HasPut = true case "delete": data.HasDelete = true } } // Check if we need request/response models data.HasRequest = data.HasPost || data.HasPut data.HasResponse = true data.HasParam = data.HasGet || data.HasPut || data.HasDelete fmt.Printf("Generating handler: %s with methods: %v\n", handlerName, methods) // Create directories if they don't exist os.MkdirAll("internal/handlers", 0755) os.MkdirAll("internal/models", 0755) // Generate files generateHandlerFile(data) generateModelFile(data) updateRoutesFile(data) fmt.Printf("Successfully generated handler: %s\n", handlerName) fmt.Println("Don't forget to run: swag init -g cmd/api/main.go") } func generateHandlerFile(data HandlerData) { handlerTemplate := `package handlers import ( "net/http" "strings" "{{.ModuleName}}/internal/models" "github.com/gin-gonic/gin" "github.com/google/uuid" ) // {{.Name}}Handler handles {{.NameLower}} services type {{.Name}}Handler struct{} // New{{.Name}}Handler creates a new {{.Name}}Handler func New{{.Name}}Handler() *{{.Name}}Handler { return &{{.Name}}Handler{} } {{if .HasGet}} // Get{{.Name}} godoc // @Summary Get {{.NameLower}} // @Description Returns a list of {{.NamePlural}} // @Tags {{.NameLower}} // @Accept json // @Produce json // @Success 200 {object} models.{{.Name}}GetResponse "{{.Name}} GET response" // @Router /api/v1/{{.NamePlural}} [get] func (h *{{.Name}}Handler) Get{{.Name}}(c *gin.Context) { response := models.{{.Name}}GetResponse{ Message: "List of {{.NamePlural}}", Data: []string{"{{.Name}} 1", "{{.Name}} 2"}, } c.JSON(http.StatusOK, response) } {{end}} {{if .HasGet}} // Get{{.Name}}ByID godoc // @Summary Get {{.NameLower}} by ID // @Description Returns a single {{.NameLower}} by ID // @Tags {{.NameLower}} // @Accept json // @Produce json // @Param id path string true "{{.Name}} ID" // @Success 200 {object} models.{{.Name}}GetByIDResponse "{{.Name}} GET by ID response" // @Failure 404 {object} models.ErrorResponse "{{.Name}} not found" // @Router /api/v1/{{.NamePlural}}/{id} [get] func (h *{{.Name}}Handler) Get{{.Name}}ByID(c *gin.Context) { id := c.Param("id") response := models.{{.Name}}GetByIDResponse{ ID: id, Message: "{{.Name}} details", } c.JSON(http.StatusOK, response) } {{end}} {{if .HasPost}} // Create{{.Name}} godoc // @Summary Create {{.NameLower}} // @Description Creates a new {{.NameLower}} // @Tags {{.NameLower}} // @Accept json // @Produce json // @Param request body models.{{.Name}}CreateRequest true "{{.Name}} creation request" // @Success 201 {object} models.{{.Name}}CreateResponse "{{.Name}} created successfully" // @Failure 400 {object} models.ErrorResponse "Bad request" // @Router /api/v1/{{.NamePlural}} [post] func (h *{{.Name}}Handler) Create{{.Name}}(c *gin.Context) { var req models.{{.Name}}CreateRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } response := models.{{.Name}}CreateResponse{ ID: uuid.NewString(), Message: "{{.Name}} created successfully", Data: req, } c.JSON(http.StatusCreated, response) } {{end}} {{if .HasPut}} // Update{{.Name}} godoc // @Summary Update {{.NameLower}} // @Description Updates an existing {{.NameLower}} // @Tags {{.NameLower}} // @Accept json // @Produce json // @Param id path string true "{{.Name}} ID" // @Param request body models.{{.Name}}UpdateRequest true "{{.Name}} update request" // @Success 200 {object} models.{{.Name}}UpdateResponse "{{.Name}} updated successfully" // @Failure 400 {object} models.ErrorResponse "Bad request" // @Failure 404 {object} models.ErrorResponse "{{.Name}} not found" // @Router /api/v1/{{.NamePlural}}/{id} [put] func (h *{{.Name}}Handler) Update{{.Name}}(c *gin.Context) { id := c.Param("id") var req models.{{.Name}}UpdateRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } response := models.{{.Name}}UpdateResponse{ ID: id, Message: "{{.Name}} updated successfully", Data: req, } c.JSON(http.StatusOK, response) } {{end}} {{if .HasDelete}} // Delete{{.Name}} godoc // @Summary Delete {{.NameLower}} // @Description Deletes a {{.NameLower}} by ID // @Tags {{.NameLower}} // @Accept json // @Produce json // @Param id path string true "{{.Name}} ID" // @Success 200 {object} models.{{.Name}}DeleteResponse "{{.Name}} deleted successfully" // @Failure 404 {object} models.ErrorResponse "{{.Name}} not found" // @Router /api/v1/{{.NamePlural}}/{id} [delete] func (h *{{.Name}}Handler) Delete{{.Name}}(c *gin.Context) { id := c.Param("id") response := models.{{.Name}}DeleteResponse{ ID: id, Message: "{{.Name}} deleted successfully", } c.JSON(http.StatusOK, response) } {{end}} ` writeFile("internal/handlers/"+data.NameLower+".go", handlerTemplate, data) } func generateModelFile(data HandlerData) { modelTemplate := `package models {{if .HasGet}} // {{.Name}}GetResponse represents the response for GET {{.NamePlural}} type {{.Name}}GetResponse struct { Message string {{.Backtick}}json:"message"{{.Backtick}} Data interface{} {{.Backtick}}json:"data"{{.Backtick}} } {{end}} {{if .HasGet}} // {{.Name}}GetByIDResponse represents the response for GET {{.NameLower}} by ID type {{.Name}}GetByIDResponse struct { ID string {{.Backtick}}json:"id"{{.Backtick}} Message string {{.Backtick}}json:"message"{{.Backtick}} } {{end}} {{if .HasPost}} // {{.Name}}CreateRequest represents the request for creating {{.NameLower}} type {{.Name}}CreateRequest struct { Name string {{.Backtick}}json:"name" binding:"required"{{.Backtick}} // Add more fields as needed } // {{.Name}}CreateResponse represents the response for creating {{.NameLower}} type {{.Name}}CreateResponse struct { ID string {{.Backtick}}json:"id"{{.Backtick}} Message string {{.Backtick}}json:"message"{{.Backtick}} Data interface{} {{.Backtick}}json:"data"{{.Backtick}} } {{end}} {{if .HasPut}} // {{.Name}}UpdateRequest represents the request for updating {{.NameLower}} type {{.Name}}UpdateRequest struct { Name string {{.Backtick}}json:"name" binding:"required"{{.Backtick}} // Add more fields as needed } // {{.Name}}UpdateResponse represents the response for updating {{.NameLower}} type {{.Name}}UpdateResponse struct { ID string {{.Backtick}}json:"id"{{.Backtick}} Message string {{.Backtick}}json:"message"{{.Backtick}} Data interface{} {{.Backtick}}json:"data"{{.Backtick}} } {{end}} {{if .HasDelete}} // {{.Name}}DeleteResponse represents the response for deleting {{.NameLower}} type {{.Name}}DeleteResponse struct { ID string {{.Backtick}}json:"id"{{.Backtick}} Message string {{.Backtick}}json:"message"{{.Backtick}} } {{end}} // ErrorResponse represents an error response type ErrorResponse struct { Error string {{.Backtick}}json:"error"{{.Backtick}} } ` // Replace backtick with actual backtick modelTemplate = strings.ReplaceAll(modelTemplate, "{{.Backtick}}", "`") writeFile("internal/models/"+data.NameLower+".go", modelTemplate, data) } func updateRoutesFile(data HandlerData) { routesFile := "internal/routes/v1/routes.go" // Read existing routes file content, err := os.ReadFile(routesFile) if err != nil { fmt.Printf("Error reading routes file: %v\n", err) return } // Convert to string routesContent := string(content) // Find the place to insert new routes insertMarker := "\t\t// Example endpoints" // Generate new routes newRoutes := fmt.Sprintf("\t\t// %s endpoints\n", data.Name) if data.HasGet { newRoutes += fmt.Sprintf("\t\t%sHandler := handlers.New%sHandler()\n", data.NameLower, data.Name) newRoutes += fmt.Sprintf("\t\tv1.GET(\"/%s\", %sHandler.Get%s)\n", data.NamePlural, data.NameLower, data.Name) } if data.HasGet { newRoutes += fmt.Sprintf("\t\tv1.GET(\"/%s/:id\", %sHandler.Get%sByID)\n", data.NamePlural, data.NameLower, data.Name) } if data.HasPost { if !data.HasGet { newRoutes += fmt.Sprintf("\t\t%sHandler := handlers.New%sHandler()\n", data.NameLower, data.Name) } newRoutes += fmt.Sprintf("\t\tv1.POST(\"/%s\", %sHandler.Create%s)\n", data.NamePlural, data.NameLower, data.Name) } if data.HasPut { newRoutes += fmt.Sprintf("\t\tv1.PUT(\"/%s/:id\", %sHandler.Update%s)\n", data.NamePlural, data.NameLower, data.Name) } if data.HasDelete { newRoutes += fmt.Sprintf("\t\tv1.DELETE(\"/%s/:id\", %sHandler.Delete%s)\n", data.NamePlural, data.NameLower, data.Name) } newRoutes += "\n" // Insert new routes after the marker newContent := strings.Replace(routesContent, insertMarker, insertMarker+"\n"+newRoutes, 1) // Write back to file err = os.WriteFile(routesFile, []byte(newContent), 0644) if err != nil { fmt.Printf("Error writing routes file: %v\n", err) return } } func writeFile(filename, templateStr string, data HandlerData) { tmpl, err := template.New("template").Parse(templateStr) if err != nil { fmt.Printf("Error parsing template: %v\n", err) return } file, err := os.Create(filename) if err != nil { fmt.Printf("Error creating file %s: %v\n", filename, err) return } defer file.Close() err = tmpl.Execute(file, data) if err != nil { fmt.Printf("Error executing template: %v\n", err) return } fmt.Printf("Generated: %s\n", filename) }