Skip to content

Commit 5f2bd79

Browse files
committed
refactor(pluginsys): extract plugin-related types and interfaces into pluginsys package
This commit refactors the codebase by moving plugin-related types, interfaces, and constants into a new `pluginsys` package. This improves code organization and maintainability by centralizing plugin-related logic. Additionally, the `LuaPlugin` struct has been updated to use the new `pluginsys` package, and the `Plugin` interface has been introduced to define plugin behavior. This change also includes minor fixes and improvements to ensure consistency across the codebase.
1 parent 3804968 commit 5f2bd79

11 files changed

Lines changed: 586 additions & 521 deletions

File tree

internal/lua_plugin.go

Lines changed: 80 additions & 343 deletions
Large diffs are not rendered by default.

internal/luai/plugin.go

Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
package luai
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
10+
"github.com/version-fox/vfox/internal/cache"
11+
"github.com/version-fox/vfox/internal/config"
12+
"github.com/version-fox/vfox/internal/logger"
13+
"github.com/version-fox/vfox/internal/pluginsys"
14+
"github.com/version-fox/vfox/internal/util"
15+
lua "github.com/yuin/gopher-lua"
16+
)
17+
18+
const (
19+
luaPluginObjKey = "PLUGIN"
20+
)
21+
22+
type LuaPlugin2 struct {
23+
vm *LuaVM
24+
pluginObj *lua.LTable
25+
26+
*pluginsys.PluginInfo
27+
}
28+
29+
func (l *LuaPlugin2) HasFunction(name string) bool {
30+
return l.pluginObj.RawGetString(name) != lua.LNil
31+
}
32+
33+
func (l *LuaPlugin2) Close() {
34+
l.vm.Close()
35+
}
36+
37+
func (l *LuaPlugin2) Available(ctx *pluginsys.AvailableHookCtx) (*pluginsys.AvailableHookResult, error) {
38+
L := l.vm.Instance
39+
ctxTable, err := Marshal(L, ctx)
40+
if err != nil {
41+
return nil, err
42+
}
43+
table, err := l.CallFunction("Available", ctxTable)
44+
if err != nil {
45+
return nil, err
46+
}
47+
if table == nil || table.Type() == lua.LTNil {
48+
return nil, errors.New("no result provided")
49+
}
50+
51+
hookResult := pluginsys.AvailableHookResult{}
52+
err = Unmarshal(table, &hookResult)
53+
if err != nil {
54+
return nil, errors.New("failed to unmarshal the return value: " + err.Error())
55+
}
56+
57+
return &hookResult, nil
58+
}
59+
func (l *LuaPlugin2) PreInstall(ctx *pluginsys.PreInstallHookCtx) (*pluginsys.PreInstallHookResult, error) {
60+
L := l.vm.Instance
61+
ctxTable, err := Marshal(L, ctx)
62+
if err != nil {
63+
return nil, err
64+
}
65+
table, err := l.CallFunction("PreInstall", ctxTable)
66+
if err != nil {
67+
return nil, err
68+
}
69+
if table == nil || table.Type() == lua.LTNil {
70+
return nil, errors.New("no result provided")
71+
}
72+
hookResult := pluginsys.PreInstallHookResult{}
73+
err = Unmarshal(table, &hookResult)
74+
if err != nil {
75+
return nil, errors.New("failed to unmarshal the return value: " + err.Error())
76+
}
77+
return &hookResult, nil
78+
}
79+
80+
func (l *LuaPlugin2) EnvKeys(ctx *pluginsys.EnvKeysHookCtx) ([]*pluginsys.EnvKeysHookResultItem, error) {
81+
L := l.vm.Instance
82+
ctxTable, err := Marshal(L, ctx)
83+
if err != nil {
84+
return nil, err
85+
}
86+
table, err := l.CallFunction("EnvKeys", ctxTable)
87+
if err != nil {
88+
return nil, err
89+
}
90+
if table == nil || table.Type() == lua.LTNil || table.Len() == 0 {
91+
return nil, fmt.Errorf("no environment variables provided")
92+
}
93+
94+
var hookResult []*pluginsys.EnvKeysHookResultItem
95+
err = Unmarshal(table, &hookResult)
96+
if err != nil {
97+
return nil, errors.New("failed to unmarshal the return value: " + err.Error())
98+
}
99+
return hookResult, nil
100+
}
101+
102+
// PreUse
103+
func (l *LuaPlugin2) PreUse(ctx *pluginsys.PreUseHookCtx) (*pluginsys.PreUseHookResult, error) {
104+
L := l.vm.Instance
105+
ctxTable, err := Marshal(L, ctx)
106+
if err != nil {
107+
return nil, err
108+
}
109+
table, err := l.CallFunction("PreUse", ctxTable)
110+
if err != nil {
111+
return nil, err
112+
}
113+
if table == nil || table.Type() == lua.LTNil {
114+
return nil, errors.New("no result provided")
115+
}
116+
hookResult := pluginsys.PreUseHookResult{}
117+
err = Unmarshal(table, &hookResult)
118+
if err != nil {
119+
return nil, errors.New("failed to unmarshal the return value: " + err.Error())
120+
}
121+
return &hookResult, nil
122+
}
123+
124+
func (l *LuaPlugin2) PreUninstall(ctx *pluginsys.PreUninstallHookCtx) error {
125+
L := l.vm.Instance
126+
ctxTable, err := Marshal(L, ctx)
127+
if err != nil {
128+
return err
129+
}
130+
_, err = l.CallFunction("PreUninstall", ctxTable)
131+
return err
132+
}
133+
134+
func (l *LuaPlugin2) PostInstall(ctx *pluginsys.PostInstallHookCtx) error {
135+
L := l.vm.Instance
136+
ctxTable, err := Marshal(L, ctx)
137+
if err != nil {
138+
return err
139+
}
140+
_, err = l.CallFunction("PostInstall", ctxTable)
141+
return err
142+
}
143+
144+
func (l *LuaPlugin2) ParseLegacyFile(ctx *pluginsys.ParseLegacyFileHookCtx) (*pluginsys.ParseLegacyFileResult, error) {
145+
L := l.vm.Instance
146+
ctxTable, err := Marshal(L, ctx)
147+
if err != nil {
148+
return nil, err
149+
}
150+
table, err := l.CallFunction("ParseLegacyFile", ctxTable)
151+
if err != nil {
152+
return nil, err
153+
}
154+
if table == nil || table.Type() == lua.LTNil {
155+
return nil, errors.New("no result provided")
156+
}
157+
hookResult := pluginsys.ParseLegacyFileResult{}
158+
err = Unmarshal(table, &hookResult)
159+
if err != nil {
160+
return nil, errors.New("failed to unmarshal the return value: " + err.Error())
161+
}
162+
return &hookResult, nil
163+
}
164+
165+
func (l *LuaPlugin2) CallFunction(funcName string, args ...lua.LValue) (*lua.LTable, error) {
166+
logger.Debugf("CallFunction: %s\n", funcName)
167+
168+
table, err := l.vm.CallFunction(l.pluginObj.RawGetString(funcName), append([]lua.LValue{l.pluginObj}, args...)...)
169+
170+
return table, err
171+
}
172+
173+
func NewLuaPlugin2(pluginDirPath string, config *config.Config, runtimeVersion string) (*LuaPlugin2, error) {
174+
vm := NewLuaVM()
175+
if err := vm.Prepare(&PrepareOptions{
176+
Config: config,
177+
}); err != nil {
178+
return nil, err
179+
}
180+
181+
mainPath := filepath.Join(pluginDirPath, "main.lua")
182+
// main.lua first
183+
if util.FileExists(mainPath) {
184+
vm.LimitPackagePath(filepath.Join(pluginDirPath, "?.lua"))
185+
if err := vm.Instance.DoFile(mainPath); err != nil {
186+
return nil, err
187+
}
188+
} else {
189+
// Limit package search scope, hooks directory search priority is higher than lib directory
190+
hookPath := filepath.Join(pluginDirPath, "hooks", "?.lua")
191+
libPath := filepath.Join(pluginDirPath, "lib", "?.lua")
192+
vm.LimitPackagePath(hookPath, libPath)
193+
194+
// load metadata file
195+
metadataPath := filepath.Join(pluginDirPath, "metadata.lua")
196+
if !util.FileExists(metadataPath) {
197+
return nil, fmt.Errorf("plugin invalid, metadata file not found")
198+
}
199+
200+
if err := vm.Instance.DoFile(metadataPath); err != nil {
201+
return nil, fmt.Errorf("failed to load metadata file, %w", err)
202+
}
203+
204+
// load hook func files
205+
for _, hf := range pluginsys.HookFuncMap {
206+
hp := filepath.Join(pluginDirPath, "hooks", hf.Filename+".lua")
207+
208+
if !hf.Required && !util.FileExists(hp) {
209+
continue
210+
}
211+
if err := vm.Instance.DoFile(hp); err != nil {
212+
return nil, fmt.Errorf("failed to load [%s] hook function: %s", hf.Name, err.Error())
213+
}
214+
}
215+
}
216+
217+
// !!!! Must be set after loading the script to prevent overwriting!
218+
// set OS_TYPE and ARCH_TYPE
219+
vm.Instance.SetGlobal(pluginsys.OsType, lua.LString(util.GetOSType()))
220+
vm.Instance.SetGlobal(pluginsys.ArchType, lua.LString(util.GetArchType()))
221+
222+
r, err := Marshal(vm.Instance, pluginsys.RuntimeInfo{
223+
OsType: string(util.GetOSType()),
224+
ArchType: string(util.GetArchType()),
225+
Version: runtimeVersion,
226+
PluginDirPath: pluginDirPath,
227+
})
228+
if err != nil {
229+
return nil, err
230+
}
231+
232+
vm.Instance.SetGlobal(pluginsys.Runtime, r)
233+
pluginObj := vm.Instance.GetGlobal(luaPluginObjKey)
234+
if pluginObj.Type() == lua.LTNil {
235+
return nil, fmt.Errorf("plugin object not found")
236+
}
237+
PLUGIN := pluginObj.(*lua.LTable)
238+
pluginInfo := &pluginsys.PluginInfo{}
239+
if err = Unmarshal(PLUGIN, pluginInfo); err != nil {
240+
return nil, err
241+
}
242+
243+
source := &LuaPlugin2{
244+
vm: vm,
245+
pluginObj: PLUGIN,
246+
247+
PluginInfo: pluginInfo,
248+
}
249+
// wrap Available hook with Cache.
250+
if source.HasFunction("Available") {
251+
targetHook := PLUGIN.RawGetString("Available")
252+
source.pluginObj.RawSetString("Available", vm.Instance.NewFunction(func(L *lua.LState) int {
253+
ctxTable := L.CheckTable(2)
254+
255+
cachePath := filepath.Join(pluginDirPath, "available.cache")
256+
invokeAvailableHook := func() int {
257+
logger.Debugf("Calling the original Available hook. \n")
258+
table, err := vm.CallFunction(targetHook, PLUGIN, ctxTable)
259+
if err != nil {
260+
L.RaiseError(err.Error())
261+
return 0
262+
}
263+
if util.FileExists(cachePath) {
264+
logger.Debugf("Removing the old cache file: %s \n", cachePath)
265+
_ = os.Remove(cachePath)
266+
}
267+
L.Push(table)
268+
return 1
269+
}
270+
271+
logger.Debugf("Available hook cache duration: %v\n", config.Cache.AvailableHookDuration)
272+
// Cache is disabled
273+
if config.Cache.AvailableHookDuration == 0 {
274+
return invokeAvailableHook()
275+
}
276+
277+
ctx := &pluginsys.AvailableHookCtx{}
278+
if err := Unmarshal(ctxTable, ctx); err != nil {
279+
L.RaiseError(err.Error())
280+
return 0
281+
}
282+
283+
cacheKey := strings.Join(ctx.Args, "##")
284+
if cacheKey == "" {
285+
cacheKey = "empty"
286+
}
287+
fileCache, err := cache.NewFileCache(cachePath)
288+
if err != nil {
289+
return invokeAvailableHook()
290+
}
291+
cacheValue, ok := fileCache.Get(cacheKey)
292+
logger.Debugf("Available hook cache key: %s, hit: %v \n", cacheKey, ok)
293+
if ok {
294+
var hookResult []map[string]interface{}
295+
if err = cacheValue.Unmarshal(&hookResult); err != nil {
296+
return invokeAvailableHook()
297+
}
298+
table, err := Marshal(L, hookResult)
299+
if err != nil {
300+
return invokeAvailableHook()
301+
}
302+
L.Push(table)
303+
return 1
304+
} else {
305+
table, err := vm.CallFunction(targetHook, PLUGIN, ctxTable)
306+
if err != nil {
307+
L.RaiseError(err.Error())
308+
return 0
309+
}
310+
if table == nil || table.Type() == lua.LTNil {
311+
fileCache.Set(cacheKey, nil, cache.ExpireTime(config.Cache.AvailableHookDuration))
312+
_ = fileCache.Close()
313+
} else {
314+
var hookResult []map[string]interface{}
315+
if err = Unmarshal(table, &hookResult); err == nil {
316+
if value, err := cache.NewValue(hookResult); err == nil {
317+
fileCache.Set(cacheKey, value, cache.ExpireTime(config.Cache.AvailableHookDuration))
318+
_ = fileCache.Close()
319+
}
320+
}
321+
}
322+
L.Push(table)
323+
return 1
324+
}
325+
326+
}))
327+
328+
}
329+
return source, nil
330+
}

