1use std::collections::HashMap;
4
5use topcoat::{Result, context::Cx, view::*};
6
7use super::{
8 Schema,
9 condition::{Condition, watch},
10 embedded::Embedded,
11 fields::{Field, Placement},
12 layouts::{Grid, Group, Section},
13 lenses::FieldResolver,
14};
15use crate::form::FieldErrors;
16
17#[derive(Debug)]
18pub(crate) enum Node {
19 Field(usize),
21 Section(Box<Section>),
22 Group(Box<Group>),
23 Grid(Box<Grid>),
24 Embedded(Box<Embedded>),
25 Unbound(Unbound),
28}
29
30pub(crate) struct Unbound {
32 pub(crate) build: Box<dyn Fn(&FieldResolver) -> Schema + Send + Sync>,
33 pub(crate) value: &'static str,
35}
36
37impl std::fmt::Debug for Unbound {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 f.debug_tuple("Unbound").field(&self.value).finish()
40 }
41}
42
43pub struct Source<'a> {
45 values: &'a HashMap<String, String>,
46 errors: &'a FieldErrors,
47 watched: HashMap<String, Vec<Condition>>,
49 scope: String,
52 options: Option<String>,
54}
55
56impl<'a> Source<'a> {
57 pub fn form(values: &'a HashMap<String, String>, errors: &'a FieldErrors) -> Self {
59 Self {
60 values,
61 errors,
62 watched: HashMap::new(),
63 scope: String::new(),
64 options: None,
65 }
66 }
67
68 pub(crate) fn scoped(mut self, scope: impl Into<String>) -> Self {
71 self.scope = scope.into();
72 self
73 }
74
75 pub(crate) fn options_at(mut self, url: impl Into<String>) -> Self {
77 self.options = Some(url.into());
78 self
79 }
80
81 pub(crate) fn scope(&self) -> &str {
83 &self.scope
84 }
85
86 pub(crate) fn watching(mut self, watched: HashMap<String, Vec<Condition>>) -> Self {
88 self.watched = watched;
89 self
90 }
91
92 fn guard<'v>(
94 &self,
95 cx: &'v Cx,
96 condition: Option<&Condition>,
97 view: BoxView<'v>,
98 ) -> BoxView<'v> {
99 match condition {
100 Some(condition) => condition.guard(cx, &self.scope, self.values, view),
101 None => view,
102 }
103 }
104
105 pub(crate) fn value(&self, name: &str) -> Option<&str> {
106 self.values.get(name).map(String::as_str)
107 }
108
109 pub(crate) fn errors(&self) -> &FieldErrors {
111 self.errors
112 }
113
114 pub(crate) fn options(&self) -> Option<&str> {
116 self.options.as_deref()
117 }
118
119 pub(crate) fn id(&self, name: &str) -> String {
121 self.placement(None).id(name)
122 }
123
124 fn placement<'p>(&'p self, parent: Option<&'p str>) -> Placement<'p> {
126 Placement {
127 parent,
128 scope: &self.scope,
129 options: self.options.as_deref(),
130 }
131 }
132
133 pub(crate) fn error_for(&self, field: &Field) -> Option<String> {
135 self.errors
136 .first(field.name())
137 .map(|error| error.message(field.label_str()))
138 }
139}
140
141impl Node {
142 pub(crate) async fn render<'a>(
144 &self,
145 cx: &'a Cx,
146 fields: &[Field],
147 source: &Source<'_>,
148 ) -> Result<BoxView<'a>> {
149 match self {
150 Node::Field(index) => {
151 let field = &fields[*index];
152 if let Some(repeater) = field.as_repeater() {
154 let view = Box::pin(repeater.render(cx, field, source)).await?;
155 return Ok(source.guard(cx, field.condition(), view));
156 }
157 let error = source.error_for(field);
158 let parent = field.parent_key().and_then(|key| source.value(key));
159 let mut view = Box::pin(field.render_under(
160 cx,
161 source.value(field.name()),
162 error.as_deref(),
163 source.placement(parent),
164 ))
165 .await?;
166 if let Some(conditions) = source.watched.get(field.name()) {
167 view = watch(cx, field, conditions, &source.scope, source.values, view);
168 }
169 Ok(source.guard(cx, field.condition(), view))
170 }
171 Node::Section(s) => {
172 let view = Box::pin(s.render(cx, fields, source)).await?;
173 Ok(source.guard(cx, s.condition.as_ref(), view))
174 }
175 Node::Group(g) => {
176 let view = Box::pin(g.render(cx, fields, source)).await?;
177 Ok(source.guard(cx, g.condition.as_ref(), view))
178 }
179 Node::Grid(g) => {
180 let view = Box::pin(g.render(cx, fields, source)).await?;
181 Ok(source.guard(cx, g.condition.as_ref(), view))
182 }
183 Node::Embedded(e) => Box::pin(e.render(cx, fields, source)).await,
184 Node::Unbound(_) => Ok(().boxed()),
186 }
187 }
188
189 pub(crate) fn children(&self) -> Option<&[Node]> {
192 match self {
193 Node::Section(s) => Some(&s.children.nodes),
194 Node::Group(g) => Some(&g.children.nodes),
195 Node::Grid(g) => Some(&g.children.nodes),
196 Node::Field(_) | Node::Embedded(_) | Node::Unbound(_) => None,
197 }
198 }
199
200 fn children_mut(&mut self) -> Option<&mut Vec<Node>> {
201 match self {
202 Node::Section(s) => Some(&mut s.children.nodes),
203 Node::Group(g) => Some(&mut g.children.nodes),
204 Node::Grid(g) => Some(&mut g.children.nodes),
205 Node::Field(_) | Node::Embedded(_) | Node::Unbound(_) => None,
206 }
207 }
208
209 pub(crate) fn offset(&mut self, by: usize) {
211 match self {
212 Node::Field(index) => *index += by,
213 Node::Embedded(e) => e.offset(by),
214 _ => {
215 for child in self.children_mut().into_iter().flatten() {
216 child.offset(by);
217 }
218 }
219 }
220 }
221}
222
223pub(crate) fn bind_nodes(nodes: &mut Vec<Node>, fields: &mut Vec<Field>, resolver: &FieldResolver) {
226 let mut bound = Vec::with_capacity(nodes.len());
227 for node in std::mem::take(nodes) {
228 let mut node = match node {
229 Node::Unbound(unbound) => {
230 let Schema {
231 nodes: mut built,
232 fields: built_fields,
233 ..
234 } = (unbound.build)(resolver);
235 let offset = fields.len();
236 for node in &mut built {
237 node.offset(offset);
238 }
239 fields.extend(built_fields);
240 bound.extend(built);
241 continue;
242 }
243 node => node,
244 };
245 if let Some(children) = node.children_mut() {
246 bind_nodes(children, fields, resolver);
247 }
248 bound.push(node);
249 }
250 *nodes = bound;
251}
252
253pub(crate) fn unbound_values(nodes: &[Node], out: &mut Vec<&'static str>) {
255 for node in nodes {
256 match node {
257 Node::Unbound(unbound) => out.push(unbound.value),
258 node => unbound_values(node.children().unwrap_or_default(), out),
259 }
260 }
261}
262
263pub(crate) async fn render_nodes<'a>(
265 cx: &'a Cx,
266 nodes: &[Node],
267 fields: &[Field],
268 source: &Source<'_>,
269) -> Result<BoxView<'a>> {
270 let mut views = Vec::with_capacity(nodes.len());
271 for node in nodes {
272 views.push(node.render(cx, fields, source).await?);
273 }
274 Ok(view! {
275 cx =>
276 for v in views {
277 (v)
278 }
279 }
280 .boxed())
281}
282
283#[diagnostic::on_unimplemented(
288 message = "`{Self}` cannot be placed in a `Schema<{F}>`",
289 label = "not a control, layout block, schema, or tuple of 2 to 12 of them for this schema",
290 note = "a resource's form places its record form's own controls, from `controls()`; \
291 `Field::text` and the other constructors build a page's or an action's controls"
292)]
293pub trait IntoSchema<F = ()> {
294 fn into_schema(self) -> Schema<F>;
295}
296
297#[doc(hidden)]
300pub trait Retype {
301 type As<G>;
302
303 fn retype<G>(self) -> Self::As<G>;
304}
305
306impl<F> IntoSchema<F> for Schema<F> {
307 fn into_schema(self) -> Schema<F> {
308 self
309 }
310}
311
312impl IntoSchema for Field {
313 fn into_schema(self) -> Schema {
314 Schema::from_parts(vec![Node::Field(0)], vec![self])
315 }
316}
317
318macro_rules! container_nodes {
321 ($($ty:ident),+ $(,)?) => {
322 $(
323 impl<F> IntoSchema<F> for $ty<Schema<F>> {
324 fn into_schema(self) -> Schema<F> {
325 let mut fields = Vec::new();
326 let block = self.map(|mut children| {
327 fields = std::mem::take(&mut children.fields);
328 children.retype()
329 });
330 Schema::from_parts(vec![Node::$ty(Box::new(block))], fields)
331 }
332 }
333
334 impl<F> IntoSchema<F> for $ty<()> {
335 fn into_schema(self) -> Schema<F> {
336 self.holding(Schema::<F>::default()).into_schema()
337 }
338 }
339 )+
340 };
341}
342
343container_nodes!(Section, Group, Grid);
344
345macro_rules! into_schema_tuples {
347 ($($T:ident => $v:ident),+ $(,)?) => {
348 impl<Form, $($T),+> IntoSchema<Form> for ($($T,)+)
349 where
350 $($T: IntoSchema<Form>,)+
351 {
352 fn into_schema(self) -> Schema<Form> {
353 let ($($v,)+) = self;
354 let mut schema = Schema::default();
355 $( schema.append($v.into_schema()); )+
356 schema
357 }
358 }
359 };
360}
361
362into_schema_tuples!(A => a, B => b);
363into_schema_tuples!(A => a, B => b, C => c);
364into_schema_tuples!(A => a, B => b, C => c, D => d);
365into_schema_tuples!(A => a, B => b, C => c, D => d, E => e);
366into_schema_tuples!(A => a, B => b, C => c, D => d, E => e, F => f);
367into_schema_tuples!(A => a, B => b, C => c, D => d, E => e, F => f, G => g);
368into_schema_tuples!(
369 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h
370);
371into_schema_tuples!(
372 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i
373);
374into_schema_tuples!(
375 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i, J => j
376);
377into_schema_tuples!(
378 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i, J => j, K => k
379);
380into_schema_tuples!(
381 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i, J => j, K => k, L => l
382);