Files
mesh-sdk/go/catalogue/seat_data.go
T
jschoubben 1c7356c247
mesh/delivery-group group feat/sdk1 rejected: composed together: the checker's verdict at the members' heads (428d048dd50a): unit: git.novox.be/novox/mesh-sdk/…
mesh/delivery closed: a newer head of the same pull request
mesh/check failed: unit: git.novox.be/novox/mesh-sdk/go/process.TestStatePutWritesAKeyWhole: a Put through a short blackout: 0, mesh-mail's state send…
catalogue: the composition's rules and refusals leave for the node-engine, so mesh-sdk renders no declaration and refuses no composition (ADR 0593 §7)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 19:19:25 +01:00

145 lines
5.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package catalogue
import (
"math"
"regexp"
"sort"
)
// Shape is what a kind received as data looks like.
type Shape struct {
// Places are the fields a holder narrows its placeholders by, each with every value it takes.
Places []PlaceField
// Order is the whole-number field that orders the pieces within one placeholder (lowest first,
// then module order); absent in a piece means OrderDefault.
Order string
// Check is what else is wrong with one piece's data, beyond its placing fields and its order.
Check func(data map[string]any) []string `json:"-"`
// Examples are pieces every holder's template must render, one for each variant the shape has,
// so a holder that cannot render one is refused at registration and not found on a machine.
Examples []map[string]any
// Reads is the state on the bus a piece shows, each a state and the one key of it, or nothing
// (novox/hq ADR 0260): a contributor offers only state it keeps itself, and the holder on the same
// machine is granted to read that key, so the holder's manifest names no contributor.
Reads func(data map[string]any) []StateRead `json:"-"`
// Text says every piece of this kind is, rendered, text that a collector may place as a collection of items
// (design 57 L3): a collector then names the kind as `${collected:contribution:<seat>:<kind>:<field>=<value>,…}`.
Text bool
}
// PlaceField is one field a holder places by, and the values it takes.
type PlaceField struct {
Field string
Values []string
}
// OrderDefault is a piece's order when it gives none: the middle of 0–99.
const OrderDefault = 50
// KnownCapabilities is every capability the node-engine detects (mesh-host internal/profile), the
// names an `if-capability` may give (novox/hq ADR 0255). A name nothing detects would leave its
// contribution out on every machine, silently, so it is refused. A detector added there is added here.
var KnownCapabilities = []string{
"battery", "container-groups", "container-runtime", "file-references", "firewall", "graphical-session", "overlay",
"package-manager",
"power-meter", "privileged", "seat", "service-manager", "uplink-dhcpcd", "uplink-networkmanager",
"uplink-systemd-networkd", "user-namespaces", "virtualisation",
}
// StateRead is one key of another module's state that a holder reads: Read is `<module>.<name>`,
// and Key the one key, empty for the holder's own machine until ReadsGranted names it.
type StateRead struct {
Read, Key string
}
// stateKeyName is a key a piece may name: a machine's name is one.
var stateKeyName = regexp.MustCompile(`^[a-z0-9][a-z0-9-]*$`)
// ReadsGranted is the state a holder reads because modules on its machine offer it in pieces of a
// kind its seat receives as data (novox/hq ADR 0260), each with the one key the piece shows — the
// machine's own when it names none — sorted and once each. A module's items of such a kind (design 57 L3) grant
// what they show too, always at the machine's own key; an item whose capability the machine lacks grants a key
// that is never written, which is no wider than its own machine's. The bus grants the holder these keys and
// no others: the holder names no contributor, and reads only what is offered to it where it runs.
func ReadsGranted(holder Manifest, onMachine []Manifest, machine string) []StateRead {
seen := map[StateRead]bool{}
for _, claim := range holder.Claims {
s, known := SeatNamed(claim.Name)
if !known {
continue
}
for _, r := range s.Receives {
if r.Shape == nil || r.Shape.Reads == nil {
continue
}
for _, m := range onMachine {
for _, c := range m.allContributions() {
if cs, ok := SeatNamed(c.Seat); !ok || cs.Name != s.Name || c.Kind != r.Kind {
continue
}
for _, sr := range r.Shape.Reads(c.Data) {
if sr.Key == "" {
sr.Key = machine
}
if module, _, err := ReadState(sr.Read); err == nil && module == m.Module && module != holder.Module &&
stateKeyName.MatchString(sr.Key) {
seen[sr] = true
}
}
}
// A moved contributor's items of the kind (design 57 L3): what each shows, read from its attributes as a
// contribution's data, and always this machine's own key (ADR 0260), never another's (issue 774:
// power's draw block became an item and i3status-rust was issued nothing).
for _, it := range m.Items {
if it.Collection != "contribution:"+s.Name+":"+r.Kind {
continue
}
for _, sr := range r.Shape.Reads(itemData(it)) {
sr.Key = machine
if module, _, err := ReadState(sr.Read); err == nil && module == m.Module && module != holder.Module &&
stateKeyName.MatchString(sr.Key) {
seen[sr] = true
}
}
}
}
}
}
out := make([]StateRead, 0, len(seen))
for sr := range seen {
out = append(out, sr)
}
sort.Slice(out, func(i, j int) bool {
if out[i].Read != out[j].Read {
return out[i].Read < out[j].Read
}
return out[i].Key < out[j].Key
})
return out
}
// itemData is an item's attributes in the shape of a contribution's data: each at the top and among the options, where
// a shape's Reads looks for what a piece shows (barBlockReads: shows, options.state).
func itemData(it Item) map[string]any {
data, options := map[string]any{}, map[string]any{}
for k, v := range it.Attributes {
data[k], options[k] = v, v
}
data["options"] = options
return data
}
// wholeIn is v as a whole number within [lo, hi]; JSON gives every number as a float.
func wholeIn(v any, lo, hi int) (int, bool) {
f, ok := v.(float64)
if !ok {
if i, isInt := v.(int); isInt {
f, ok = float64(i), true
}
}
if !ok || f != math.Trunc(f) || f < float64(lo) || f > float64(hi) {
return 0, false
}
return int(f), true
}