internal/luai/vm.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,16 @@ func (vm *LuaVM) ReturnedValue() *lua.LTable {
6565
return table
6666
}
6767

68-
func (vm *LuaVM) CallFunction(function lua.LValue, args ...lua.LValue) error {
68+
func (vm *LuaVM) CallFunction(function lua.LValue, args ...lua.LValue) (*lua.LTable, error) {
6969
if err := vm.Instance.CallByParam(lua.P{
7070
Fn: function.(*lua.LFunction),
7171
NRet: 1,
7272
Protect: true,
7373
}, args...); err != nil {
74-
return err
74+
return nil, err
7575
}
76-
return nil
76+
77+
return vm.ReturnedValue(), nil
7778
}
7879

7980
func (vm *LuaVM) GetTableString(table *lua.LTable, key string) string {

internal/manager.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,9 @@ func (m *Manager) SessionEnvKeys(opt SessionEnvOptions) (SdkEnvs, error) {
138138
tvs = append(tvs, workToolVersion)
139139

140140
if opt.WithGlobalEnv {
141-
homeToolVersion, err := toolset.NewToolVersion(m.PathMeta.HomePath)
142-
if err != nil {
143-
return nil, err
141+
homeToolVersion, herr := toolset.NewToolVersion(m.PathMeta.HomePath)
142+
if herr != nil {
143+
return nil, herr
144144
}
145145

146146
tvs = append(tvs, homeToolVersion)
@@ -631,7 +631,7 @@ func (m *Manager) Add(pluginName, url, alias string) error {
631631
//
632632
// 1.only support .lua or .zip file type plugin.
633633
// 2.install plugin to temp dir first, then validate the plugin, if success, return *LuaPlugin
634-
func (m *Manager) installPluginToTemp(path string) (*Plugin, error) {
634+
func (m *Manager) installPluginToTemp(path string) (*LuaPlugin, error) {
635635
ext := filepath.Ext(path)
636636
if ext != ".lua" && ext != ".zip" {
637637
return nil, fmt.Errorf("unsupported %s type plugin to install, only support .lua or .zip", ext)

0 commit comments

Comments
 (0)