132 lines
2.7 KiB
Go
132 lines
2.7 KiB
Go
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
// All rights reserved.
|
|
|
|
// plugin.go
|
|
package expr
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"plugin"
|
|
"strings"
|
|
)
|
|
|
|
var pluginRegister map[string]*plugin.Plugin
|
|
|
|
func registerPlugin(name string, p *plugin.Plugin) (err error) {
|
|
if pluginExists(name) {
|
|
err = fmt.Errorf("plugin %q already loaded", name)
|
|
} else {
|
|
pluginRegister[name] = p
|
|
}
|
|
return
|
|
}
|
|
|
|
func pluginExists(name string) (exists bool) {
|
|
_, exists = pluginRegister[name]
|
|
return
|
|
}
|
|
|
|
func makePluginName(name string) (decorated string) {
|
|
var template string
|
|
if execName, err := os.Executable(); err != nil || !strings.Contains(execName, "debug") {
|
|
template = "expr-%s-plugin.so"
|
|
} else {
|
|
template = "expr-%s-plugin.so.debug"
|
|
}
|
|
decorated = fmt.Sprintf(template, name)
|
|
return
|
|
}
|
|
|
|
func importPlugin( /*ctx ExprContext,*/ dirList []string, name string) (err error) {
|
|
var filePath string
|
|
var p *plugin.Plugin
|
|
var sym plugin.Symbol
|
|
var moduleName string
|
|
var importFunc func(ExprContext)
|
|
var ok bool
|
|
|
|
decoratedName := makePluginName(name)
|
|
|
|
if filePath, err = makeFilepath(decoratedName, dirList); err != nil {
|
|
return
|
|
}
|
|
|
|
if p, err = plugin.Open(filePath); err != nil {
|
|
return
|
|
}
|
|
|
|
if sym, err = p.Lookup("MODULE_NAME"); err != nil {
|
|
return
|
|
}
|
|
|
|
if moduleName = *sym.(*string); moduleName == "" {
|
|
err = fmt.Errorf("plugin %q does not provide a valid module name", decoratedName)
|
|
return
|
|
}
|
|
|
|
if sym, err = p.Lookup("DEPENDENCIES"); err != nil {
|
|
return
|
|
}
|
|
if deps := *sym.(*[]string); len(deps) > 0 {
|
|
// var count int
|
|
if err = loadModules(dirList, deps); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
if sym, err = p.Lookup("ImportFuncs"); err != nil {
|
|
return
|
|
}
|
|
|
|
if importFunc, ok = sym.(func(ExprContext)); !ok {
|
|
err = fmt.Errorf("plugin %q does not provide a valid import function", decoratedName)
|
|
return
|
|
}
|
|
|
|
registerPlugin(moduleName, p)
|
|
importFunc(globalCtx)
|
|
|
|
return
|
|
}
|
|
|
|
func importPluginFromSearchPath(name any) (count int, err error) {
|
|
var moduleSpec any
|
|
|
|
dirList := buildSearchDirList("plugin", ENV_EXPR_PLUGIN_PATH)
|
|
count = 0
|
|
it := NewAnyIterator(name)
|
|
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
|
|
if module, ok := moduleSpec.(string); ok {
|
|
if err = importPlugin(dirList, module); err != nil {
|
|
break
|
|
}
|
|
count++
|
|
} else {
|
|
err = fmt.Errorf("expected string as item nr %d, got %s", it.Index()+1, TypeName(moduleSpec))
|
|
break
|
|
}
|
|
}
|
|
if err == io.EOF {
|
|
err = nil
|
|
}
|
|
return
|
|
}
|
|
|
|
func loadModules(dirList []string, moduleNames []string) (err error) {
|
|
for _, name := range moduleNames {
|
|
if err1 := importPlugin(dirList, name); err1 != nil {
|
|
if !ImportInContext(name) {
|
|
err = err1
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func init() {
|
|
pluginRegister = make(map[string]*plugin.Plugin)
|
|
}
|