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)
 | 
						|
}
 |