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