117 lines
2.7 KiB
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
|
|
}
|