| 91 |
91 |
|
// module around it calls that.
|
| 92 |
92 |
|
let returns = match &shaped {
|
| 93 |
93 |
|
Shaped::Nodes => quote!(::std::vec::Vec<::quasi_router::Node>),
|
|
94 |
+ |
Shaped::Acts => quote!(::std::vec::Vec<::quasi_router::Act>),
|
| 94 |
95 |
|
Shaped::Single { name, optional } => {
|
| 95 |
96 |
|
let shaped_type = format_ident!("{}", name, span = returns.span());
|
| 96 |
97 |
|
if *optional {
|
| 123 |
124 |
|
/// tab that answers over htmx wraps the same members itself. 52 of the
|
| 124 |
125 |
|
/// population's 484 shapes return this, third after `Slot` and `Node`.
|
| 125 |
126 |
|
Nodes,
|
|
127 |
+ |
/// `Vec<Act>`: a menu, which is the controls a row holds back.
|
|
128 |
+ |
///
|
|
129 |
+ |
/// [`Nodes`](Self::Nodes)' twin and for its reason. `Row::menu` and
|
|
130 |
+ |
/// `Cells::menu` take the whole list, so a menu that is built conditionally
|
|
131 |
+ |
/// has nowhere to accrete, and audiofiles' file list has two of them --
|
|
132 |
+ |
/// what a row offers and what a chosen set does. Every member is an `act`,
|
|
133 |
+ |
/// because a menu is nothing but controls.
|
|
134 |
+ |
Acts,
|
| 126 |
135 |
|
}
|
| 127 |
136 |
|
|
| 128 |
137 |
|
/// The type a shape returns, read off the declaration.
|
| 140 |
149 |
|
let Some(inner) = sole_argument(returns, &last.arguments)? else {
|
| 141 |
150 |
|
return Err(syn::Error::new_spanned(returns, "`Vec` of what?"));
|
| 142 |
151 |
|
};
|
| 143 |
|
- |
if !matches!(shaped_name(inner)?, Shaped::Single { ref name, optional: false } if name == "Node")
|
| 144 |
|
- |
{
|
| 145 |
|
- |
return Err(syn::Error::new_spanned(
|
|
152 |
+ |
return match shaped_name(inner)? {
|
|
153 |
+ |
Shaped::Single {
|
|
154 |
+ |
ref name,
|
|
155 |
+ |
optional: false,
|
|
156 |
+ |
} if name == "Node" => Ok(Shaped::Nodes),
|
|
157 |
+ |
Shaped::Single {
|
|
158 |
+ |
ref name,
|
|
159 |
+ |
optional: false,
|
|
160 |
+ |
} if name == "Act" => Ok(Shaped::Acts),
|
|
161 |
+ |
_ => Err(syn::Error::new_spanned(
|
| 146 |
162 |
|
returns,
|
| 147 |
|
- |
"the only list a shape builds is `Vec<Node>`, which is a panel's members",
|
| 148 |
|
- |
));
|
| 149 |
|
- |
}
|
| 150 |
|
- |
return Ok(Shaped::Nodes);
|
|
163 |
+ |
"a shape builds two lists: `Vec<Node>`, a panel's members, and \
|
|
164 |
+ |
`Vec<Act>`, a menu",
|
|
165 |
+ |
)),
|
|
166 |
+ |
};
|
| 151 |
167 |
|
}
|
| 152 |
168 |
|
if last.ident == "Option" {
|
| 153 |
169 |
|
let Some(inner) = sole_argument(returns, &last.arguments)? else {
|
| 169 |
185 |
|
returns,
|
| 170 |
186 |
|
"a panel with no members is an empty `Vec<Node>`, not a `None`",
|
| 171 |
187 |
|
)),
|
|
188 |
+ |
Shaped::Acts => Err(syn::Error::new_spanned(
|
|
189 |
+ |
returns,
|
|
190 |
+ |
"a menu with no entries is an empty `Vec<Act>`, not a `None`",
|
|
191 |
+ |
)),
|
| 172 |
192 |
|
};
|
| 173 |
193 |
|
}
|
| 174 |
194 |
|
Ok(Shaped::Single {
|
| 202 |
222 |
|
/// an emission produces a value rather than an effect, so the two are
|
| 203 |
223 |
|
/// independent within one block.
|
| 204 |
224 |
|
fn value_body(items: &[Item], shaped: &Shaped, span: Span) -> Result<TokenStream> {
|
|
225 |
+ |
let shaped = match shaped {
|
|
226 |
+ |
Shaped::Nodes => return nodes(items),
|
|
227 |
+ |
Shaped::Acts => return acts(items),
|
|
228 |
+ |
single => single,
|
|
229 |
+ |
};
|
| 205 |
230 |
|
let Shaped::Single {
|
| 206 |
231 |
|
name: shaped,
|
| 207 |
232 |
|
optional,
|
| 208 |
233 |
|
} = shaped
|
| 209 |
234 |
|
else {
|
| 210 |
|
- |
return nodes(items);
|
|
235 |
+ |
unreachable!("the two lists are answered above")
|
| 211 |
236 |
|
};
|
| 212 |
237 |
|
let optional = *optional;
|
| 213 |
238 |
|
let shaped = shaped.as_str();
|
| 1257 |
1282 |
|
}))
|
| 1258 |
1283 |
|
}
|
| 1259 |
1284 |
|
|
|
1285 |
+ |
/// A menu's entries, in order.
|
|
1286 |
+ |
///
|
|
1287 |
+ |
/// [`nodes`]' twin, and narrower: every member is an `act`, because a menu is
|
|
1288 |
+ |
/// nothing but controls. A guard and a loop work the way they do everywhere
|
|
1289 |
+ |
/// else, which is the whole reason this exists -- audiofiles' row menu is
|
|
1290 |
+ |
/// fourteen entries of which nine are conditional.
|
|
1291 |
+ |
fn acts(items: &[Item]) -> Result<TokenStream> {
|
|
1292 |
+ |
let held = format_ident!("entries", span = Span::call_site());
|
|
1293 |
+ |
let statements = act_statements(items, &held)?;
|
|
1294 |
+ |
Ok(quote!({
|
|
1295 |
+ |
let mut #held = ::std::vec::Vec::new();
|
|
1296 |
+ |
#(#statements)*
|
|
1297 |
+ |
#held
|
|
1298 |
+ |
}))
|
|
1299 |
+ |
}
|
|
1300 |
+ |
|
|
1301 |
+ |
fn act_statements(items: &[Item], entries: &proc_macro2::Ident) -> Result<Vec<TokenStream>> {
|
|
1302 |
+ |
items
|
|
1303 |
+ |
.iter()
|
|
1304 |
+ |
.map(|item| match item {
|
|
1305 |
+ |
Item::Bind { name, source } => {
|
|
1306 |
+ |
let value = source_value(source)?;
|
|
1307 |
+ |
Ok(quote!(let #name = #value;))
|
|
1308 |
+ |
}
|
|
1309 |
+ |
Item::For {
|
|
1310 |
+ |
dereferenced,
|
|
1311 |
+ |
binder,
|
|
1312 |
+ |
iterable,
|
|
1313 |
+ |
body,
|
|
1314 |
+ |
} => {
|
|
1315 |
+ |
let iterable = hole(iterable)?;
|
|
1316 |
+ |
let inner = act_statements(body, entries)?;
|
|
1317 |
+ |
let binder = binder_pattern(*dereferenced, binder);
|
|
1318 |
+ |
Ok(quote!(for #binder in #iterable { #(#inner)* }))
|
|
1319 |
+ |
}
|
|
1320 |
+ |
// R9, as everywhere else: the entry is built whether or not the
|
|
1321 |
+ |
// guard places it.
|
|
1322 |
+ |
Item::Emit(Emission::Guarded { guard, inner }) => {
|
|
1323 |
+ |
let test = predicate(guard)?;
|
|
1324 |
+ |
let value = act(inner)?;
|
|
1325 |
+ |
let held = format_ident!("entry", span = Span::call_site());
|
|
1326 |
+ |
Ok(quote!({
|
|
1327 |
+ |
let #held = #value;
|
|
1328 |
+ |
if #test {
|
|
1329 |
+ |
#entries.push(#held);
|
|
1330 |
+ |
}
|
|
1331 |
+ |
}))
|
|
1332 |
+ |
}
|
|
1333 |
+ |
Item::Emit(emission) => {
|
|
1334 |
+ |
let value = act(emission)?;
|
|
1335 |
+ |
Ok(quote!(#entries.push(#value);))
|
|
1336 |
+ |
}
|
|
1337 |
+ |
Item::Attribute { name, .. } => Err(syn::Error::new(
|
|
1338 |
+ |
name.span(),
|
|
1339 |
+ |
"a menu sets nothing: say it on the entry it belongs to",
|
|
1340 |
+ |
)),
|
|
1341 |
+ |
})
|
|
1342 |
+ |
.collect()
|
|
1343 |
+ |
}
|
|
1344 |
+ |
|
| 1260 |
1345 |
|
fn node_statements(items: &[Item], nodes: &proc_macro2::Ident) -> Result<Vec<TokenStream>> {
|
| 1261 |
1346 |
|
items
|
| 1262 |
1347 |
|
.iter()
|