goweb/plugin/registry.go

118 lines
2.0 KiB
Go

package plugin
import (
"context"
"path/filepath"
"plugin"
"sync"
"github.com/pkg/errors"
)
type Registry struct {
plugins map[string]Plugin
mutex sync.RWMutex
}
func (r *Registry) Add(plg Plugin) {
r.mutex.Lock()
defer r.mutex.Unlock()
r.plugins[plg.PluginName()] = plg
}
func (r *Registry) Get(name string) (Plugin, error) {
r.mutex.RLock()
defer r.mutex.RUnlock()
plg, exists := r.plugins[name]
if !exists {
return nil, errors.WithStack(ErrPluginNotFound)
}
return plg, nil
}
func (r *Registry) Load(ctx context.Context, path string) (Plugin, error) {
p, err := plugin.Open(path)
if err != nil {
return nil, errors.WithStack(err)
}
registerFuncSymbol, err := p.Lookup("RegisterPlugin")
if err != nil {
return nil, errors.WithStack(err)
}
register, ok := registerFuncSymbol.(func(context.Context) (Plugin, error))
if !ok {
return nil, errors.WithStack(ErrInvalidRegisterFunc)
}
plg, err := register(ctx)
if err != nil {
return nil, errors.WithStack(err)
}
if plg == nil {
return nil, errors.WithStack(ErrInvalidPlugin)
}
r.Add(plg)
return plg, nil
}
func (r *Registry) LoadAll(ctx context.Context, pattern string) ([]Plugin, error) {
extensions := make([]Plugin, 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) Plugins() []Plugin {
r.mutex.RLock()
defer r.mutex.RUnlock()
plugins := make([]Plugin, 0, len(r.plugins))
for _, p := range r.plugins {
plugins = append(plugins, p)
}
return plugins
}
type IteratorFunc func(plg Plugin) error
func (r *Registry) Each(iterator IteratorFunc) error {
r.mutex.RLock()
defer r.mutex.RUnlock()
for _, p := range r.plugins {
if err := iterator(p); err != nil {
return errors.WithStack(err)
}
}
return nil
}
func NewRegistry() *Registry {
return &Registry{
plugins: make(map[string]Plugin),
}
}