aicra/internal/checker/public.go

137 lines
2.8 KiB
Go
Raw Normal View History

2018-05-22 17:56:55 +00:00
package checker
import (
"fmt"
"io/ioutil"
"log"
2018-05-22 17:56:55 +00:00
"plugin"
"strings"
)
// CreateRegistry creates an empty type registry
// - if loadDir is True if will load all available types
// inside the local ./types folder
2018-07-08 23:34:21 +00:00
func CreateRegistry(loadDir ...string) *Registry {
/* (1) Create registry */
2018-07-08 23:34:21 +00:00
reg := &Registry{
2018-05-22 17:56:55 +00:00
Types: make([]Type, 0),
}
/* (2) If no default to use -> empty registry */
if len(loadDir) < 1 {
return reg
}
/* (3) List types */
plugins, err := ioutil.ReadDir(loadDir[0])
if err != nil {
log.Fatal(err)
}
/* (4) Else try to load each given default */
for _, file := range plugins {
// ignore non .so files
if !strings.HasSuffix(file.Name(), ".so") {
continue
}
err := reg.Add(file.Name())
if err != nil {
log.Fatalf("Cannot load plugin '%s'", file.Name())
}
}
return reg
2018-05-22 17:56:55 +00:00
}
// Add adds a type to the registry; it must be a
// valid and existing plugin name with or without the .so extension
// it must be located in the relative directory ./types
2018-07-08 23:37:57 +00:00
func (reg *Registry) Add(pluginName string) error {
2018-05-22 17:56:55 +00:00
/* (1) Check plugin name */
if len(pluginName) < 1 {
return fmt.Errorf("Plugin name must not be empty")
}
/* (2) Check if valid plugin name */
if strings.ContainsAny(pluginName, "/") {
return fmt.Errorf("'%s' can only be a name, not a path", pluginName)
}
/* (3) Check plugin extension */
if !strings.HasSuffix(pluginName, ".so") {
pluginName = fmt.Sprintf("%s.so", pluginName)
2018-05-22 17:56:55 +00:00
}
/* (4) Try to load the plugin */
p, err := plugin.Open(fmt.Sprintf(".build/type/%s", pluginName))
2018-05-22 17:56:55 +00:00
if err != nil {
return err
}
/* (5) Export wanted properties */
2018-05-22 17:56:55 +00:00
matcher, err := p.Lookup("Match")
if err != nil {
return fmt.Errorf("Missing method 'Match()'; %s", err)
}
checker, err := p.Lookup("Check")
if err != nil {
return fmt.Errorf("Missing method 'Check()'; %s", err)
}
/* (6) Cast Match+Check */
2018-05-22 17:56:55 +00:00
matcherCast, ok := matcher.(func(string) bool)
if !ok {
return fmt.Errorf("Match() is malformed")
}
checkerCast, ok := checker.(func(interface{}) bool)
if !ok {
return fmt.Errorf("Check() is malformed")
}
/* (7) Add type to registry */
2018-07-08 23:37:57 +00:00
reg.Types = append(reg.Types, Type{
2018-05-22 17:56:55 +00:00
Match: matcherCast,
Check: checkerCast,
})
return nil
}
2018-07-08 23:34:21 +00:00
// Run finds a type checker from the registry matching the type @typeName
// and uses this checker to check the @value. If no type checker matches
// the @typeName name, error is returned by default.
2018-07-08 23:37:57 +00:00
func (reg Registry) Run(typeName string, value interface{}) error {
2018-05-22 17:56:55 +00:00
2018-07-08 23:34:21 +00:00
var T *Type
2018-05-22 17:56:55 +00:00
/* (1) Iterate to find matching type (take first) */
2018-07-08 23:37:57 +00:00
for _, t := range reg.Types {
2018-05-22 17:56:55 +00:00
// stop if found
2018-07-08 23:34:21 +00:00
if t.Match(typeName) {
2018-05-22 17:56:55 +00:00
T = &t
break
}
}
/* (2) Abort if no matching type */
if T == nil {
return fmt.Errorf("No matching type")
2018-05-22 17:56:55 +00:00
}
/* (3) Check */
if !T.Check(value) {
return fmt.Errorf("Does not match")
}
return nil
2018-05-22 17:56:55 +00:00
}