forked from spacewander/boltcli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathscript.go
126 lines (113 loc) · 2.33 KB
/
script.go
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package main
import (
"strings"
"github.com/Shopify/go-lua"
)
var (
vm *lua.State
curCmd string
)
func init() {
vm = lua.NewState()
lua.OpenLibraries(vm)
injectAPI(vm)
}
func injectAPI(L *lua.State) {
L.CreateTable(0, 1)
L.CreateTable(0, 1)
L.PushGoFunction(dispatchCmd)
L.SetField(-2, "__index")
L.SetMetaTable(-2)
// inject global api namespace
L.Global("package")
L.Field(-1, "loaded")
L.PushValue(-3)
L.SetField(-2, "bolt")
L.Pop(2)
L.SetGlobal("bolt")
}
func dispatchCmd(L *lua.State) int {
// ignore the meta table itself (the first arg)
if s, ok := lua.ToStringMeta(L, 2); ok {
s = strings.ToLower(s)
_, ok = CmdMap[s]
if ok {
curCmd = s
L.PushGoFunction(execCmdInLuaScript)
return 1
}
}
// it is equal to return nil
return 0
}
func pushList(L *lua.State, res []string) {
L.CreateTable(len(res), 0)
for i, s := range res {
L.PushString(s)
L.RawSetInt(-2, i+1)
}
}
func pushMap(L *lua.State, res map[string]interface{}) {
L.CreateTable(0, len(res))
for k, v := range res {
L.PushString(k)
switch v := v.(type) {
case int64:
L.PushInteger(int(v))
case string:
L.PushString(v)
case map[string]interface{}:
pushMap(L, v)
}
L.RawSet(-3)
}
}
func execCmdInLuaScript(L *lua.State) int {
args := []string{}
nargs := L.Top()
for i := 1; i <= nargs; i++ {
luaType := L.TypeOf(i)
switch luaType {
case lua.TypeNumber:
fallthrough
case lua.TypeString:
if s, ok := lua.ToStringMeta(L, i); ok {
args = append(args, s)
}
default:
// arg x is one based, like other stuff in lua land
L.PushFString("The type of arg %d is incorrect, only number and string are acceptable", i)
L.Error()
}
}
// we have checked the existence of 'curCmd' before
f, _ := CmdMap[curCmd]
res, err := f(args...)
if err != nil {
L.PushNil()
L.PushString(err.Error())
return 2
}
switch res := res.(type) {
case bool:
L.PushBoolean(res)
case []byte:
L.PushString(string(res))
case string:
L.PushString(res)
case []string:
pushList(L, res)
case map[string]interface{}:
pushMap(L, res)
case int:
L.PushInteger(res)
default:
L.PushFString("The type of result returns from command '%s' with args %v is unsupported", curCmd, args)
L.Error()
}
return 1
}
// StartScript evals given script file
func StartScript(script string) error {
return lua.DoFile(vm, script)
}