max / quasi
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
- Claude-Session
- https://claude.ai/code/session_01MptwXZ8k65v19rFmdGAyki
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() |