- Adapter registry 코드 리뷰 및 계획 파일 업데이트 - Node adapters factory, registry 수정 - Node runtime 및 router 개선 - 테스트 파일 업데이트
137 lines
4 KiB
Go
137 lines
4 KiB
Go
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
|
|
}
|
|
|
|
type entry struct {
|
|
typeName string
|
|
adapter runtime.Adapter
|
|
}
|
|
|
|
// Registry holds all registered adapters keyed by instance key.
|
|
type Registry struct {
|
|
entries map[string]entry
|
|
order []string // insertion order for lifecycle and diagnostics
|
|
}
|
|
|
|
func NewRegistry() *Registry {
|
|
return &Registry{entries: make(map[string]entry)}
|
|
}
|
|
|
|
// Register adds an adapter using a.Name() as both instance key and type name.
|
|
// This preserves backward compatibility for callers that register by type.
|
|
func (r *Registry) Register(a runtime.Adapter) {
|
|
r.RegisterKeyed(a.Name(), a.Name(), a)
|
|
}
|
|
|
|
// RegisterKeyed adds an adapter with an explicit instance key and type name.
|
|
// If key is empty, typeName is used as the key. Duplicate keys replace the
|
|
// existing entry but preserve insertion order.
|
|
func (r *Registry) RegisterKeyed(key, typeName string, a runtime.Adapter) {
|
|
if key == "" {
|
|
key = typeName
|
|
}
|
|
if _, exists := r.entries[key]; !exists {
|
|
r.order = append(r.order, key)
|
|
}
|
|
r.entries[key] = entry{typeName: typeName, adapter: a}
|
|
}
|
|
|
|
// Get returns the adapter registered under the exact instance key.
|
|
func (r *Registry) Get(key string) (runtime.Adapter, bool) {
|
|
e, ok := r.entries[key]
|
|
return e.adapter, ok
|
|
}
|
|
|
|
// Lookup returns an adapter by exact instance key first. If no exact match is
|
|
// found it falls back to type-name lookup: succeeds when exactly one instance
|
|
// of that type is registered, and returns an ambiguous error when multiple
|
|
// instances share the same type name.
|
|
func (r *Registry) Lookup(name string) (runtime.Adapter, error) {
|
|
if e, ok := r.entries[name]; ok {
|
|
return e.adapter, nil
|
|
}
|
|
var matchKeys []string
|
|
for _, k := range r.order {
|
|
if r.entries[k].typeName == name {
|
|
matchKeys = append(matchKeys, k)
|
|
}
|
|
}
|
|
switch len(matchKeys) {
|
|
case 0:
|
|
return nil, fmt.Errorf("adapter %q not found", name)
|
|
case 1:
|
|
return r.entries[matchKeys[0]].adapter, nil
|
|
default:
|
|
return nil, fmt.Errorf("adapter %q is ambiguous: matches instance keys %v; use an instance key", name, matchKeys)
|
|
}
|
|
}
|
|
|
|
// All returns all registered adapters in registration order.
|
|
func (r *Registry) All() []runtime.Adapter {
|
|
out := make([]runtime.Adapter, 0, len(r.order))
|
|
for _, key := range r.order {
|
|
out = append(out, r.entries[key].adapter)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// MustGet returns the adapter or panics — useful during bootstrap validation.
|
|
func (r *Registry) MustGet(name string) runtime.Adapter {
|
|
a, err := r.Lookup(name)
|
|
if err != nil {
|
|
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 _, key := range r.order {
|
|
lc, ok := r.entries[key].adapter.(LifecycleAdapter)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if err := lc.Start(ctx); err != nil {
|
|
for i := len(started) - 1; i >= 0; i-- {
|
|
if startedLC, ok := r.entries[started[i]].adapter.(LifecycleAdapter); ok {
|
|
_ = startedLC.Stop(context.Background())
|
|
}
|
|
}
|
|
return fmt.Errorf("adapter %q start: %w", key, err)
|
|
}
|
|
started = append(started, key)
|
|
}
|
|
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-- {
|
|
key := r.order[i]
|
|
lc, ok := r.entries[key].adapter.(LifecycleAdapter)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if err := lc.Stop(ctx); err != nil {
|
|
if firstErr == nil {
|
|
firstErr = fmt.Errorf("adapter %q stop: %w", key, err)
|
|
}
|
|
}
|
|
}
|
|
return firstErr
|
|
}
|