Files
inkwell/core.manager.go
mr.zeroandClaude Opus 5 08f15a7e3d
ci/woodpecker/push/woodpecker Pipeline was successful
Manager.Set and Manager.All
Set is the deliberate overwrite: it replaces a registered entry in place,
keeping its position in the insertion order, and falls back to Register
when the name is new. Register keeps panicking on duplicates.

All returns every entry in insertion order; orderedWidgets is now just
that call, and reversedWidgets builds on it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 09:50:28 +02:00

117 lines
2.8 KiB
Go

package inkwell
import "sort"
// Named is implemented by every entity that can be registered into a Manager.
type Named interface {
GetName() string
}
// Manager is the single registry shape used by every entity type. Each entity
// kind gets a named alias (ItemManager = Manager[Item], etc.). Insertion
// order is preserved — Names() returns it. SortedNames() returns alphabetical.
type Manager[T Named] struct {
items map[string]T
order []string
}
func NewManager[T Named]() *Manager[T] {
return &Manager[T]{items: make(map[string]T)}
}
// Register adds v to the registry. Panics on empty or duplicate name —
// these are construction-time bugs, not runtime conditions.
func (m *Manager[T]) Register(v T) {
name := v.GetName()
if name == "" {
panic("inkwell: Register: empty Name on " + typeName(v))
}
if _, dup := m.items[name]; dup {
panic("inkwell: Register: duplicate " + typeName(v) + " name: " + name)
}
m.items[name] = v
m.order = append(m.order, name)
}
// Set replaces a registered entry in place, keeping its position in the
// registration order, and registers the entry when the name is new. Register
// panics on a duplicate; Set is the deliberate overwrite.
func (m *Manager[T]) Set(v T) {
name := v.GetName()
if _, ok := m.items[name]; !ok {
m.Register(v)
return
}
m.items[name] = v
}
func (m *Manager[T]) Get(name string) (T, bool) {
v, ok := m.items[name]
return v, ok
}
func (m *Manager[T]) MustGet(name string) T {
v, ok := m.items[name]
if !ok {
panic("inkwell: MustGet: unknown name: " + name)
}
return v
}
func (m *Manager[T]) Has(name string) bool {
_, ok := m.items[name]
return ok
}
func (m *Manager[T]) Len() int { return len(m.items) }
// Names returns the registered names in insertion order. Use SortedNames
// when iteration order needs to be stable across runs.
func (m *Manager[T]) Names() []string {
return append([]string(nil), m.order...)
}
// All returns every registered entry in insertion order.
func (m *Manager[T]) All() []T {
out := make([]T, len(m.order))
for i, n := range m.order {
out[i] = m.items[n]
}
return out
}
func (m *Manager[T]) SortedNames() []string {
names := append([]string(nil), m.order...)
sort.Strings(names)
return names
}
func (m *Manager[T]) Each(fn func(T)) {
for _, n := range m.order {
fn(m.items[n])
}
}
// Remove drops a registered entry. Used by hot-reload / dynamic UI changes.
// Silently no-ops on unknown names.
func (m *Manager[T]) Remove(name string) {
if _, ok := m.items[name]; !ok {
return
}
delete(m.items, name)
for i, n := range m.order {
if n == name {
m.order = append(m.order[:i], m.order[i+1:]...)
return
}
}
}
func typeName(v any) string {
type namer interface{ TypeLabel() string }
if n, ok := v.(namer); ok {
return n.TypeLabel()
}
return "entity"
}