github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/znet/template_go.go (about) 1 package znet 2 3 import ( 4 "fmt" 5 "html/template" 6 "io" 7 "os" 8 "path/filepath" 9 "strings" 10 "sync" 11 12 "github.com/sohaha/zlsgo/zfile" 13 "github.com/sohaha/zlsgo/zlog" 14 "github.com/sohaha/zlsgo/zstring" 15 ) 16 17 type htmlEngine struct { 18 log *zlog.Logger 19 funcmap map[string]interface{} 20 Templates *template.Template 21 directory string 22 options TemplateOptions 23 mutex sync.RWMutex 24 loaded bool 25 } 26 27 type TemplateOptions struct { 28 Extension string 29 Layout string 30 DelimLeft string 31 DelimRight string 32 Reload bool 33 Debug bool 34 } 35 36 func getTemplateOptions(debug bool, opt ...func(o *TemplateOptions)) TemplateOptions { 37 o := TemplateOptions{ 38 Extension: ".html", 39 DelimLeft: "{{", 40 DelimRight: "}}", 41 Layout: "slot", 42 } 43 if debug { 44 o.Debug = true 45 o.Reload = true 46 } 47 for _, f := range opt { 48 f(&o) 49 } 50 return o 51 } 52 53 var _ Template = &htmlEngine{} 54 55 func newGoTemplate(e *Engine, directory string, opt ...func(o *TemplateOptions)) *htmlEngine { 56 h := &htmlEngine{ 57 directory: directory, 58 funcmap: make(map[string]interface{}), 59 } 60 if e != nil { 61 h.log = e.Log 62 h.options = getTemplateOptions(e.IsDebug(), opt...) 63 } else { 64 h.log = zlog.New() 65 h.log.ResetFlags(zlog.BitLevel) 66 h.options = getTemplateOptions(true, opt...) 67 } 68 return h 69 } 70 71 func (e *htmlEngine) AddFunc(name string, fn interface{}) *htmlEngine { 72 e.mutex.Lock() 73 e.funcmap[name] = fn 74 e.mutex.Unlock() 75 return e 76 } 77 78 func (e *htmlEngine) SetFuncMap(m map[string]interface{}) *htmlEngine { 79 e.funcmap = m 80 return e 81 } 82 83 func (e *htmlEngine) Load() error { 84 if e.loaded && !e.options.Reload { 85 return nil 86 } 87 88 e.mutex.Lock() 89 defer e.mutex.Unlock() 90 91 e.Templates = template.New(e.directory) 92 e.Templates.Delims(e.options.DelimLeft, e.options.DelimRight) 93 e.Templates.Funcs(e.funcmap) 94 95 total := 0 96 tip := zstring.Buffer() 97 err := filepath.Walk(e.directory, func(path string, info os.FileInfo, err error) error { 98 if err != nil { 99 return err 100 } 101 102 if info == nil || info.IsDir() { 103 return nil 104 } 105 106 if len(e.options.Extension) >= len(path) || path[len(path)-len(e.options.Extension):] != e.options.Extension { 107 return nil 108 } 109 110 rel, err := filepath.Rel(e.directory, path) 111 if err != nil { 112 return err 113 } 114 name := filepath.ToSlash(rel) 115 name = strings.TrimSuffix(name, e.options.Extension) 116 117 buf, err := zfile.ReadFile(path) 118 if err != nil { 119 return err 120 } 121 122 _, err = e.Templates.New(name).Parse(string(buf)) 123 if err == nil { 124 if e.options.Debug { 125 total++ 126 tip.WriteString("\t - " + name + "\n") 127 } 128 } 129 130 return err 131 }) 132 if err == nil && !e.loaded && e.options.Debug { 133 e.log.Debugf("Loaded HTML Templates (%d): \n%s", total, tip.String()) 134 } 135 136 e.loaded = true 137 return err 138 } 139 140 func (e *htmlEngine) Render(out io.Writer, template string, data interface{}, layout ...string) error { 141 if !e.loaded || e.options.Reload { 142 if err := e.Load(); err != nil { 143 return err 144 } 145 } 146 147 tmpl := e.Templates.Lookup(template) 148 if tmpl == nil { 149 return fmt.Errorf("template %s does not exist", template) 150 } 151 152 if len(layout) > 0 && layout[0] != "" { 153 lay := e.Templates.Lookup(layout[0]) 154 if lay == nil { 155 return fmt.Errorf("layout %s does not exist", layout[0]) 156 } 157 e.mutex.Lock() 158 defer e.mutex.Unlock() 159 lay.Funcs(map[string]interface{}{ 160 e.options.Layout: func() error { 161 return tmpl.Execute(out, data) 162 }, 163 }) 164 return lay.Execute(out, data) 165 } 166 167 return tmpl.Execute(out, data) 168 }