package adapters import ( "context" "fmt" "iop/apps/node/internal/runtime" ) // LifecycleAdapter is an optional interface for adapters that need start/stop lifecycle management. type LifecycleAdapter interface { Start(ctx context.Context) error Stop(ctx context.Context) error } // Registry holds all registered adapters by name. type Registry struct { adapters map[string]runtime.Adapter order []string // insertion order — first registered is the default } func NewRegistry() *Registry { return &Registry{adapters: make(map[string]runtime.Adapter)} } // Register adds an adapter. The first registered adapter becomes the default. func (r *Registry) Register(a runtime.Adapter) { name := a.Name() if _, exists := r.adapters[name]; !exists { r.order = append(r.order, name) } r.adapters[name] = a } func (r *Registry) Get(name string) (runtime.Adapter, bool) { a, ok := r.adapters[name] return a, ok } func (r *Registry) Default() string { if len(r.order) == 0 { return "" } return r.order[0] } // All returns all registered adapters in registration order. func (r *Registry) All() []runtime.Adapter { out := make([]runtime.Adapter, 0, len(r.order)) for _, name := range r.order { out = append(out, r.adapters[name]) } return out } // MustGet returns the adapter or panics — useful during bootstrap validation. func (r *Registry) MustGet(name string) runtime.Adapter { a, ok := r.Get(name) if !ok { panic(fmt.Sprintf("adapter %q not registered", name)) } return a } // Start calls Start on all registered LifecycleAdapters in registration order. // On failure, already-started adapters are stopped in reverse order before returning. func (r *Registry) Start(ctx context.Context) error { started := make([]string, 0, len(r.order)) for _, name := range r.order { lc, ok := r.adapters[name].(LifecycleAdapter) if !ok { continue } if err := lc.Start(ctx); err != nil { for i := len(started) - 1; i >= 0; i-- { if startedLC, ok := r.adapters[started[i]].(LifecycleAdapter); ok { _ = startedLC.Stop(context.Background()) } } return fmt.Errorf("adapter %q start: %w", name, err) } started = append(started, name) } return nil } // Stop calls Stop on all registered LifecycleAdapters in reverse registration order. // All adapters are stopped even if some fail; the first error is returned. func (r *Registry) Stop(ctx context.Context) error { var firstErr error for i := len(r.order) - 1; i >= 0; i-- { name := r.order[i] lc, ok := r.adapters[name].(LifecycleAdapter) if !ok { continue } if err := lc.Stop(ctx); err != nil { if firstErr == nil { firstErr = fmt.Errorf("adapter %q stop: %w", name, err) } } } return firstErr }