package module import ( "fmt" "sort" "sync" ) // Registry holds all known modules keyed by Meta.ID. type Registry struct { mu sync.RWMutex modules map[string]Module } // NewRegistry returns an empty registry. func NewRegistry() *Registry { return &Registry{modules: make(map[string]Module)} } // Register adds a module. Returns an error if the ID is already present // or empty. Safe for concurrent use. func (r *Registry) Register(m Module) error { id := m.Meta().ID if id == "" { return fmt.Errorf("module has empty ID") } r.mu.Lock() defer r.mu.Unlock() if _, dup := r.modules[id]; dup { return fmt.Errorf("module %q already registered", id) } r.modules[id] = m return nil } // Get returns the module with the given ID, or (nil, false) if absent. func (r *Registry) Get(id string) (Module, bool) { r.mu.RLock() defer r.mu.RUnlock() m, ok := r.modules[id] return m, ok } // All returns all registered modules sorted by ID for determinism. func (r *Registry) All() []Module { r.mu.RLock() defer r.mu.RUnlock() out := make([]Module, 0, len(r.modules)) for _, m := range r.modules { out = append(out, m) } sort.Slice(out, func(i, j int) bool { return out[i].Meta().ID < out[j].Meta().ID }) return out }