package servlet import ( "errors" util "geniuscartel.xyz/vinegar/vinegarUtil" "html/template" "io/ioutil" "net/http" "os" "path" "strings" ) type ( //TemplateManager handles loading and rendering templates. //It contains the following fields: // //templatePath - The base path to load template files from. // //componentPath - The base path to load template components from. // //templates - A map of template name to parsed *template.Template. // //components - A map of component name to template.HTML content. // //mixins - A map of mixin name to content string. // //Templates - A slice of all template names that were loaded. // //The main methods are: // //NewTemplateManager - Constructor to create a new instance. // //RenderTemplate - Renders a template by name and returns the output. // //GetComponent - Fetches a component by name. // //AddMixin - Adds a new mixin value by name. // //AddMixinFromFile - Loads a mixin from a file. // //RenderAllToFile - Renders all templates to files in a directory. // //Typical usage: // // tm := NewTemplateManager("templates", "components") // output := tm.RenderTemplate("index.tmpl") // tm.AddMixin("title", "My Page") TemplateManager struct { templatePath string componentPath string templates map[string]*template.Template components map[string]template.HTML mixins map[string]string Templates []string } ) func NewTemplateManager(templatePath string, componentPath string) *TemplateManager { templates := make(map[string]*template.Template) components := make(map[string]template.HTML) mixins := make(map[string]string) tm := TemplateManager{templatePath: templatePath, componentPath: componentPath, templates: templates, components: components, mixins: mixins} tm.loadAssets() return &tm } func (tm *TemplateManager) loadAssets() error { templateFiles, err := os.ReadDir(tm.templatePath) if err != nil { return err } for _, templateFile := range templateFiles { if !templateFile.IsDir() { filePath := path.Join(tm.templatePath, templateFile.Name()) dataContent, err := util.GetDiskContent(filePath) if err != nil { return err } tmpl := template.New(templateFile.Name()) tmpl.Parse(string(*dataContent)) tm.templates[templateFile.Name()] = tmpl tm.Templates = append(tm.Templates, templateFile.Name()) } } componentFiles, err := os.ReadDir(tm.componentPath) if err != nil { return err } for _, componentFile := range componentFiles { if !componentFile.IsDir() { filePath := path.Join(tm.componentPath, componentFile.Name()) contentBytes, err := util.GetDiskContent(filePath) if err != nil { return err } content := string(*contentBytes) tm.components[componentFile.Name()] = template.HTML(content) } } return nil } func (tm *TemplateManager) RenderTemplate(key string) (string, error) { tmpl, exists := tm.templates[key] if !exists { return "", errors.New("Could not load template for key: " + key) } buff := strings.Builder{} err := tmpl.Execute(&buff, tm) if err != nil { return "", err } return buff.String(), nil } func (tm *TemplateManager) GetComponent(key string) (template.HTML, error) { content, exists := tm.components[key] if !exists { return "", errors.New("Could not load data for key: " + key) } return content, nil } func (tm *TemplateManager) GetMixin(key string) template.HTML { content, exists := tm.mixins[key] if exists { return template.HTML(content) } else { return "" } } func (tm *TemplateManager) AddMixin(key string, value string) error { tm.mixins[key] = value return nil } func (tm *TemplateManager) AddMixinFromFile(key string, pathlike string) error { content, err := util.GetDiskContent(pathlike) if err != nil { return err } else { err2 := tm.AddMixin(key, string(*content)) if err2 != nil { return err2 } return nil } } func (tm *TemplateManager) RenderAllToFile(pathlike string) error { for _, v := range tm.Templates { p := path.Join(pathlike, v) tmpl := tm.templates[v] buff := strings.Builder{} err := tmpl.Execute(&buff, tm) if err != nil { return err } err = ioutil.WriteFile(p, []byte(buff.String()), 0755) if err != nil { return err } } return nil } func RenderTemplate(w http.ResponseWriter, pathlike string, data any) error { templateHelper := template.New(pathlike) f, err := os.OpenFile(pathlike, os.O_RDONLY, 0777) if err != nil { return err } defer f.Close() content, err := ioutil.ReadAll(f) if err != nil { return err } templ, err := templateHelper.Parse(string(content)) err = templ.Execute(w, data) if err != nil { return err } return nil }