package stdio import ( "encoding/json" "fmt" "os" "strings" "sync" "git.novox.be/novox/mesh-sdk/go/announce" ) // What this bundle said it keeps (novox/hq ADR 0495 clause 2; issue 1790). var ( keepsMu sync.Mutex kept map[string]bool // nil until Keeps: no state is refused then announced []byte // the announcement, published once the bundle serves ) // Keeps says on the bus, as this bundle's module, every state it keeps and which of them others may read (novox/hq // ADR 0495 clause 2): what a reader is granted is what the owner announced (clause 3). Call it before Serve; the // announcement is published through the runtime once the bundle serves, or at once when it already does. From then // on a state of the module's own that it did not announce is refused at the call, naming it, so code and announcement // cannot drift (clause 5). It refuses a state no bucket could be named by and a state given twice. func Keeps(states ...announce.State) error { module := os.Getenv("MESH_MODULE") if module == "" { module = os.Getenv(ServedAs) } data, err := announce.Encode(announce.States{Module: module, Node: os.Getenv("MESH_NODE"), States: states}) if err != nil { return err } names := map[string]bool{} for _, s := range states { names[s.Name] = true } keepsMu.Lock() kept, announced = names, data keepsMu.Unlock() currentMu.Lock() serving := current != nil currentMu.Unlock() if serving { return announce1() } return nil } // announce1 publishes the announcement Keeps made, if any. func announce1() error { keepsMu.Lock() data := announced keepsMu.Unlock() if data == nil { return nil } return Emit(announce.Event, json.RawMessage(data)) } // refusedUnannounced is the error of a call on a state of the module's own that Keeps did not name, or nil. A read of // another module's state (`.`) is the grant's business. func refusedUnannounced(name string) error { if strings.Contains(name, ".") { return nil } keepsMu.Lock() defer keepsMu.Unlock() if kept != nil && !kept[name] { return fmt.Errorf("the state %q is not among the states this module announced it keeps (stdio.Keeps)", name) } return nil } // forgetKeeps is for a test: nothing announced. func forgetKeeps() { keepsMu.Lock() kept, announced = nil, nil keepsMu.Unlock() }