verstak-desktop/internal/core/capability/registry.go

117 lines
2.7 KiB
Go

// Package capability provides a registry for plugin capabilities.
package capability
import (
"fmt"
"sort"
"sync"
)
// Registry tracks available capabilities and which plugins provide them.
type Registry struct {
mu sync.RWMutex
capabilities map[string]*Entry // capability name -> entry
}
// Entry represents a capability and its provider.
type Entry struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
PluginID string `json:"pluginId"`
Status string `json:"status"` // "stable", "draft", "deprecated"
}
// NewRegistry creates a new capability registry.
func NewRegistry() *Registry {
return &Registry{
capabilities: make(map[string]*Entry),
}
}
// Register adds a capability provided by a plugin.
func (r *Registry) Register(pluginID string, capabilities []string) error {
r.mu.Lock()
defer r.mu.Unlock()
for _, name := range capabilities {
if existing, ok := r.capabilities[name]; ok {
return fmt.Errorf("capability %q already registered by plugin %q", name, existing.PluginID)
}
r.capabilities[name] = &Entry{
Name: name,
PluginID: pluginID,
Status: "draft",
}
}
return nil
}
// Unregister removes all capabilities provided by a plugin.
func (r *Registry) Unregister(pluginID string) {
r.mu.Lock()
defer r.mu.Unlock()
for name, entry := range r.capabilities {
if entry.PluginID == pluginID {
delete(r.capabilities, name)
}
}
}
// Has checks if a capability is registered.
func (r *Registry) Has(name string) bool {
r.mu.RLock()
defer r.mu.RUnlock()
_, ok := r.capabilities[name]
return ok
}
// Get returns a capability entry by name.
func (r *Registry) Get(name string) *Entry {
r.mu.RLock()
defer r.mu.RUnlock()
return r.capabilities[name]
}
// List returns all registered capabilities, sorted by name.
func (r *Registry) List() []Entry {
r.mu.RLock()
defer r.mu.RUnlock()
entries := make([]Entry, 0, len(r.capabilities))
for _, e := range r.capabilities {
entries = append(entries, *e)
}
sort.Slice(entries, func(i, j int) bool {
return entries[i].Name < entries[j].Name
})
return entries
}
// Available returns the set of available capability names.
func (r *Registry) Available() map[string]bool {
r.mu.RLock()
defer r.mu.RUnlock()
result := make(map[string]bool, len(r.capabilities))
for name := range r.capabilities {
result[name] = true
}
return result
}
// CheckRequired verifies that all required capabilities are present.
// Returns missing capabilities.
func (r *Registry) CheckRequired(required []string) []string {
r.mu.RLock()
defer r.mu.RUnlock()
var missing []string
for _, capName := range required {
if _, ok := r.capabilities[capName]; !ok {
missing = append(missing, capName)
}
}
return missing
}