Compare commits

...

1 Commits

Author SHA1 Message Date
wpetit 8b85f60e63 WIP: extension feature 2020-11-17 09:19:15 +01:00
9 changed files with 179 additions and 0 deletions

1
example/extendable/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/bin

View File

@ -0,0 +1,11 @@
build: extension
go build -o ./bin/app ./
extension:
go build -o ./bin/myext.so -buildmode=plugin ./myext
watch:
modd
run:
./bin/app

View File

@ -0,0 +1,23 @@
package main
import (
"context"
"log"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/extension"
)
func main() {
reg := extension.NewRegistry()
ctx := context.Background()
extensions, err := reg.LoadAll(ctx, "./bin/*.so")
if err != nil {
log.Fatal(errors.WithStack(err))
}
for _, ext := range extensions {
log.Printf("Loaded extension '%s', version '%s'", ext.ExtensionName(), ext.ExtensionVersion())
}
}

View File

@ -0,0 +1,6 @@
**/*.go
modd.conf
Makefile {
prep: make build
prep: make run
}

View File

@ -0,0 +1,12 @@
package main
type MyExtension struct {
}
func (e *MyExtension) ExtensionName() string {
return "my.extension"
}
func (e *MyExtension) ExtensionVersion() string {
return "0.0.0"
}

View File

@ -0,0 +1,11 @@
package main
import (
"context"
"gitlab.com/wpetit/goweb/extension"
)
func RegisterExtension(ctx context.Context) (extension.Extension, error) {
return &MyExtension{}, nil
}

8
extension/error.go Normal file
View File

@ -0,0 +1,8 @@
package extension
import "errors"
var (
ErrInvalidRegisterFunc = errors.New("invalid register func")
ErrInvalidExtension = errors.New("invalid extension")
)

6
extension/extension.go Normal file
View File

@ -0,0 +1,6 @@
package extension
type Extension interface {
ExtensionName() string
ExtensionVersion() string
}

101
extension/registry.go Normal file
View File

@ -0,0 +1,101 @@
package extension
import (
"context"
"path/filepath"
"plugin"
"sync"
"github.com/pkg/errors"
)
type Registry struct {
extensions map[string]Extension
mutex sync.RWMutex
}
func (r *Registry) Load(ctx context.Context, path string) (Extension, error) {
p, err := plugin.Open(path)
if err != nil {
return nil, errors.WithStack(err)
}
registerFuncSymbol, err := p.Lookup("RegisterExtension")
if err != nil {
return nil, errors.WithStack(err)
}
register, ok := registerFuncSymbol.(func(context.Context) (Extension, error))
if !ok {
return nil, errors.WithStack(ErrInvalidRegisterFunc)
}
ext, err := register(ctx)
if err != nil {
return nil, errors.WithStack(err)
}
if ext == nil {
return nil, errors.WithStack(ErrInvalidExtension)
}
r.mutex.Lock()
r.extensions[ext.ExtensionName()] = ext
r.mutex.Unlock()
return ext, nil
}
func (r *Registry) LoadAll(ctx context.Context, pattern string) ([]Extension, error) {
extensions := make([]Extension, 0)
matches, err := filepath.Glob(pattern)
if err != nil {
return nil, errors.WithStack(err)
}
for _, m := range matches {
ext, err := r.Load(ctx, m)
if err != nil {
return nil, errors.WithStack(err)
}
extensions = append(extensions, ext)
}
return extensions, nil
}
func (r *Registry) Extensions() []Extension {
r.mutex.RLock()
defer r.mutex.RUnlock()
extensions := make([]Extension, 0, len(r.extensions))
for _, e := range r.extensions {
extensions = append(extensions, e)
}
return extensions
}
type ExtensionFilterFunc func(ext Extension) bool
func (r *Registry) Filter(filter ExtensionFilterFunc) []Extension {
r.mutex.RLock()
defer r.mutex.RUnlock()
extensions := make([]Extension, 0, len(r.extensions))
for _, e := range r.extensions {
if filter(e) {
extensions = append(extensions, e)
}
}
return extensions
}
func NewRegistry() *Registry {
return &Registry{
extensions: make(map[string]Extension),
}
}