Four delivery parts each wrote the same wiring of the seat-list reader (start from the bootstrap, give the catalogue what it holds, read again when events go live, two tools over a reader set later). seatlist.Part is that wiring, its tools named by the part's prefix and its again answered to the callers the part gives (ADR 0636 §7), so W4-W8 delete their copies. Builds: design 47 G §204, §246 Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
111 lines
3.7 KiB
Go
111 lines
3.7 KiB
Go
package seatlist
|
|
|
|
import (
|
|
"errors"
|
|
"sync/atomic"
|
|
|
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
)
|
|
|
|
// A part that reads the seat list wires its reader one way (novox/hq ADR 0636 §1, §7): started from the bootstrapped
|
|
// definitions, what it holds given to catalogue at once and at every change, read again when the part's path for the
|
|
// seat's events becomes live, and its two tools `<prefix>_seat_list` and `<prefix>_seat_list_again` served over whatever
|
|
// reader the part holds at the call. Part is that wiring, so no part writes it again; the part names its tools' prefix
|
|
// and the callers that may read again, which are its own to give (stdio.VerbCallers' entries).
|
|
|
|
// Part is a part's reader of the seat list, set once Follow starts it; before, the list counts as unread and the part
|
|
// acts on nothing new.
|
|
type Part struct {
|
|
prefix string
|
|
callers []string
|
|
reader atomic.Pointer[Reader]
|
|
}
|
|
|
|
// NewPart is the wiring of a part whose tools are named `<prefix>_seat_list` and `<prefix>_seat_list_again`, the second
|
|
// answered only to callers.
|
|
func NewPart(prefix string, callers []string) *Part {
|
|
return &Part{prefix: prefix, callers: append([]string(nil), callers...)}
|
|
}
|
|
|
|
// Follow starts the reader over bus from the bootstrapped definitions: what it holds is given to catalogue at once and at
|
|
// every change, and changed, when given, is called after each change (the part acts again). A reader that cannot
|
|
// subscribe is said through logf and still held, from the bootstrap. It returns the reader.
|
|
func (p *Part) Follow(bus Bus, changed func(), logf func(string, ...any)) *Reader {
|
|
reader, err := Start(Config{Bus: bus, Bootstrap: Bootstrapped(),
|
|
OnChange: func(seats []catalogue.Seat) {
|
|
catalogue.UseSeats(seats)
|
|
if changed != nil {
|
|
changed()
|
|
}
|
|
}})
|
|
if err != nil && logf != nil {
|
|
logf("%s cannot follow its seat list, so it acts only on the seats the bootstrapped definitions name: %v", p.prefix, err)
|
|
}
|
|
if reader != nil {
|
|
catalogue.UseSeats(reader.Seats())
|
|
p.reader.Store(reader)
|
|
}
|
|
return reader
|
|
}
|
|
|
|
// Reader is the reader Follow started, or nil before.
|
|
func (p *Part) Reader() *Reader {
|
|
if p == nil {
|
|
return nil
|
|
}
|
|
return p.reader.Load()
|
|
}
|
|
|
|
// MayAct is the reader's MayAct, false before it is started.
|
|
func (p *Part) MayAct(seats ...string) bool {
|
|
r := p.Reader()
|
|
return r != nil && r.MayAct(seats...)
|
|
}
|
|
|
|
// Heard says the seat has answered the reader (Status's Heard), false before it is started.
|
|
func (p *Part) Heard() bool {
|
|
r := p.Reader()
|
|
return r != nil && r.Status().Heard
|
|
}
|
|
|
|
// Health is why the list is not read, or empty when it is.
|
|
func (p *Part) Health() string {
|
|
r := p.Reader()
|
|
if r == nil {
|
|
return "the seat list reader has not started yet"
|
|
}
|
|
return r.Status().Health()
|
|
}
|
|
|
|
// EventsLive is called when the part's path for the seat's events is heard: while the list is unread it is read now, on a
|
|
// goroutine of its own (Reader.ReloadIfUnread).
|
|
func (p *Part) EventsLive() {
|
|
if r := p.Reader(); r != nil {
|
|
r.ReloadIfUnread()
|
|
}
|
|
}
|
|
|
|
// ToolNames are the two tools' names.
|
|
func (p *Part) ToolNames() (read, again string) {
|
|
return p.prefix + "_seat_list", p.prefix + "_seat_list_again"
|
|
}
|
|
|
|
// Tools are the reader's two tools (Tools) over whatever reader the part holds at the call: before Follow, each says the
|
|
// reader has not started.
|
|
func (p *Part) Tools() []stdio.Tool {
|
|
read, again := p.ToolNames()
|
|
shape := Tools(&Reader{}, read, again, p.callers)
|
|
for i := range shape {
|
|
run := i
|
|
shape[i].Run = func(args map[string]any) (any, error) {
|
|
r := p.Reader()
|
|
if r == nil {
|
|
return nil, errors.New("the seat list reader has not started yet: the runtime has not served this module")
|
|
}
|
|
return Tools(r, read, again, p.callers)[run].Run(args)
|
|
}
|
|
}
|
|
return shape
|
|
}
|