aboutsummaryrefslogtreecommitdiffstats
path: root/plugin.go
blob: d2737d09509dfb26a6b1759092bcb29671574470 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package koushin

import (
	"fmt"
	"path/filepath"

	"github.com/labstack/echo/v4"
	"github.com/yuin/gopher-lua"
	"layeh.com/gopher-luar"
)

type Plugin interface {
	Name() string
	Render(name string, data interface{}) error
	Close() error
}

type luaPlugin struct {
	filename        string
	state           *lua.LState
	renderCallbacks map[string]*lua.LFunction
}

func (p *luaPlugin) Name() string {
	return p.filename
}

func (p *luaPlugin) onRender(l *lua.LState) int {
	name := l.CheckString(1)
	f := l.CheckFunction(2)
	p.renderCallbacks[name] = f
	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{
		Fn:      f,
		NRet:    0,
		Protect: true,
	}, luar.New(p.state, data)); err != nil {
		return err
	}

	return nil
}

func (p *luaPlugin) Close() error {
	p.state.Close()
	return nil
}

func loadLuaPlugin(filename string) (*luaPlugin, error) {
	l := lua.NewState()
	p := &luaPlugin{
		filename:        filename,
		state:           l,
		renderCallbacks: make(map[string]*lua.LFunction),
	}

	mt := l.NewTypeMetatable("koushin")
	l.SetGlobal("koushin", mt)
	l.SetField(mt, "on_render", l.NewFunction(p.onRender))
	// TODO: set_filter

	if err := l.DoFile(filename); err != nil {
		l.Close()
		return nil, err
	}

	return p, nil
}

func loadAllLuaPlugins(log echo.Logger) ([]Plugin, error) {
	filenames, err := filepath.Glob("plugins/*.lua")
	if err != nil {
		return nil, fmt.Errorf("filepath.Glob failed: %v", err)
	}

	plugins := make([]Plugin, 0, len(filenames))
	for _, filename := range filenames {
		log.Printf("Loading Lua plugin '%v'", filename)
		p, err := loadLuaPlugin(filename)
		if err != nil {
			for _, p := range plugins {
				p.Close()
			}
			return nil, fmt.Errorf("failed to load Lua plugin '%v': %v", filename, err)
		}
		plugins = append(plugins, p)
	}

	return plugins, nil
}