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::{FieldError, FieldErrors, FormValue, FormValues};
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 FormValues,
46 errors: &'a FieldErrors,
47 watched: HashMap<String, Vec<Condition>>,
49 scope: String,
52 options: Option<String>,
54 editing: bool,
56}
57
58impl<'a> Source<'a> {
59 pub fn form(values: &'a FormValues, errors: &'a FieldErrors) -> Self {
61 Self {
62 values,
63 errors,
64 watched: HashMap::new(),
65 scope: String::new(),
66 options: None,
67 editing: false,
68 }
69 }
70
71 pub(crate) fn editing(mut self, editing: bool) -> Self {
73 self.editing = editing;
74 self
75 }
76
77 pub(crate) fn scoped(mut self, scope: impl Into<String>) -> Self {
80 self.scope = scope.into();
81 self
82 }
83
84 pub(crate) fn options_at(mut self, url: impl Into<String>) -> Self {
86 self.options = Some(url.into());
87 self
88 }
89
90 pub(crate) fn scope(&self) -> &str {
92 &self.scope
93 }
94
95 pub(crate) fn watching(mut self, watched: HashMap<String, Vec<Condition>>) -> Self {
97 self.watched = watched;
98 self
99 }
100
101 fn guard<'v>(
103 &self,
104 cx: &'v Cx,
105 condition: Option<&Condition>,
106 view: BoxView<'v>,
107 ) -> BoxView<'v> {
108 match condition {
109 Some(condition) => condition.guard(cx, &self.scope, self.values, view),
110 None => view,
111 }
112 }
113
114 pub(crate) fn value(&self, name: &str) -> Option<&FormValue> {
115 self.values.get(name)
116 }
117
118 pub(crate) fn text(&self, name: &str) -> Option<&str> {
120 self.values.text(name)
121 }
122
123 pub(crate) fn rows(&self, name: &str) -> &[FormValues] {
125 self.values.rows(name).unwrap_or_default()
126 }
127
128 pub(crate) fn errors(&self) -> &FieldErrors {
130 self.errors
131 }
132
133 pub(crate) fn options(&self) -> Option<&str> {
135 self.options.as_deref()
136 }
137
138 pub(crate) fn id(&self, name: &str) -> String {
140 self.placement(None).id(name)
141 }
142
143 fn placement<'p>(&'p self, parent: Option<&'p str>) -> Placement<'p> {
145 Placement {
146 parent,
147 scope: &self.scope,
148 options: self.options.as_deref(),
149 editing: self.editing,
150 }
151 }
152
153 pub(crate) fn error_for(&self, field: &Field) -> Option<&FieldError> {
155 self.errors.first(field.name())
156 }
157}
158
159impl Node {
160 pub(crate) async fn render<'a>(
162 &self,
163 cx: &'a Cx,
164 fields: &[Field],
165 source: &Source<'_>,
166 ) -> Result<BoxView<'a>> {
167 match self {
168 Node::Field(index) => {
169 let field = &fields[*index];
170 if let Some(repeater) = field.as_repeater() {
172 let view = Box::pin(repeater.render(cx, field, source)).await?;
173 return Ok(source.guard(cx, field.condition(), view));
174 }
175 let error = source.error_for(field);
176 let parent = field.parent_key().and_then(|key| source.text(key));
177 let mut view = Box::pin(field.render_under(
178 cx,
179 source.value(field.name()),
180 error,
181 source.placement(parent),
182 ))
183 .await?;
184 if let Some(conditions) = source.watched.get(field.name()) {
185 view = watch(cx, field, conditions, &source.scope, source.values, view);
186 }
187 Ok(source.guard(cx, field.condition(), view))
188 }
189 Node::Section(s) => {
190 let view = Box::pin(s.render(cx, fields, source)).await?;
191 Ok(source.guard(cx, s.condition.as_ref(), view))
192 }
193 Node::Group(g) => {
194 let view = Box::pin(g.render(cx, fields, source)).await?;
195 Ok(source.guard(cx, g.condition.as_ref(), view))
196 }
197 Node::Grid(g) => {
198 let view = Box::pin(g.render(cx, fields, source)).await?;
199 Ok(source.guard(cx, g.condition.as_ref(), view))
200 }
201 Node::Embedded(e) => Box::pin(e.render(cx, fields, source)).await,
202 Node::Unbound(_) => Ok(().boxed()),
204 }
205 }
206
207 pub(crate) fn children(&self) -> Option<&[Node]> {
210 match self {
211 Node::Section(s) => Some(&s.children.nodes),
212 Node::Group(g) => Some(&g.children.nodes),
213 Node::Grid(g) => Some(&g.children.nodes),
214 Node::Field(_) | Node::Embedded(_) | Node::Unbound(_) => None,
215 }
216 }
217
218 fn children_mut(&mut self) -> Option<&mut Vec<Node>> {
219 match self {
220 Node::Section(s) => Some(&mut s.children.nodes),
221 Node::Group(g) => Some(&mut g.children.nodes),
222 Node::Grid(g) => Some(&mut g.children.nodes),
223 Node::Field(_) | Node::Embedded(_) | Node::Unbound(_) => None,
224 }
225 }
226
227 pub(crate) fn offset(&mut self, by: usize) {
229 match self {
230 Node::Field(index) => *index += by,
231 Node::Embedded(e) => e.offset(by),
232 _ => {
233 for child in self.children_mut().into_iter().flatten() {
234 child.offset(by);
235 }
236 }
237 }
238 }
239}
240
241pub(crate) fn bind_nodes(nodes: &mut Vec<Node>, fields: &mut Vec<Field>, resolver: &FieldResolver) {
244 let mut bound = Vec::with_capacity(nodes.len());
245 for node in std::mem::take(nodes) {
246 let mut node = match node {
247 Node::Unbound(unbound) => {
248 let Schema {
249 nodes: mut built,
250 fields: built_fields,
251 ..
252 } = (unbound.build)(resolver);
253 let offset = fields.len();
254 for node in &mut built {
255 node.offset(offset);
256 }
257 fields.extend(built_fields);
258 bound.extend(built);
259 continue;
260 }
261 node => node,
262 };
263 if let Some(children) = node.children_mut() {
264 bind_nodes(children, fields, resolver);
265 }
266 bound.push(node);
267 }
268 *nodes = bound;
269}
270
271pub(crate) fn unbound_values(nodes: &[Node], out: &mut Vec<&'static str>) {
273 for node in nodes {
274 match node {
275 Node::Unbound(unbound) => out.push(unbound.value),
276 node => unbound_values(node.children().unwrap_or_default(), out),
277 }
278 }
279}
280
281pub(crate) async fn render_nodes<'a>(
283 cx: &'a Cx,
284 nodes: &[Node],
285 fields: &[Field],
286 source: &Source<'_>,
287) -> Result<BoxView<'a>> {
288 let mut views = Vec::with_capacity(nodes.len());
289 for node in nodes {
290 views.push(node.render(cx, fields, source).await?);
291 }
292 Ok(view! {
293 cx =>
294 for v in views {
295 (v)
296 }
297 }
298 .boxed())
299}
300
301#[diagnostic::on_unimplemented(
306 message = "`{Self}` cannot be placed in a `Schema<{F}>`",
307 label = "not a control, layout block, schema, or tuple of 2 to 12 of them for this schema",
308 note = "a resource's form places its record form's own controls, from `controls()`; \
309 `Field::text` and the other constructors build a page's or an action's controls"
310)]
311pub trait IntoSchema<F = ()> {
312 fn into_schema(self) -> Schema<F>;
313}
314
315#[doc(hidden)]
318pub trait Retype {
319 type As<G>;
320
321 fn retype<G>(self) -> Self::As<G>;
322}
323
324impl<F> IntoSchema<F> for Schema<F> {
325 fn into_schema(self) -> Schema<F> {
326 self
327 }
328}
329
330impl IntoSchema for Field {
331 fn into_schema(self) -> Schema {
332 Schema::from_parts(vec![Node::Field(0)], vec![self])
333 }
334}
335
336macro_rules! container_nodes {
339 ($($ty:ident),+ $(,)?) => {
340 $(
341 impl<F> IntoSchema<F> for $ty<Schema<F>> {
342 fn into_schema(self) -> Schema<F> {
343 let mut fields = Vec::new();
344 let block = self.map(|mut children| {
345 fields = std::mem::take(&mut children.fields);
346 children.retype()
347 });
348 Schema::from_parts(vec![Node::$ty(Box::new(block))], fields)
349 }
350 }
351
352 impl<F> IntoSchema<F> for $ty<()> {
353 fn into_schema(self) -> Schema<F> {
354 self.holding(Schema::<F>::default()).into_schema()
355 }
356 }
357 )+
358 };
359}
360
361container_nodes!(Section, Group, Grid);
362
363macro_rules! into_schema_tuples {
365 ($($T:ident => $v:ident),+ $(,)?) => {
366 impl<Form, $($T),+> IntoSchema<Form> for ($($T,)+)
367 where
368 $($T: IntoSchema<Form>,)+
369 {
370 fn into_schema(self) -> Schema<Form> {
371 let ($($v,)+) = self;
372 let mut schema = Schema::default();
373 $( schema.append($v.into_schema()); )+
374 schema
375 }
376 }
377 };
378}
379
380into_schema_tuples!(A => a, B => b);
381into_schema_tuples!(A => a, B => b, C => c);
382into_schema_tuples!(A => a, B => b, C => c, D => d);
383into_schema_tuples!(A => a, B => b, C => c, D => d, E => e);
384into_schema_tuples!(A => a, B => b, C => c, D => d, E => e, F => f);
385into_schema_tuples!(A => a, B => b, C => c, D => d, E => e, F => f, G => g);
386into_schema_tuples!(
387 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h
388);
389into_schema_tuples!(
390 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i
391);
392into_schema_tuples!(
393 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i, J => j
394);
395into_schema_tuples!(
396 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i, J => j, K => k
397);
398into_schema_tuples!(
399 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i, J => j, K => k, L => l
400);