; persist/upcast — event schema evolution. An append-only log keeps events
; forever, so old events have old shapes. Rather than migrate stored data (you
; can't rewrite history) or branch every projection on version, register an
; upcaster per event type: a pure (event -> event) that lifts an old event to
; the current shape. Reads pass through the registry so projections see ONE
; shape. The registry is an immutable dict the consumer threads (no global
; mutable state). Requires: lib/persist/event.sx, lib/persist/log.sx.

(define persist/upcasters (fn () {}))
(define persist/register-upcaster (fn (reg type fn) (assoc reg type fn)))

; apply the registered upcaster for an event's type, or pass it through unchanged
(define
  persist/upcast
  (fn
    (reg e)
    (let ((f (get reg (persist/event-type e)))) (if f (f e) e))))

; read a stream with every event lifted to current shape
(define
  persist/read-upcast
  (fn
    (b stream reg)
    (map (fn (e) (persist/upcast reg e)) (persist/read b stream))))

; project over upcasted events — projections never see a legacy shape
(define
  persist/project-upcast
  (fn
    (b stream reg step seed)
    (reduce step seed (persist/read-upcast b stream reg))))

; helper: upcast an event's :data by merging in/overriding fields, keeping the
; record's stream/seq/type/at. Common upcaster body.
(define
  persist/upcast-data
  (fn
    (e new-data)
    (persist/event
      (persist/event-stream e)
      (persist/event-seq e)
      (persist/event-type e)
      (persist/event-at e)
      (merge (persist/event-data e) new-data))))
