package main import ( "context" "encoding/json" "fmt" "github.com/novox/mesh-controller/internal/conditions" "github.com/novox/mesh-controller/internal/inventory" ) // The looks of the open conditions — how often each was observed, when last, its newest evidence — are // kept in the controller's store, not the conditions bucket (novox/hq issue 420, ADR 0289 §1): the bucket // is written when what a condition says changes, never for a look that saw it as it was. // storeLooks keeps the conditions' looks in the inventory. type storeLooks struct{ inv *inventory.Inventory } func (s storeLooks) KeepLooks(ctx context.Context, key string, l conditions.Looks) error { evidence, err := json.Marshal(l.Evidence) if err != nil { return err } return s.inv.KeepConditionLooks(ctx, inventory.ConditionLooks{Key: key, Observations: l.Observations, LastObserved: l.LastObserved, Evidence: evidence}) } func (s storeLooks) Looked(ctx context.Context, key string) (conditions.Looks, bool, error) { row, kept, err := s.inv.ConditionLooksOf(ctx, key) if err != nil || !kept { return conditions.Looks{}, kept, err } l, err := looksOf(row) return l, err == nil, err } func (s storeLooks) AllLooks(ctx context.Context) (map[string]conditions.Looks, error) { rows, err := s.inv.AllConditionLooks(ctx) if err != nil { return nil, err } out := make(map[string]conditions.Looks, len(rows)) for key, row := range rows { l, err := looksOf(row) if err != nil { return nil, err } out[key] = l } return out, nil } func (s storeLooks) ForgetLooks(ctx context.Context, key string) error { return s.inv.ForgetConditionLooks(ctx, key) } func looksOf(row inventory.ConditionLooks) (conditions.Looks, error) { l := conditions.Looks{Observations: row.Observations, LastObserved: row.LastObserved.UTC()} if len(row.Evidence) > 0 { if err := json.Unmarshal(row.Evidence, &l.Evidence); err != nil { return conditions.Looks{}, fmt.Errorf("the evidence kept for the condition %s cannot be read: %w", row.Key, err) } } return l, nil } // looksHere is where a process that is not the serving controller reads the conditions' looks: the store, // opened for it, closed by the function returned. A store that cannot be opened is said on every condition // read through it, never read as none. func looksHere(ctx context.Context) (conditions.LookStore, func()) { inv, err := openInventory(ctx) if err != nil { return conditions.NoLooks{Err: err}, func() {} } return storeLooks{inv}, inv.Close }