package adapters import ( "errors" "fmt" "log/slog" "strings" "sync" ) // Factory creates a Provider from a provider type string and JSON config. // The registry uses factories to lazily instantiate providers from DB rows. type Factory func(id string, config map[string]any) (Provider, error) // Registry manages provider instances keyed by their DB id. // It caches instantiated providers and uses registered factories to create // them on demand from stored configuration. type Registry struct { mu sync.RWMutex instances map[string]Provider factories map[string]Factory logger *slog.Logger } func NewRegistry(log *slog.Logger) *Registry { if log == nil { log = slog.Default() } return &Registry{ instances: map[string]Provider{}, factories: map[string]Factory{}, logger: log.With(slog.String("component", "memory_provider_registry")), } } // RegisterFactory registers a factory for a given provider type (e.g. "builtin"). func (r *Registry) RegisterFactory(providerType string, factory Factory) { r.mu.Lock() defer r.mu.Unlock() r.factories[strings.TrimSpace(providerType)] = factory } // Register adds a pre-built provider instance by ID. func (r *Registry) Register(id string, provider Provider) { r.mu.Lock() defer r.mu.Unlock() r.instances[strings.TrimSpace(id)] = provider } // Get returns the provider for the given DB record ID. func (r *Registry) Get(id string) (Provider, error) { id = strings.TrimSpace(id) if id == "" { return nil, errors.New("provider id is required") } r.mu.RLock() p, ok := r.instances[id] r.mu.RUnlock() if ok { return p, nil } return nil, fmt.Errorf("memory provider not found: %s", id) } // Instantiate creates a provider from a DB row and caches it. // If the instance already exists, it is returned directly. func (r *Registry) Instantiate(id, providerType string, config map[string]any) (Provider, error) { id = strings.TrimSpace(id) providerType = strings.TrimSpace(providerType) r.mu.RLock() if p, ok := r.instances[id]; ok { r.mu.RUnlock() return p, nil } r.mu.RUnlock() r.mu.Lock() defer r.mu.Unlock() // Double-check after acquiring write lock. if p, ok := r.instances[id]; ok { return p, nil } factory, ok := r.factories[providerType] if !ok { return nil, fmt.Errorf("unknown memory provider type: %s", providerType) } p, err := factory(id, config) if err != nil { return nil, fmt.Errorf("instantiate memory provider %s (%s): %w", id, providerType, err) } r.instances[id] = p return p, nil } // Remove evicts a cached provider instance (e.g. after config update or delete). func (r *Registry) Remove(id string) { r.mu.Lock() defer r.mu.Unlock() delete(r.instances, strings.TrimSpace(id)) }