package main import ( "encoding/json" "fmt" "sort" "strings" "sync" stdio "git.novox.be/novox/mesh-sdk/go" ) // memBucket is a state in memory, with the bus's revisions and its refusal of a lost race. type memBucket struct { mu sync.Mutex values map[string][]byte revs map[string]uint64 rev uint64 writes []string // "op key", in order fail map[string]error } func newBucket() *memBucket { return &memBucket{values: map[string][]byte{}, revs: map[string]uint64{}, fail: map[string]error{}} } func (b *memBucket) Get(key string) (*stdio.StateEntry, error) { b.mu.Lock() defer b.mu.Unlock() if err := b.fail["get "+key]; err != nil { return nil, err } v, ok := b.values[key] if !ok { return nil, nil } return &stdio.StateEntry{Key: key, Value: json.RawMessage(v), Revision: b.revs[key]}, nil } func (b *memBucket) write(op, key string, value any) (uint64, error) { if err := b.fail[op+" "+key]; err != nil { return 0, err } data, err := json.Marshal(value) if err != nil { return 0, err } b.rev++ b.values[key], b.revs[key] = data, b.rev b.writes = append(b.writes, op+" "+key) return b.rev, nil } func (b *memBucket) Create(key string, value any) (uint64, error) { b.mu.Lock() defer b.mu.Unlock() if _, ok := b.values[key]; ok { return 0, fmt.Errorf("%s: %w", key, stdio.ErrChanged) } return b.write("create", key, value) } func (b *memBucket) Update(key string, value any, revision uint64) (uint64, error) { b.mu.Lock() defer b.mu.Unlock() if b.revs[key] != revision { return 0, fmt.Errorf("%s: %w", key, stdio.ErrChanged) } return b.write("update", key, value) } func (b *memBucket) Put(key string, value any) (uint64, error) { b.mu.Lock() defer b.mu.Unlock() return b.write("put", key, value) } func (b *memBucket) Delete(key string) error { b.mu.Lock() defer b.mu.Unlock() if err := b.fail["delete "+key]; err != nil { return err } delete(b.values, key) delete(b.revs, key) b.writes = append(b.writes, "delete "+key) return nil } func (b *memBucket) Keys() ([]string, error) { b.mu.Lock() defer b.mu.Unlock() keys := []string{} for k := range b.values { keys = append(keys, k) } sort.Strings(keys) return keys, nil } // value reads a key into its shape; a missing key is the zero value and false. func (b *memBucket) value(key string, v any) bool { b.mu.Lock() defer b.mu.Unlock() raw, ok := b.values[key] if ok { if err := json.Unmarshal(raw, v); err != nil { panic(err) } } return ok } // writesTo are the writes to keys starting with prefix. func (b *memBucket) writesTo(prefix string) []string { b.mu.Lock() defer b.mu.Unlock() var out []string for _, w := range b.writes { if _, key, _ := strings.Cut(w, " "); strings.HasPrefix(key, prefix) { out = append(out, w) } } return out } // sentEvent is one event a fake sink was handed. type sentEvent struct { Name string Body any } type eventLog struct { mu sync.Mutex sent []sentEvent } func (e *eventLog) Emit(name string, body any, _ ...stdio.EmitOptions) error { e.mu.Lock() defer e.mu.Unlock() e.sent = append(e.sent, sentEvent{name, body}) return nil } func (e *eventLog) names() []string { e.mu.Lock() defer e.mu.Unlock() var out []string for _, s := range e.sent { out = append(out, s.Name) } return out }