1use std::{
4 collections::HashMap,
5 sync::atomic::{AtomicUsize, Ordering},
6};
7
8use topcoat::{Result, context::Cx, view::*};
9
10use super::{
11 Schema,
12 condition::{Condition, disable, watch},
13 embedded::Embedded,
14 fields::{Field, Placement},
15 layouts::{Grid, Group, Section, Step, Tab, Tabs, Wizard, spanned},
16};
17use crate::values::{FieldError, FieldErrors, FormValue, FormValues};
18
19#[derive(Debug)]
20pub(crate) enum Node {
21 Field(usize),
23 Section(Box<Section>),
24 Group(Box<Group>),
25 Grid(Box<Grid>),
26 Tabs(Box<Tabs>),
28 Tab(Box<Tab>),
30 Wizard(Box<Wizard>),
32 Step(Box<Step>),
34 Embedded(Box<Embedded>),
35}
36
37pub struct Source<'a> {
39 values: &'a FormValues,
40 errors: &'a FieldErrors,
41 watched: HashMap<String, Vec<Condition>>,
43 scope: String,
46 options: Option<String>,
48 editing: bool,
50 mask: super::FormMask,
52 prefix: String,
54 tab_depth: AtomicUsize,
57 flat: bool,
59}
60
61impl<'a> Source<'a> {
62 pub fn form(values: &'a FormValues, errors: &'a FieldErrors) -> Self {
64 Self {
65 values,
66 errors,
67 watched: HashMap::new(),
68 scope: String::new(),
69 options: None,
70 editing: false,
71 mask: super::FormMask::default(),
72 prefix: String::new(),
73 tab_depth: AtomicUsize::new(0),
74 flat: false,
75 }
76 }
77
78 pub(crate) fn flat(mut self, flat: bool) -> Self {
80 self.flat = flat;
81 self
82 }
83
84 pub(super) fn is_flat(&self) -> bool {
86 self.flat
87 }
88
89 pub(crate) fn editing(mut self, editing: bool) -> Self {
91 self.editing = editing;
92 self
93 }
94
95 pub(crate) fn masked(mut self, mask: super::FormMask) -> Self {
97 self.mask = mask;
98 self
99 }
100
101 pub(crate) fn scoped(mut self, scope: impl Into<String>) -> Self {
104 self.scope = scope.into();
105 self
106 }
107
108 pub(crate) fn posting_under(mut self, prefix: impl Into<String>) -> Self {
111 self.prefix = prefix.into();
112 self
113 }
114
115 pub(crate) fn options_at(mut self, url: impl Into<String>) -> Self {
117 self.options = Some(url.into());
118 self
119 }
120
121 pub(crate) fn scope(&self) -> &str {
123 &self.scope
124 }
125
126 pub(crate) fn watching(mut self, watched: HashMap<String, Vec<Condition>>) -> Self {
128 self.watched = watched;
129 self
130 }
131
132 pub(crate) fn guard<'v>(
134 &self,
135 cx: &'v Cx,
136 condition: Option<&Condition>,
137 view: BoxView<'v>,
138 ) -> BoxView<'v> {
139 match condition {
140 Some(condition) => condition.guard(cx, &self.scope, self.values, view),
141 None => view,
142 }
143 }
144
145 pub(crate) fn tab_depth(&self) -> usize {
147 self.tab_depth.load(Ordering::Relaxed)
148 }
149
150 pub(crate) fn set_tab_depth(&self, depth: usize) {
152 self.tab_depth.store(depth, Ordering::Relaxed);
153 }
154
155 pub(crate) fn holds(&self, condition: Option<&Condition>) -> bool {
158 condition.is_none_or(|condition| condition.holds(self.values))
159 }
160
161 pub(crate) fn value(&self, name: &str) -> Option<&FormValue> {
162 self.values.get(name)
163 }
164
165 pub(crate) fn text(&self, name: &str) -> Option<&str> {
167 self.values.text(name)
168 }
169
170 pub(crate) fn rows(&self, name: &str) -> &[FormValues] {
172 self.values.rows(name).unwrap_or_default()
173 }
174
175 pub(crate) fn errors(&self) -> &FieldErrors {
177 self.errors
178 }
179
180 pub(crate) fn options(&self) -> Option<&str> {
182 self.options.as_deref()
183 }
184
185 pub(crate) fn id(&self, name: &str) -> String {
187 self.placement(name, None).id(name)
188 }
189
190 fn placement<'p>(&'p self, name: &str, parent: Option<&'p str>) -> Placement<'p> {
192 Placement {
193 parent,
194 scope: &self.scope,
195 options: self.options.as_deref(),
196 editing: self.editing,
197 locked: self.mask.locks(name),
198 prefix: &self.prefix,
199 }
200 }
201
202 pub(crate) fn error_for(&self, field: &Field) -> Option<&FieldError> {
204 self.errors.first(field.name())
205 }
206}
207
208impl Node {
209 pub(crate) async fn render<'a>(
211 &self,
212 cx: &'a Cx,
213 fields: &[Field],
214 source: &Source<'_>,
215 ) -> Result<BoxView<'a>> {
216 match self {
217 Node::Field(index) => {
218 if !shows(&fields[*index], source) {
219 return Ok(().boxed());
220 }
221 let field = &fields[*index];
222 if let Some(repeater) = field.as_repeater() {
224 let view = Box::pin(repeater.render(cx, field, source)).await?;
225 let view = spanned(cx, field.span(), view);
226 return Ok(source.guard(cx, field.condition(), view));
227 }
228 let error = source.error_for(field);
229 let parent = field.parent_key().and_then(|key| source.text(key));
230 let mut view = Box::pin(field.render_under(
231 cx,
232 source.value(field.name()),
233 error,
234 source.placement(field.name(), parent),
235 ))
236 .await?;
237 if let Some(conditions) = source.watched.get(field.name()) {
238 let posted = format!("{}{}", source.prefix, field.name());
239 view = watch(
240 cx,
241 field,
242 &posted,
243 conditions,
244 &source.scope,
245 source.values,
246 view,
247 );
248 }
249 if let Some(condition) = field.disabling() {
250 view = disable(cx, condition, &source.scope, source.values, view);
251 }
252 let view = spanned(cx, field.span(), view);
253 Ok(source.guard(cx, field.condition(), view))
254 }
255 Node::Section(_) | Node::Group(_) | Node::Grid(_) | Node::Tabs(_) | Node::Wizard(_)
257 if !self.shows_any(fields, source) =>
258 {
259 Ok(().boxed())
260 }
261 Node::Section(s) => {
262 let view = Box::pin(s.render(cx, fields, source)).await?;
263 Ok(source.guard(cx, s.condition.as_ref(), view))
264 }
265 Node::Group(g) => {
266 let view = Box::pin(g.render(cx, fields, source)).await?;
267 Ok(source.guard(cx, g.condition.as_ref(), view))
268 }
269 Node::Grid(g) => {
270 let view = Box::pin(g.render(cx, fields, source)).await?;
271 Ok(source.guard(cx, g.condition.as_ref(), view))
272 }
273 Node::Tabs(t) => {
274 let view = Box::pin(t.render(cx, fields, source)).await?;
275 Ok(source.guard(cx, t.condition.as_ref(), view))
276 }
277 Node::Tab(t) => Box::pin(render_nodes(cx, &t.children.nodes, fields, source)).await,
279 Node::Wizard(w) => {
280 let view = Box::pin(w.render(cx, fields, source)).await?;
281 Ok(source.guard(cx, w.condition.as_ref(), view))
282 }
283 Node::Step(s) => Box::pin(render_nodes(cx, &s.children.nodes, fields, source)).await,
285 Node::Embedded(e) => Box::pin(e.render(cx, fields, source)).await,
286 }
287 }
288
289 pub(crate) fn has_error(&self, fields: &[Field], source: &Source<'_>) -> bool {
292 let mut any = false;
293 self.visit_fields(&mut |index| {
294 let name = fields[index].name();
295 any |= source.errors.iter().any(|error| {
296 error.key == name
297 || error
298 .key
299 .strip_prefix(name)
300 .is_some_and(|rest| rest.starts_with('.'))
301 });
302 });
303 any
304 }
305
306 pub(crate) fn first_field(&self) -> Option<usize> {
308 let mut first = None;
309 self.visit_fields(&mut |index| {
310 first.get_or_insert(index);
311 });
312 first
313 }
314
315 fn visit_fields(&self, f: &mut impl FnMut(usize)) {
317 match self {
318 Node::Field(index) => f(*index),
319 Node::Embedded(e) => e.visit_fields(f),
320 node => {
321 for child in node.children().unwrap_or_default() {
322 child.visit_fields(f);
323 }
324 }
325 }
326 }
327
328 pub(crate) fn shows_any(&self, fields: &[Field], source: &Source<'_>) -> bool {
330 match self {
331 Node::Field(index) => shows(&fields[*index], source),
332 Node::Embedded(e) => {
333 let mut any = false;
334 e.visit_fields(&mut |index| any |= shows(&fields[index], source));
335 any
336 }
337 node => node
338 .children()
339 .unwrap_or_default()
340 .iter()
341 .any(|child| child.shows_any(fields, source)),
342 }
343 }
344
345 pub(crate) fn children(&self) -> Option<&[Node]> {
348 match self {
349 Node::Section(s) => Some(&s.children.nodes),
350 Node::Group(g) => Some(&g.children.nodes),
351 Node::Grid(g) => Some(&g.children.nodes),
352 Node::Tabs(t) => Some(&t.children.nodes),
353 Node::Tab(t) => Some(&t.children.nodes),
354 Node::Wizard(w) => Some(&w.children.nodes),
355 Node::Step(s) => Some(&s.children.nodes),
356 Node::Field(_) | Node::Embedded(_) => None,
357 }
358 }
359
360 fn children_mut(&mut self) -> Option<&mut Vec<Node>> {
361 match self {
362 Node::Section(s) => Some(&mut s.children.nodes),
363 Node::Group(g) => Some(&mut g.children.nodes),
364 Node::Grid(g) => Some(&mut g.children.nodes),
365 Node::Tabs(t) => Some(&mut t.children.nodes),
366 Node::Tab(t) => Some(&mut t.children.nodes),
367 Node::Wizard(w) => Some(&mut w.children.nodes),
368 Node::Step(s) => Some(&mut s.children.nodes),
369 Node::Field(_) | Node::Embedded(_) => None,
370 }
371 }
372
373 pub(crate) fn offset(&mut self, by: usize) {
375 match self {
376 Node::Field(index) => *index += by,
377 Node::Embedded(e) => e.offset(by),
378 _ => {
379 for child in self.children_mut().into_iter().flatten() {
380 child.offset(by);
381 }
382 }
383 }
384 }
385}
386
387impl<F> Schema<F> {
388 pub(crate) fn needs_own_keys(&self) -> Option<&str> {
392 let mut embedded = Vec::new();
393 for node in &self.nodes {
394 node.embedded_fields(&mut embedded);
395 }
396 self.fields
397 .iter()
398 .enumerate()
399 .find(|(index, field)| {
400 embedded.contains(index)
401 || field.is_file()
402 || field.as_repeater().is_some()
403 || field.parent_key().is_some()
404 || field
405 .as_choice()
406 .is_some_and(|choice| choice.is_searchable())
407 })
408 .map(|(_, field)| field.name())
409 }
410}
411
412impl Node {
413 fn embedded_fields(&self, out: &mut Vec<usize>) {
415 match self {
416 Node::Embedded(e) => e.visit_fields(&mut |index| out.push(index)),
417 node => {
418 for child in node.children().unwrap_or_default() {
419 child.embedded_fields(out);
420 }
421 }
422 }
423 }
424}
425
426fn shows(field: &Field, source: &Source<'_>) -> bool {
429 !(source.mask.hides(field.name())
430 || field.is_hidden(source.editing)
431 || (source.mask.locks(field.name()) && field.as_repeater().is_some()))
432}
433
434pub(crate) async fn render_nodes<'a>(
436 cx: &'a Cx,
437 nodes: &[Node],
438 fields: &[Field],
439 source: &Source<'_>,
440) -> Result<BoxView<'a>> {
441 let mut views = Vec::with_capacity(nodes.len());
442 for node in nodes {
443 views.push(node.render(cx, fields, source).await?);
444 }
445 Ok(view! {
446 for v in views {
447 (v)
448 }
449 }
450 .boxed())
451}
452
453#[diagnostic::on_unimplemented(
458 message = "`{Self}` cannot be placed in a `Schema<{F}>`",
459 label = "not a control, layout block, schema, or tuple of 2 to 12 of them for this schema",
460 note = "a resource's form places its record form's own controls, from `controls()`; \
461 `Field::text` and the other constructors build a page's or an action's controls"
462)]
463pub trait IntoSchema<F = ()> {
464 fn into_schema(self) -> Schema<F>;
465}
466
467#[doc(hidden)]
470pub trait Retype {
471 type As<G>;
472
473 fn retype<G>(self) -> Self::As<G>;
474}
475
476impl<F> IntoSchema<F> for Schema<F> {
477 fn into_schema(self) -> Schema<F> {
478 self
479 }
480}
481
482impl IntoSchema for Field {
483 fn into_schema(self) -> Schema {
484 Schema::from_parts(vec![Node::Field(0)], vec![self])
485 }
486}
487
488macro_rules! container_nodes {
491 ($($ty:ident),+ $(,)?) => {
492 $(
493 impl<F> IntoSchema<F> for $ty<Schema<F>> {
494 fn into_schema(self) -> Schema<F> {
495 let mut fields = Vec::new();
496 let block = self.map(|mut children| {
497 fields = std::mem::take(&mut children.fields);
498 children.retype()
499 });
500 Schema::from_parts(vec![Node::$ty(Box::new(block))], fields)
501 }
502 }
503
504 impl<F> IntoSchema<F> for $ty<()> {
505 fn into_schema(self) -> Schema<F> {
506 self.holding(Schema::<F>::default()).into_schema()
507 }
508 }
509 )+
510 };
511}
512
513container_nodes!(Section, Group, Grid);
514
515impl<F> IntoSchema<F> for Tabs<Schema<F>> {
516 fn into_schema(self) -> Schema<F> {
517 let mut fields = Vec::new();
518 let block = self.map(|mut children| {
519 fields = std::mem::take(&mut children.fields);
520 children.retype()
521 });
522 Schema::from_parts(vec![Node::Tabs(Box::new(block))], fields)
523 }
524}
525
526macro_rules! into_schema_tuples {
528 ($($T:ident => $v:ident),+ $(,)?) => {
529 impl<Form, $($T),+> IntoSchema<Form> for ($($T,)+)
530 where
531 $($T: IntoSchema<Form>,)+
532 {
533 fn into_schema(self) -> Schema<Form> {
534 let ($($v,)+) = self;
535 let mut schema = Schema::default();
536 $( schema.append($v.into_schema()); )+
537 schema
538 }
539 }
540 };
541}
542
543into_schema_tuples!(A => a, B => b);
544into_schema_tuples!(A => a, B => b, C => c);
545into_schema_tuples!(A => a, B => b, C => c, D => d);
546into_schema_tuples!(A => a, B => b, C => c, D => d, E => e);
547into_schema_tuples!(A => a, B => b, C => c, D => d, E => e, F => f);
548into_schema_tuples!(A => a, B => b, C => c, D => d, E => e, F => f, G => g);
549into_schema_tuples!(
550 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h
551);
552into_schema_tuples!(
553 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i
554);
555into_schema_tuples!(
556 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i, J => j
557);
558into_schema_tuples!(
559 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i, J => j, K => k
560);
561into_schema_tuples!(
562 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i, J => j, K => k, L => l
563);