;; content-on-sx — document metadata (title / slug / tags). ;; ;; CtDoc carries optional metadata alongside its blocks (ivars declared in ;; doc.sx). Reads go through message dispatch; setters are copy-on-write ;; (functional st-iv-set!), consistent with the immutable document model. ;; ;; Requires (loaded by harness): block.sx, doc.sx. ;; ── reads ── (define doc-title (fn (doc) (st-send doc "title" (list)))) (define doc-slug (fn (doc) (st-send doc "slug" (list)))) (define doc-tags (fn (doc) (let ((t (st-send doc "tags" (list)))) (if (= t nil) (list) t)))) (define doc-meta (fn (doc) {:slug (doc-slug doc) :id (doc-id doc) :title (doc-title doc) :tags (doc-tags doc)})) ;; ── copy-on-write setters ── (define doc-with-title (fn (doc title) (st-iv-set! doc "title" title))) (define doc-with-slug (fn (doc slug) (st-iv-set! doc "slug" slug))) (define doc-with-tags (fn (doc tags) (st-iv-set! doc "tags" tags))) (define doc-add-tag (fn (doc tag) (doc-with-tags doc (append (doc-tags doc) (list tag))))) ;; set several at once: meta is a dict with optional :title :slug :tags (define doc-with-meta (fn (doc meta) (let ((d1 (if (has-key? meta :title) (doc-with-title doc (get meta :title)) doc))) (let ((d2 (if (has-key? meta :slug) (doc-with-slug d1 (get meta :slug)) d1))) (if (has-key? meta :tags) (doc-with-tags d2 (get meta :tags)) d2))))) ;; constructor with metadata (define doc-new-meta (fn (id blocks meta) (doc-with-meta (doc-new id blocks) meta))) ;; ── content/* facade aliases ── (define content/title doc-title) (define content/slug doc-slug) (define content/tags doc-tags) (define content/meta doc-meta) (define content/with-title doc-with-title) (define content/with-slug doc-with-slug) (define content/with-tags doc-with-tags) (define content/with-meta doc-with-meta)