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