107 lines
3.4 KiB
Go
107 lines
3.4 KiB
Go
package stdio
|
|
|
|
import (
|
|
"encoding/json"
|
|
"time"
|
|
)
|
|
|
|
// State — the current value of something a module keeps on the bus, which every machine sees,
|
|
// including one that joins later (novox/hq ADR 0201). Not history: that is an event. Never a secret,
|
|
// sealed or not. A module declares what it keeps (`state`) and what it reads (`reads`, as
|
|
// `<module>.<name>`) and names each here as it named it there; the runtime refuses, with the reason,
|
|
// what it did not declare.
|
|
|
|
// StateEntry is one key's current value.
|
|
type StateEntry struct {
|
|
Key string `json:"key"`
|
|
Value json.RawMessage `json:"value"`
|
|
Revision uint64 `json:"revision"`
|
|
}
|
|
|
|
// StateChange is one change a watch hands over: a key put or deleted.
|
|
type StateChange struct {
|
|
State string `json:"state"`
|
|
Key string `json:"key"`
|
|
Op string `json:"op"`
|
|
Value json.RawMessage `json:"value,omitempty"`
|
|
Revision uint64 `json:"revision"`
|
|
// Current is true for a value that was there when the watch began, false for a change since.
|
|
Current bool `json:"current"`
|
|
}
|
|
|
|
// KeptState is one state this module keeps, or reads.
|
|
type KeptState struct{ name string }
|
|
|
|
// State is a state this module keeps (by its local name) or reads (`<module>.<name>`).
|
|
func State(name string) KeptState { return KeptState{name: name} }
|
|
|
|
const stateTimeout = 30 * time.Second
|
|
|
|
func (s KeptState) ask(verb string, params map[string]any) (json.RawMessage, error) {
|
|
c, err := live()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params["state"] = s.name
|
|
return c.ask("mesh/state."+verb, params, stateTimeout)
|
|
}
|
|
|
|
// Get is a key's current value, or nil when it has none.
|
|
func (s KeptState) Get(key string) (*StateEntry, error) {
|
|
raw, err := s.ask("get", map[string]any{"key": key})
|
|
if err != nil || len(raw) == 0 || string(raw) == "null" {
|
|
return nil, err
|
|
}
|
|
var e StateEntry
|
|
return &e, json.Unmarshal(raw, &e)
|
|
}
|
|
|
|
// Put writes a key — the owner's instances only — and returns the revision written.
|
|
func (s KeptState) Put(key string, value any) (uint64, error) {
|
|
raw, err := s.ask("put", map[string]any{"key": key, "value": value})
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
var put struct {
|
|
Revision uint64 `json:"revision"`
|
|
}
|
|
return put.Revision, json.Unmarshal(raw, &put)
|
|
}
|
|
|
|
// Delete removes a key — the owner's instances only. A key that was not there is not an error.
|
|
func (s KeptState) Delete(key string) error {
|
|
_, err := s.ask("delete", map[string]any{"key": key})
|
|
return err
|
|
}
|
|
|
|
// Keys is every key with a current value, sorted.
|
|
func (s KeptState) Keys() ([]string, error) {
|
|
raw, err := s.ask("keys", map[string]any{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var keys []string
|
|
return keys, json.Unmarshal(raw, &keys)
|
|
}
|
|
|
|
// Watch hands handle every current value — none that is deleted — and then every change, in order,
|
|
// and returns once the current values have all been handled. key narrows it: a key, with `*` for one
|
|
// name and `**` for the rest; empty is every key. The watch lasts as long as the process.
|
|
func (s KeptState) Watch(key string, handle func(StateChange) error) error {
|
|
c, err := live()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
id := c.newID()
|
|
c.mu.Lock()
|
|
c.watches[id] = handle
|
|
c.mu.Unlock()
|
|
if _, err := c.askAs(id, "mesh/state.watch", map[string]any{"state": s.name, "key": key}, stateTimeout); err != nil {
|
|
c.mu.Lock()
|
|
delete(c.watches, id)
|
|
c.mu.Unlock()
|
|
return err
|
|
}
|
|
return nil
|
|
}
|