; persist/view — a materialized view: the consumer-facing read model. It bundles
; a stream, a fold (step + seed) and a snapshot name. Attached to a hub it
; refreshes incrementally on every publish, so the materialized value stays
; current on write and reads are O(1) snapshot loads (persist/view-peek) instead
; of a full fold. This is what feed indices, mod audit rollups, search counters,
; etc. sit on. Requires: lib/persist/snapshot.sx, lib/persist/subscribe.sx.

(define persist/view (fn (name stream step seed) {:name name :step step :stream stream :seed seed}))
(define persist/view-name (fn (v) (get v :name)))
(define persist/view-stream (fn (v) (get v :stream)))

; bring the view's snapshot up to date with the log tail; returns the state
(define
  persist/view-refresh
  (fn
    (b v)
    (persist/checkpoint
      b
      (get v :stream)
      (get v :name)
      (get v :step)
      (get v :seed))))

; current materialized value — refreshes first, so never stale
(define
  persist/view-value
  (fn (b v) (persist/project-value (persist/view-refresh b v))))

; O(1) read of the last persisted snapshot value WITHOUT folding the tail. Equal
; to view-value when the view is attached (kept current on every publish);
; otherwise may lag the log by the un-refreshed tail.
(define
  persist/view-peek
  (fn
    (b v)
    (persist/project-value
      (persist/snapshot-load b (get v :name) (get v :seed)))))

; attach to a hub: refresh the view on every publish to its stream
(define
  persist/view-attach
  (fn
    (h v)
    (begin
      (persist/subscribe
        h
        (persist/view-stream v)
        (fn (bk s e) (persist/view-refresh bk v)))
      h)))
