192 lines
5.2 KiB
Go
192 lines
5.2 KiB
Go
package route
|
|
|
|
import (
|
|
"bytes"
|
|
"text/template"
|
|
|
|
"github.com/Masterminds/sprig/v3"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// TemplateRenderer defines the interface for template rendering operations
|
|
type TemplateRenderer interface {
|
|
Render(data RenderData) ([]byte, error)
|
|
Validate() error
|
|
GetTemplateInfo() TemplateInfo
|
|
}
|
|
|
|
// TemplateInfo provides metadata about the template
|
|
type TemplateInfo struct {
|
|
Name string
|
|
Version string
|
|
Functions []string
|
|
Options []string
|
|
Size int
|
|
}
|
|
|
|
// RouteRenderer implements TemplateRenderer for route generation
|
|
type RouteRenderer struct {
|
|
template *template.Template
|
|
info TemplateInfo
|
|
logger *log.Entry
|
|
}
|
|
|
|
// NewRouteRenderer creates a new RouteRenderer instance with proper initialization
|
|
func NewRouteRenderer() *RouteRenderer {
|
|
renderer := &RouteRenderer{
|
|
logger: log.WithField("module", "route-renderer"),
|
|
info: TemplateInfo{
|
|
Name: "router",
|
|
Version: "1.0.0",
|
|
Functions: []string{
|
|
"sprig",
|
|
"template",
|
|
"custom",
|
|
},
|
|
Options: []string{
|
|
"missingkey=error",
|
|
},
|
|
},
|
|
}
|
|
|
|
// Initialize template with error handling
|
|
if err := renderer.initializeTemplate(); err != nil {
|
|
renderer.logger.WithError(err).Error("Failed to initialize template")
|
|
return nil
|
|
}
|
|
|
|
renderer.info.Size = len(routeTpl)
|
|
renderer.logger.WithFields(log.Fields{
|
|
"template_size": renderer.info.Size,
|
|
"version": renderer.info.Version,
|
|
}).Info("Route renderer initialized successfully")
|
|
|
|
return renderer
|
|
}
|
|
|
|
// initializeTemplate sets up the template with proper functions and options
|
|
func (r *RouteRenderer) initializeTemplate() error {
|
|
// Create template with sprig functions and custom options
|
|
tmpl := template.New(r.info.Name).
|
|
Funcs(sprig.FuncMap()).
|
|
Option("missingkey=error")
|
|
|
|
// Parse the template
|
|
parsedTmpl, err := tmpl.Parse(routeTpl)
|
|
if err != nil {
|
|
return WrapError(err, "failed to parse route template")
|
|
}
|
|
|
|
r.template = parsedTmpl
|
|
return nil
|
|
}
|
|
|
|
// Render renders the template with the provided data
|
|
func (r *RouteRenderer) Render(data RenderData) ([]byte, error) {
|
|
// Validate input data
|
|
if err := r.validateRenderData(data); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Create buffer for rendering
|
|
var buf bytes.Buffer
|
|
buf.Grow(estimatedBufferSize(data)) // Pre-allocate buffer for better performance
|
|
|
|
// Execute template with error handling
|
|
if err := r.template.Execute(&buf, data); err != nil {
|
|
r.logger.WithError(err).WithFields(log.Fields{
|
|
"package_name": data.PackageName,
|
|
"routes_count": len(data.Routes),
|
|
}).Error("Template execution failed")
|
|
return nil, WrapError(err, "template execution failed for package: %s", data.PackageName)
|
|
}
|
|
|
|
// Validate rendered content
|
|
result := buf.Bytes()
|
|
if len(result) == 0 {
|
|
return nil, NewRouteError(ErrTemplateFailed, "rendered content is empty for package: %s", data.PackageName)
|
|
}
|
|
|
|
r.logger.WithFields(log.Fields{
|
|
"package_name": data.PackageName,
|
|
"routes_count": len(data.Routes),
|
|
"content_length": len(result),
|
|
}).Debug("Template rendered successfully")
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// Validate checks if the renderer is properly configured
|
|
func (r *RouteRenderer) Validate() error {
|
|
if r.template == nil {
|
|
return NewRouteError(ErrTemplateFailed, "template is not initialized")
|
|
}
|
|
|
|
if r.info.Name == "" {
|
|
return NewRouteError(ErrTemplateFailed, "template name is not set")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetTemplateInfo returns metadata about the template
|
|
func (r *RouteRenderer) GetTemplateInfo() TemplateInfo {
|
|
return r.info
|
|
}
|
|
|
|
// validateRenderData validates the input data before rendering
|
|
func (r *RouteRenderer) validateRenderData(data RenderData) error {
|
|
if data.PackageName == "" {
|
|
return NewRouteError(ErrInvalidInput, "package name cannot be empty")
|
|
}
|
|
|
|
if len(data.Routes) == 0 {
|
|
return NewRouteError(ErrNoRoutes, "no routes to render for package: %s", data.PackageName)
|
|
}
|
|
|
|
// Validate that all routes have required fields
|
|
for controllerName, routes := range data.Routes {
|
|
if controllerName == "" {
|
|
return NewRouteError(ErrInvalidInput, "controller name cannot be empty")
|
|
}
|
|
|
|
for i, route := range routes {
|
|
if route.Method == "" {
|
|
return NewRouteError(ErrInvalidInput, "route method cannot be empty for controller %s, route %d", controllerName, i)
|
|
}
|
|
if route.Route == "" {
|
|
return NewRouteError(ErrInvalidInput, "route path cannot be empty for controller %s, route %d", controllerName, i)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// estimatedBufferSize calculates the estimated buffer size needed for rendering
|
|
func estimatedBufferSize(data RenderData) int {
|
|
// Base size for package structure
|
|
baseSize := 1024 // ~1KB for base package structure
|
|
|
|
// Add size based on routes
|
|
routesSize := len(data.Routes) * 256 // ~256 bytes per route
|
|
|
|
// Add size based on imports
|
|
importsSize := len(data.Imports) * 64 // ~64 bytes per import
|
|
|
|
// Add size based on controllers
|
|
controllersSize := len(data.Controllers) * 128 // ~128 bytes per controller
|
|
|
|
return baseSize + routesSize + importsSize + controllersSize
|
|
}
|
|
|
|
// renderTemplate is the legacy function for backward compatibility
|
|
// Use NewRouteRenderer().Render() for new code
|
|
func renderTemplate(data RenderData) ([]byte, error) {
|
|
renderer := NewRouteRenderer()
|
|
if renderer == nil {
|
|
return nil, NewRouteError(ErrTemplateFailed, "failed to create route renderer")
|
|
}
|
|
return renderer.Render(data)
|
|
}
|