From 76599232dc8f652e04dfc62258c8707c00f8b7fc Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 9 Dec 2019 17:54:24 +0100 Subject: [PATCH] Add a Lua API to set template filters --- plugin.go | 40 +++++++++++++++++++++++++++++++++++++--- server.go | 10 +++++----- template.go | 11 ++++++++--- 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/plugin.go b/plugin.go index d2737d0..26473b1 100644 --- a/plugin.go +++ b/plugin.go @@ -2,6 +2,7 @@ package koushin import ( "fmt" + "html/template" "path/filepath" "github.com/labstack/echo/v4" @@ -11,6 +12,7 @@ import ( type Plugin interface { Name() string + Filters() template.FuncMap Render(name string, data interface{}) error Close() error } @@ -19,6 +21,7 @@ type luaPlugin struct { filename string state *lua.LState renderCallbacks map[string]*lua.LFunction + filters template.FuncMap } func (p *luaPlugin) Name() string { @@ -32,23 +35,53 @@ func (p *luaPlugin) onRender(l *lua.LState) int { return 0 } +func (p *luaPlugin) setFilter(l *lua.LState) int { + name := l.CheckString(1) + f := l.CheckFunction(2) + p.filters[name] = func(args... interface{}) string { + luaArgs := make([]lua.LValue, len(args)) + for i, v := range args { + luaArgs[i] = luar.New(l, v) + } + + err := l.CallByParam(lua.P{ + Fn: f, + NRet: 1, + Protect: true, + }, luaArgs...) + if err != nil { + panic(err) // TODO: better error handling? + } + + ret := l.CheckString(-1) + l.Pop(1) + return ret + } + return 0 +} + func (p *luaPlugin) Render(name string, data interface{}) error { f, ok := p.renderCallbacks[name] if !ok { return nil } - if err := p.state.CallByParam(lua.P{ + err := p.state.CallByParam(lua.P{ Fn: f, NRet: 0, Protect: true, - }, luar.New(p.state, data)); err != nil { + }, luar.New(p.state, data)) + if err != nil { return err } return nil } +func (p *luaPlugin) Filters() template.FuncMap { + return p.filters +} + func (p *luaPlugin) Close() error { p.state.Close() return nil @@ -60,12 +93,13 @@ func loadLuaPlugin(filename string) (*luaPlugin, error) { filename: filename, state: l, renderCallbacks: make(map[string]*lua.LFunction), + filters: make(template.FuncMap), } mt := l.NewTypeMetatable("koushin") l.SetGlobal("koushin", mt) l.SetField(mt, "on_render", l.NewFunction(p.onRender)) - // TODO: set_filter + l.SetField(mt, "set_filter", l.NewFunction(p.setFilter)) if err := l.DoFile(filename); err != nil { l.Close() diff --git a/server.go b/server.go index 0845628..501f503 100644 --- a/server.go +++ b/server.go @@ -128,16 +128,16 @@ func New(e *echo.Echo, options *Options) error { return err } - e.Renderer, err = loadTemplates(e.Logger, options.Theme) - if err != nil { - return fmt.Errorf("failed to load templates: %v", err) - } - s.plugins, err = loadAllLuaPlugins(e.Logger) if err != nil { return fmt.Errorf("failed to load plugins: %v", err) } + e.Renderer, err = loadTemplates(e.Logger, options.Theme, s.plugins) + if err != nil { + return fmt.Errorf("failed to load templates: %v", err) + } + e.HTTPErrorHandler = func(err error, c echo.Context) { code := http.StatusInternalServerError if he, ok := err.(*echo.HTTPError); ok { diff --git a/template.go b/template.go index a4c3ee0..2cce5a3 100644 --- a/template.go +++ b/template.go @@ -27,15 +27,20 @@ func (t *tmpl) Render(w io.Writer, name string, data interface{}, ectx echo.Cont return t.t.ExecuteTemplate(w, name, data) } -func loadTemplates(logger echo.Logger, themeName string) (*tmpl, error) { - base, err := template.New("").Funcs(template.FuncMap{ +func loadTemplates(logger echo.Logger, themeName string, plugins []Plugin) (*tmpl, error) { + base := template.New("").Funcs(template.FuncMap{ "tuple": func(values ...interface{}) []interface{} { return values }, "pathescape": func(s string) string { return url.PathEscape(s) }, - }).ParseGlob("public/*.html") + }) + for _, p := range plugins { + base = base.Funcs(p.Filters()) + } + + base, err := base.ParseGlob("public/*.html") if err != nil { return nil, err }