Files
commandkit/registry.go

85 lines
2.3 KiB
Go

package commandkit
import (
"errors"
"fmt"
)
// Register adds a batch of declarations to the application. The complete batch
// is validated before registry mutation; on error the registry and its derived
// command metadata remain unchanged. A successful nonempty batch becomes
// visible to later invocations. Declarations remain reusable.
func (a *App) Register(declarations ...Declaration) error {
return a.installBatch(declarations...)
}
func (a *App) installBatch(declarations ...Declaration) error {
if a == nil {
return fmt.Errorf("cannot register commands on a nil app")
}
commands := make([]Command, 0, len(declarations))
rules := make([]RoutingRule, 0)
for _, declaration := range declarations {
switch value := declaration.(type) {
case Command:
commands = append(commands, value)
case *Command:
if value == nil {
return fmt.Errorf("nil *Command declaration")
}
commands = append(commands, *value)
case RoutingRule:
rules = append(rules, value)
case *RoutingRule:
if value == nil {
return fmt.Errorf("nil *RoutingRule declaration")
}
rules = append(rules, *value)
case nil:
return fmt.Errorf("nil declaration")
default:
return fmt.Errorf("unsupported declaration type %T", declaration)
}
}
pending := make(map[string]*commandNode, len(commands))
for _, command := range commands {
if err := validateCommandPlacement(command); err != nil {
return err
}
if command.record.absent {
continue
}
name := command.record.name
if _, exists := a.commands[name]; exists {
return fmt.Errorf("command path %q is already registered", name)
}
if _, exists := pending[name]; exists {
return fmt.Errorf("command path %q is already registered", name)
}
pending[name] = &commandNode{command: command}
}
for _, rule := range rules {
if rule.record == nil {
return fmt.Errorf("invalid zero routing rule declaration")
}
if len(rule.record.issues) > 0 {
return fmt.Errorf("routing rule: %w", errors.Join(rule.record.issues...))
}
}
if len(pending) == 0 && len(rules) == 0 {
return nil
}
if a.commands == nil {
a.commands = make(map[string]*commandNode, len(pending))
}
for name, node := range pending {
a.commands[name] = node
}
a.routingRules = append(a.routingRules, rules...)
if len(pending) > 0 {
a.snapshot = nil
}
return nil
}