Files
mesh-catalog/modules/power/cmd/power/moments.go
T

71 lines
1.9 KiB
Go

package main
import (
"regexp"
"strings"
)
// A piece is one module's code for one moment, as the controller placed it (novox/hq ADR 0204 §3,
// ADR 0211 §3): under a line naming the module, in module order.
type Piece struct {
Module string `json:"module"`
Lines int `json:"lines"`
Code string `json:"code"`
}
// marker is the line the controller writes before each module's code: "# <module>", nothing else.
var marker = regexp.MustCompile(`^# ([a-z0-9][a-z0-9-]*)$`)
// Pieces splits a moment file into its modules' pieces, exactly as the runner does: a marker line
// opens a piece; what comes before the first marker is the holder's own header and runs nowhere.
func Pieces(content string) []Piece {
var out []Piece
var cur *Piece
var b strings.Builder
flush := func() {
if cur != nil {
cur.Code = b.String()
cur.Lines = strings.Count(cur.Code, "\n")
out = append(out, *cur)
}
b.Reset()
}
for _, line := range strings.SplitAfter(content, "\n") {
if m := marker.FindStringSubmatch(strings.TrimRight(line, "\n")); m != nil {
flush()
cur = &Piece{Module: m[1]}
continue
}
if cur != nil {
b.WriteString(line)
}
}
flush()
return out
}
// Hooks is every moment's pieces on this machine, read from the files the controller placed.
func (m *Machine) Hooks() map[string]any {
moments := map[string]any{}
total := 0
for _, moment := range Moments {
p := MomentsDir + "/" + moment
if !m.exists(p) {
moments[moment] = map[string]any{"placed": false}
continue
}
pieces := Pieces(m.read(p) + "\n")
total += len(pieces)
moments[moment] = map[string]any{"placed": true, "pieces": orEmpty(pieces)}
}
return map[string]any{"moments": moments, "pieces": total,
"note": "each piece runs on its own with sh as root, bounded (MESH_POWER_BOUND, default 30 s); outcomes are in the journal under mesh-power"}
}
func orEmpty[T any](s []T) []T {
if s == nil {
return []T{}
}
return s
}