Skip to main content

max / quasi

Give the bench the declared screen it is meant to be staging The staged column measures a `describe` written for the spike against a `Data` seam. The screen it copies is declared three times over in MNW's library_contacts, so the bench now holds that port verbatim. Two things the declaration has and the copy does not, and both are why the port was worth making. Every table sits behind a guard, so the branch case staging.rs names as its real constraint is exercised rather than assumed away. And a shape reaches another by `include`, so a residual has to compose rather than be one flat program. The copy also dropped four settings: `navigating` on a profile link, and `confirm`, `tone Danger` and `awaiting` on Revoke. The port keeps them and the test asserts each, so the difference is a record instead of something the next reader rediscovers.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session
https://claude.ai/code/session_01P8ostB2UmZJGj5WjSHRSot
Author: Max Johnson <me@maxj.phd> · 2026-09-05 15:08 UTC
Signed with PGP, not checked
Commit: e1d1c32258d37609f091a90f9c3a234eb87c4e35
Parent: 8e71fe5
4 files changed, +209 insertions, -4 deletions
M Cargo.lock +5 -4
@@ -3515,6 +3515,7 @@
3515 3515 "makeover-layout",
3516 3516 "makeover-tui",
3517 3517 "makeover-webview",
3518 + "quasi-declare",
3518 3519 "quasi-http",
3519 3520 "quasi-immediate",
3520 3521 "quasi-router",
@@ -6284,6 +6285,10 @@
6284 6285 name = "quasi-type"
6285 6286 version = "0.1.3"
6286 6287
6288 + [[patch.unused]]
6289 + name = "synckit-client"
6290 + version = "0.10.0"
6291 +
6287 6292 [[patch.unused]]
6288 6293 name = "kberg"
6289 6294 version = "0.1.0"
@@ -6299,7 +6304,3 @@
6299 6304 [[patch.unused]]
6300 6305 name = "tagtree"
6301 6306 version = "0.4.1"
6302 -
6303 - [[patch.unused]]
6304 - name = "synckit-client"
6305 - version = "0.10.0"
@@ -34,6 +34,10 @@
34 34 # webview is measured through. Taken directly rather than through quasi-webview
35 35 # because a bench calling a trait method should name the trait it calls.
36 36 quasi-http = { path = "../quasi-http", version = "0.101.1" }
37 + # The macro under measurement. The bench holds the declared copy of the same
38 + # screen `staging.rs` writes by hand, so the staged column measures what the
39 + # server actually serves rather than a shape written for a benchmark.
40 + quasi-declare = { path = "../quasi-declare", version = "0.1" }
37 41 makeover-layout = "0.44"
38 42 # The renderer's own escaper, so the staged path fills a hole exactly the way
39 43 # `Node::Text` does and the guarantee is not quietly reimplemented.
@@ -97,6 +97,7 @@
97 97 //! Both columns still answer the question this bench exists for, which is what
98 98 //! a row costs, and that is read down a column.
99 99
100 + mod declared;
100 101 mod fixture;
101 102 mod immediate;
102 103 mod staging;
@@ -1,0 +1,199 @@
1 + //! The same screen, declared.
2 + //!
3 + //! [`crate::staging`] writes MNW's `library_contacts` pane by hand, against a
4 + //! `Data` seam invented for the spike. This is the screen as the server
5 + //! actually holds it: a verbatim port of the three `declare!` blocks in
6 + //! `MNW/server/src/quasi/library_contacts.rs`, against the bench's own rows.
7 + //!
8 + //! Two things it has that the hand-written copy does not, and both are the
9 + //! point. Every table is placed behind a guard, so the branch case the spike's
10 + //! module doc calls its real constraint is exercised here rather than assumed
11 + //! away. And a shape reaches another shape by `include`, so the residual has to
12 + //! compose rather than being one flat program.
13 + //!
14 + //! Kept verbatim rather than tidied. A bench measuring a shape written for the
15 + //! bench measures the bench.
16 +
17 + use quasi_declare::declare;
18 + use quasi_router::Node;
19 +
20 + use crate::fixture::{Buyer, Rows, Shared};
21 +
22 + /// The region the tab nav targets, as the server names it.
23 + const REGION: &str = "tab-content";
24 +
25 + /// This screen's own nest, which the revoke control addresses under.
26 + const PATH: &str = "/library/tabs/contacts";
27 +
28 + declare! {
29 + /// Everything inside the tab pane.
30 + shape pane(buyers: &[Buyer], shared: &[Shared]) -> Node;
31 +
32 + region REGION as Pane {
33 + empty "No contacts yet." when buyers.is_empty() and shared.is_empty();
34 +
35 + section "Your Buyers ({buyers.len()})" unless buyers.is_empty();
36 + text "Buyers who opted to share their email with you at purchase time."
37 + unless buyers.is_empty();
38 + include buyers_table(buyers) unless buyers.is_empty();
39 +
40 + section "Shared With" unless shared.is_empty();
41 + text "You've shared your email with these creators. You can revoke sharing at any time."
42 + unless shared.is_empty();
43 + include shared_table(shared) unless shared.is_empty();
44 + }
45 + }
46 +
47 + declare! {
48 + /// The buyers who shared an email.
49 + shape buyers_table(buyers: &[Buyer]) -> Node;
50 +
51 + table {
52 + column "Username" {
53 + width Content;
54 + priority Essential;
55 + }
56 + column "Email" {
57 + width Fill;
58 + priority Essential;
59 + }
60 + column "Purchases" {
61 + width Content;
62 + }
63 + column "Total Spent" {
64 + width Content;
65 + }
66 + column "Last Purchase" {
67 + width Content;
68 + priority Optional;
69 + }
70 +
71 + for buyer in buyers.iter() {
72 + cells {
73 + cell buyer.username.clone() {
74 + activate to get "/u/{buyer.username}" navigating;
75 + }
76 + cell buyer.email.clone() {
77 + activate to external "mailto:{buyer.email}";
78 + }
79 + cell buyer.purchases.clone();
80 + cell buyer.spent.clone();
81 + cell buyer.last_purchase.clone();
82 + }
83 + }
84 + }
85 + }
86 +
87 + declare! {
88 + /// The creators this reader has shared an email with.
89 + shape shared_table(shared: &[Shared]) -> Node;
90 +
91 + table {
92 + column "Creator" {
93 + width Fill;
94 + priority Essential;
95 + }
96 + column "" {
97 + width Content;
98 + priority Essential;
99 + }
100 +
101 + for creator in shared.iter() {
102 + cells {
103 + cell creator.name.clone() {
104 + activate to get "/u/{creator.username}" navigating;
105 + }
106 + cell "" {
107 + act "Revoke" to delete "{PATH}/revoke/{creator.seller_id}" awaiting {
108 + confirm "Revoke contact sharing with {creator.username}?";
109 + tone Danger;
110 + }
111 + }
112 + }
113 + }
114 + }
115 + }
116 +
117 + /// The pane, built from real rows the way a request builds it.
118 + #[must_use]
119 + pub fn describe(rows: &Rows) -> Node {
120 + pane(&rows.buyers, &rows.shared)
121 + }
122 +
123 + #[cfg(test)]
124 + mod tests {
125 + use quasi_http::Serves as _;
126 + use quasi_webview::Webview;
127 +
128 + use super::*;
129 + use crate::fixture::SIZES;
130 +
131 + /// The port is faithful to the server's screen, which is not the same
132 + /// thing as agreeing with [`crate::staging`].
133 + ///
134 + /// The hand-written copy is the impoverished one. It was written for the
135 + /// spike against a `Data` seam and dropped four things the declaration
136 + /// carries, each asserted below so that the difference is a record rather
137 + /// than a surprise the next reader has to rediscover:
138 + ///
139 + /// - `navigating` on a profile link, which drops the htmx swap because a
140 + /// profile is a whole document rather than a region.
141 + /// - `confirm`, `tone Danger` and `awaiting` on the Revoke control.
142 + ///
143 + /// Everything the acceptance rule protects does agree: every address, the
144 + /// actions and their methods, and the heading structure. That is checked
145 + /// here, and the bytes deliberately are not.
146 + #[test]
147 + fn the_declared_pane_carries_what_the_hand_written_one_dropped() {
148 + let webview = Webview::new();
149 + let rows = Rows::new(5);
150 + let hand = webview.fragment(&crate::fixture::pane(&rows));
151 + let real = webview.fragment(&describe(&rows));
152 +
153 + // What the declaration says and the copy could not.
154 + assert!(real.contains(r#"data-tone="danger""#), "{real}");
155 + assert!(real.contains("hx-confirm="), "{real}");
156 + assert!(real.contains(r#"data-awaiting="indeterminate""#), "{real}");
157 + assert!(!hand.contains("hx-confirm="), "{hand}");
158 +
159 + // `navigating`: the anchor is the whole of it, so no swap is asked for.
160 + assert!(!real.contains(r#"hx-get="/u/buyer0000""#), "{real}");
161 + assert!(hand.contains(r#"hx-get="/u/buyer0000""#), "{hand}");
162 +
163 + // What must survive regardless, which is the acceptance rule.
164 + for size in SIZES {
165 + let rows = Rows::new(size);
166 + let real = webview.fragment(&describe(&rows));
167 + for i in 0..size {
168 + assert!(real.contains(&format!(r#"href="/u/buyer{i:04}""#)), "{i}");
169 + assert!(
170 + real.contains(&format!(r#"href="mailto:buyer{i:04}@example.com""#)),
171 + "{i}"
172 + );
173 + assert!(
174 + real.contains(&format!(
175 + r#"hx-delete="/library/tabs/contacts/revoke/{:08}""#,
176 + i * 31 + 7
177 + )),
178 + "{i}"
179 + );
180 + }
181 + assert_eq!(
182 + real.matches("table-row").count(),
183 + size * 2,
184 + "at {size} rows"
185 + );
186 + assert!(real.contains(&format!("Your Buyers ({size})")), "at {size}");
187 + }
188 + }
189 +
190 + /// The guards the hand-written copy has no way to carry.
191 + #[test]
192 + fn no_rows_places_the_empty_line_and_neither_table() {
193 + let html = Webview::new().fragment(&describe(&Rows::new(0)));
194 +
195 + assert!(html.contains("No contacts yet."), "{html}");
196 + assert!(!html.contains("Your Buyers"), "{html}");
197 + assert!(!html.contains("Shared With"), "{html}");
198 + }
199 + }