Skip to main content

max / quasi

Let a list say what is left over `Rest` had a constructor and no spelling: `Node::list` builds a list with no `more`, so a paged list could not be described. goingson's mail list is the site, and it is the first consumer with a remainder it actually knows -- `list_threaded` hands back the page and the total in one call, so the description carries both numbers and `Rest` derives the difference. `more` is the one thing a list sets rather than says on a row, because it is about the list and not about any row in it. It takes a guard, so a page that is the whole of it says nothing.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session
https://claude.ai/code/session_01MptwXZ8k65v19rFmdGAyki
Author: Max Johnson <me@maxj.phd> · 2026-09-04 19:49 UTC
Signed with PGP, not checked
Commit: 23c219d9eb42ba1877f9262ba1ecaf5b49a0f36f
Parent: c8e6997
1 file changed, +45 insertions, -5 deletions
@@ -1231,17 +1231,57 @@
1231 1231 }
1232 1232
1233 1233 /// The rows of a list, built where they are read.
1234 + ///
1235 + /// `more` is the one thing a list sets rather than says on a row, because it is
1236 + /// about the list and not about any row in it: what is left over, and the
1237 + /// address that fetches it. goingson's mail list is the site -- the query hands
1238 + /// back the page and the total in one call, so the description carries both
1239 + /// numbers and `Rest` derives the difference.
1234 1240 fn list(items: &[Item]) -> Result<TokenStream> {
1235 1241 let rows = format_ident!("rows", span = Span::call_site());
1236 - let statements = list_statements(items, &rows)?;
1242 + let rest = format_ident!("rest", span = Span::call_site());
1243 + let mut more = Vec::new();
1244 + let mut body = Vec::new();
1245 + for item in items {
1246 + match item {
1247 + Item::Attribute { name, args, guard } if name == "more" => {
1248 + let [only] = args.as_slice() else {
1249 + return Err(syn::Error::new(
1250 + name.span(),
1251 + "`more` says what is left over: one `Rest`",
1252 + ));
1253 + };
1254 + let value = arg(only)?;
1255 + let setting = quote!(#rest = ::std::option::Option::Some(#value););
1256 + more.push(match guard {
1257 + None => setting,
1258 + Some(guard) => {
1259 + let test = predicate(guard)?;
1260 + quote!(if #test { #setting })
1261 + }
1262 + });
1263 + }
1264 + other => body.push(other),
1265 + }
1266 + }
1267 + let statements = list_statements(&body, &rows)?;
1268 + if more.is_empty() {
1269 + return Ok(quote!({
1270 + let mut #rows = ::std::vec::Vec::new();
1271 + #(#statements)*
1272 + ::quasi_router::Node::list(#rows)
1273 + }));
1274 + }
1237 1275 Ok(quote!({
1238 1276 let mut #rows = ::std::vec::Vec::new();
1239 1277 #(#statements)*
1240 - ::quasi_router::Node::list(#rows)
1278 + let mut #rest = ::std::option::Option::None;
1279 + #(#more)*
1280 + ::quasi_router::Node::List { rows: #rows, more: #rest }
1241 1281 }))
1242 1282 }
1243 1283
1244 - fn list_statements(items: &[Item], rows: &proc_macro2::Ident) -> Result<Vec<TokenStream>> {
1284 + fn list_statements(items: &[&Item], rows: &proc_macro2::Ident) -> Result<Vec<TokenStream>> {
1245 1285 items
1246 1286 .iter()
1247 1287 .map(|item| match item {
@@ -1256,7 +1296,7 @@
1256 1296 body,
1257 1297 } => {
1258 1298 let iterable = hole(iterable)?;
1259 - let inner = list_statements(body, rows)?;
1299 + let inner = list_statements(&body.iter().collect::<Vec<_>>(), rows)?;
1260 1300 let binder = binder_pattern(*dereferenced, binder);
1261 1301 Ok(quote!(for #binder in #iterable { #(#inner)* }))
1262 1302 }
@@ -1278,7 +1318,7 @@
1278 1318 }
1279 1319 Item::Attribute { name, .. } => Err(syn::Error::new(
1280 1320 name.span(),
1281 - "a list has nothing to set: say it on the row",
1321 + "a list sets nothing but `more`: say it on the row",
1282 1322 )),
1283 1323 })
1284 1324 .collect()