@@ -30,36 +30,46 @@ import (
30
30
)
31
31
32
32
const (
33
- LuaPluginObjKey = "PLUGIN"
34
- OsType = "OS_TYPE"
35
- ArchType = "ARCH_TYPE"
33
+ luaPluginObjKey = "PLUGIN"
34
+ osType = "OS_TYPE"
35
+ archType = "ARCH_TYPE"
36
+ runtime = "RUNTIME"
37
+ )
38
+
39
+ type HookFunc struct {
40
+ Name string
41
+ Required bool
42
+ Filename string
43
+ }
44
+
45
+ var (
46
+ // HookFuncMap is a map of built-in hook functions.
47
+ HookFuncMap = map [string ]HookFunc {
48
+ "Available" : {Name : "Available" , Required : true , Filename : "available" },
49
+ "PreInstall" : {Name : "PreInstall" , Required : true , Filename : "pre_install" },
50
+ "EnvKeys" : {Name : "EnvKeys" , Required : true , Filename : "env_keys" },
51
+ "PostInstall" : {Name : "PostInstall" , Required : false , Filename : "post_install" },
52
+ "PreUse" : {Name : "PreUse" , Required : false , Filename : "pre_use" },
53
+ }
36
54
)
37
55
38
56
type LuaPlugin struct {
39
57
vm * luai.LuaVM
40
58
pluginObj * lua.LTable
41
59
// plugin source path
42
- Filepath string
60
+ Path string
43
61
// plugin filename, this is also alias name, sdk-name
44
62
SdkName string
45
- // The name defined inside the plugin
46
-
47
63
LuaPluginInfo
48
64
}
49
65
50
- func (l * LuaPlugin ) checkValid () error {
51
- if l .vm == nil || l .vm .Instance == nil {
52
- return fmt .Errorf ("lua vm is nil" )
53
- }
54
-
55
- if ! l .HasFunction ("Available" ) {
56
- return fmt .Errorf ("[Available] function not found" )
57
- }
58
- if ! l .HasFunction ("PreInstall" ) {
59
- return fmt .Errorf ("[PreInstall] function not found" )
60
- }
61
- if ! l .HasFunction ("EnvKeys" ) {
62
- return fmt .Errorf ("[EnvKeys] function not found" )
66
+ func (l * LuaPlugin ) Validate () error {
67
+ for _ , hf := range HookFuncMap {
68
+ if hf .Required {
69
+ if ! l .HasFunction (hf .Name ) {
70
+ return fmt .Errorf ("[%s] function not found" , hf .Name )
71
+ }
72
+ }
63
73
}
64
74
return nil
65
75
}
@@ -322,7 +332,9 @@ func (l *LuaPlugin) CallFunction(funcName string, args ...lua.LValue) error {
322
332
return nil
323
333
}
324
334
325
- func NewLuaPlugin (content , path string , manager * Manager ) (* LuaPlugin , error ) {
335
+ // NewLegacyLuaPlugin creates a new LuaPlugin instance from old plugin format.
336
+ // TODO This will be deprecated in future versions.
337
+ func NewLegacyLuaPlugin (content , path string , manager * Manager ) (* LuaPlugin , error ) {
326
338
vm := luai .NewLuaVM ()
327
339
328
340
if err := vm .Prepare (& luai.PrepareOptions {
@@ -337,10 +349,10 @@ func NewLuaPlugin(content, path string, manager *Manager) (*LuaPlugin, error) {
337
349
338
350
// !!!! Must be set after loading the script to prevent overwriting!
339
351
// set OS_TYPE and ARCH_TYPE
340
- vm .Instance .SetGlobal (OsType , lua .LString (util .GetOSType ()))
341
- vm .Instance .SetGlobal (ArchType , lua .LString (util .GetArchType ()))
352
+ vm .Instance .SetGlobal (osType , lua .LString (util .GetOSType ()))
353
+ vm .Instance .SetGlobal (archType , lua .LString (util .GetArchType ()))
342
354
343
- pluginObj := vm .Instance .GetGlobal (LuaPluginObjKey )
355
+ pluginObj := vm .Instance .GetGlobal (luaPluginObjKey )
344
356
if pluginObj .Type () == lua .LTNil {
345
357
return nil , fmt .Errorf ("plugin object not found" )
346
358
}
@@ -350,11 +362,11 @@ func NewLuaPlugin(content, path string, manager *Manager) (*LuaPlugin, error) {
350
362
source := & LuaPlugin {
351
363
vm : vm ,
352
364
pluginObj : PLUGIN ,
353
- Filepath : path ,
365
+ Path : path ,
354
366
SdkName : filepath .Base (filepath .Dir (path )),
355
367
}
356
368
357
- if err := source .checkValid (); err != nil {
369
+ if err := source .Validate (); err != nil {
358
370
return nil , err
359
371
}
360
372
@@ -376,6 +388,105 @@ func NewLuaPlugin(content, path string, manager *Manager) (*LuaPlugin, error) {
376
388
return source , nil
377
389
}
378
390
391
+ // NewLuaPlugin creates a new LuaPlugin instance from the specified directory path.
392
+ // The plugin directory must meet one of the following conditions:
393
+ // - The directory must contain a metadata.lua file and a hooks directory that includes all must be implemented hook functions.
394
+ // - The directory contain a main.lua file that defines the plugin object and all hook functions.
395
+ func NewLuaPlugin (pluginDirPath string , manager * Manager ) (* LuaPlugin , error ) {
396
+ vm := luai .NewLuaVM ()
397
+
398
+ if err := vm .Prepare (& luai.PrepareOptions {
399
+ Config : manager .Config ,
400
+ }); err != nil {
401
+ return nil , err
402
+ }
403
+
404
+ mainPath := filepath .Join (pluginDirPath , "main.lua" )
405
+ // main.lua first
406
+ if util .FileExists (mainPath ) {
407
+ vm .LimitPackagePath (filepath .Join (pluginDirPath , "?.lua" ))
408
+ if err := vm .Instance .DoFile (mainPath ); err != nil {
409
+ return nil , err
410
+ }
411
+ } else {
412
+ // Limit package search scope, hooks directory search priority is higher than lib directory
413
+ hookPath := filepath .Join (pluginDirPath , "hooks" , "?.lua" )
414
+ libPath := filepath .Join (pluginDirPath , "lib" , "?.lua" )
415
+ vm .LimitPackagePath (hookPath , libPath )
416
+
417
+ // load metadata file
418
+ metadataPath := filepath .Join (pluginDirPath , "metadata.lua" )
419
+ if ! util .FileExists (metadataPath ) {
420
+ return nil , fmt .Errorf ("plugin invalid, metadata file not found" )
421
+ }
422
+
423
+ if err := vm .Instance .DoFile (metadataPath ); err != nil {
424
+ return nil , fmt .Errorf ("failed to load meatadata file, %w" , err )
425
+ }
426
+
427
+ // load hook func files
428
+ for _ , hf := range HookFuncMap {
429
+ hp := filepath .Join (pluginDirPath , "hooks" , hf .Filename + ".lua" )
430
+
431
+ if ! hf .Required && ! util .FileExists (hp ) {
432
+ continue
433
+ }
434
+ if err := vm .Instance .DoFile (hp ); err != nil {
435
+ return nil , fmt .Errorf ("failed to load [%s] hook function: %s" , hf .Name , err .Error ())
436
+ }
437
+ }
438
+ }
439
+
440
+ // !!!! Must be set after loading the script to prevent overwriting!
441
+ // set OS_TYPE and ARCH_TYPE
442
+ vm .Instance .SetGlobal (osType , lua .LString (util .GetOSType ()))
443
+ vm .Instance .SetGlobal (archType , lua .LString (util .GetArchType ()))
444
+
445
+ r , err := luai .Marshal (vm .Instance , LuaRuntime {
446
+ OsType : string (util .GetOSType ()),
447
+ ArchType : string (util .GetArchType ()),
448
+ Version : RuntimeVersion ,
449
+ })
450
+ if err != nil {
451
+ return nil , err
452
+ }
453
+ vm .Instance .SetGlobal (runtime , r )
454
+
455
+ pluginObj := vm .Instance .GetGlobal (luaPluginObjKey )
456
+ if pluginObj .Type () == lua .LTNil {
457
+ return nil , fmt .Errorf ("plugin object not found" )
458
+ }
459
+
460
+ PLUGIN := pluginObj .(* lua.LTable )
461
+
462
+ source := & LuaPlugin {
463
+ vm : vm ,
464
+ pluginObj : PLUGIN ,
465
+ Path : pluginDirPath ,
466
+ SdkName : filepath .Base (pluginDirPath ),
467
+ }
468
+
469
+ if err = source .Validate (); err != nil {
470
+ return nil , err
471
+ }
472
+
473
+ pluginInfo := LuaPluginInfo {}
474
+ if err = luai .Unmarshal (PLUGIN , & pluginInfo ); err != nil {
475
+ return nil , err
476
+ }
477
+
478
+ source .LuaPluginInfo = pluginInfo
479
+
480
+ if ! isValidName (source .Name ) {
481
+ return nil , fmt .Errorf ("invalid plugin name" )
482
+ }
483
+
484
+ if source .Name == "" {
485
+ return nil , fmt .Errorf ("no plugin name provided" )
486
+ }
487
+ return source , nil
488
+ }
489
+
379
490
func isValidName (name string ) bool {
380
491
// The regular expression means: start with a letter,
381
492
// followed by any number of letters, digits, or underscores.
0 commit comments