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, watch},
13 embedded::Embedded,
14 fields::{Field, Placement},
15 layouts::{Grid, Group, Section, Tab, Tabs},
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 Embedded(Box<Embedded>),
31}
32
33pub struct Source<'a> {
35 values: &'a FormValues,
36 errors: &'a FieldErrors,
37 watched: HashMap<String, Vec<Condition>>,
39 scope: String,
42 options: Option<String>,
44 editing: bool,
46 mask: super::FormMask,
48 tab_depth: AtomicUsize,
51}
52
53impl<'a> Source<'a> {
54 pub fn form(values: &'a FormValues, errors: &'a FieldErrors) -> Self {
56 Self {
57 values,
58 errors,
59 watched: HashMap::new(),
60 scope: String::new(),
61 options: None,
62 editing: false,
63 mask: super::FormMask::default(),
64 tab_depth: AtomicUsize::new(0),
65 }
66 }
67
68 pub(crate) fn editing(mut self, editing: bool) -> Self {
70 self.editing = editing;
71 self
72 }
73
74 pub(crate) fn masked(mut self, mask: super::FormMask) -> Self {
76 self.mask = mask;
77 self
78 }
79
80 pub(crate) fn scoped(mut self, scope: impl Into<String>) -> Self {
83 self.scope = scope.into();
84 self
85 }
86
87 pub(crate) fn options_at(mut self, url: impl Into<String>) -> Self {
89 self.options = Some(url.into());
90 self
91 }
92
93 pub(crate) fn scope(&self) -> &str {
95 &self.scope
96 }
97
98 pub(crate) fn watching(mut self, watched: HashMap<String, Vec<Condition>>) -> Self {
100 self.watched = watched;
101 self
102 }
103
104 pub(crate) fn guard<'v>(
106 &self,
107 cx: &'v Cx,
108 condition: Option<&Condition>,
109 view: BoxView<'v>,
110 ) -> BoxView<'v> {
111 match condition {
112 Some(condition) => condition.guard(cx, &self.scope, self.values, view),
113 None => view,
114 }
115 }
116
117 pub(crate) fn tab_depth(&self) -> usize {
119 self.tab_depth.load(Ordering::Relaxed)
120 }
121
122 pub(crate) fn set_tab_depth(&self, depth: usize) {
124 self.tab_depth.store(depth, Ordering::Relaxed);
125 }
126
127 pub(crate) fn holds(&self, condition: Option<&Condition>) -> bool {
130 condition.is_none_or(|condition| condition.holds(self.values))
131 }
132
133 pub(crate) fn value(&self, name: &str) -> Option<&FormValue> {
134 self.values.get(name)
135 }
136
137 pub(crate) fn text(&self, name: &str) -> Option<&str> {
139 self.values.text(name)
140 }
141
142 pub(crate) fn rows(&self, name: &str) -> &[FormValues] {
144 self.values.rows(name).unwrap_or_default()
145 }
146
147 pub(crate) fn errors(&self) -> &FieldErrors {
149 self.errors
150 }
151
152 pub(crate) fn options(&self) -> Option<&str> {
154 self.options.as_deref()
155 }
156
157 pub(crate) fn id(&self, name: &str) -> String {
159 self.placement(name, None).id(name)
160 }
161
162 fn placement<'p>(&'p self, name: &str, parent: Option<&'p str>) -> Placement<'p> {
164 Placement {
165 parent,
166 scope: &self.scope,
167 options: self.options.as_deref(),
168 editing: self.editing,
169 locked: self.mask.locks(name),
170 }
171 }
172
173 pub(crate) fn error_for(&self, field: &Field) -> Option<&FieldError> {
175 self.errors.first(field.name())
176 }
177}
178
179impl Node {
180 pub(crate) async fn render<'a>(
182 &self,
183 cx: &'a Cx,
184 fields: &[Field],
185 source: &Source<'_>,
186 ) -> Result<BoxView<'a>> {
187 match self {
188 Node::Field(index) => {
189 if !shows(&fields[*index], source) {
190 return Ok(().boxed());
191 }
192 let field = &fields[*index];
193 if let Some(repeater) = field.as_repeater() {
195 let view = Box::pin(repeater.render(cx, field, source)).await?;
196 return Ok(source.guard(cx, field.condition(), view));
197 }
198 let error = source.error_for(field);
199 let parent = field.parent_key().and_then(|key| source.text(key));
200 let mut view = Box::pin(field.render_under(
201 cx,
202 source.value(field.name()),
203 error,
204 source.placement(field.name(), parent),
205 ))
206 .await?;
207 if let Some(conditions) = source.watched.get(field.name()) {
208 view = watch(cx, field, conditions, &source.scope, source.values, view);
209 }
210 Ok(source.guard(cx, field.condition(), view))
211 }
212 Node::Section(_) | Node::Group(_) | Node::Grid(_) | Node::Tabs(_)
214 if !self.shows_any(fields, source) =>
215 {
216 Ok(().boxed())
217 }
218 Node::Section(s) => {
219 let view = Box::pin(s.render(cx, fields, source)).await?;
220 Ok(source.guard(cx, s.condition.as_ref(), view))
221 }
222 Node::Group(g) => {
223 let view = Box::pin(g.render(cx, fields, source)).await?;
224 Ok(source.guard(cx, g.condition.as_ref(), view))
225 }
226 Node::Grid(g) => {
227 let view = Box::pin(g.render(cx, fields, source)).await?;
228 Ok(source.guard(cx, g.condition.as_ref(), view))
229 }
230 Node::Tabs(t) => {
231 let view = Box::pin(t.render(cx, fields, source)).await?;
232 Ok(source.guard(cx, t.condition.as_ref(), view))
233 }
234 Node::Tab(t) => Box::pin(render_nodes(cx, &t.children.nodes, fields, source)).await,
236 Node::Embedded(e) => Box::pin(e.render(cx, fields, source)).await,
237 }
238 }
239
240 pub(crate) fn has_error(&self, fields: &[Field], source: &Source<'_>) -> bool {
243 let mut any = false;
244 self.visit_fields(&mut |index| {
245 let name = fields[index].name();
246 any |= source.errors.iter().any(|error| {
247 error.key == name
248 || error
249 .key
250 .strip_prefix(name)
251 .is_some_and(|rest| rest.starts_with('.'))
252 });
253 });
254 any
255 }
256
257 pub(crate) fn first_field(&self) -> Option<usize> {
259 let mut first = None;
260 self.visit_fields(&mut |index| {
261 first.get_or_insert(index);
262 });
263 first
264 }
265
266 fn visit_fields(&self, f: &mut impl FnMut(usize)) {
268 match self {
269 Node::Field(index) => f(*index),
270 Node::Embedded(e) => e.visit_fields(f),
271 node => {
272 for child in node.children().unwrap_or_default() {
273 child.visit_fields(f);
274 }
275 }
276 }
277 }
278
279 pub(crate) fn shows_any(&self, fields: &[Field], source: &Source<'_>) -> bool {
281 match self {
282 Node::Field(index) => shows(&fields[*index], source),
283 Node::Embedded(e) => {
284 let mut any = false;
285 e.visit_fields(&mut |index| any |= shows(&fields[index], source));
286 any
287 }
288 node => node
289 .children()
290 .unwrap_or_default()
291 .iter()
292 .any(|child| child.shows_any(fields, source)),
293 }
294 }
295
296 pub(crate) fn children(&self) -> Option<&[Node]> {
299 match self {
300 Node::Section(s) => Some(&s.children.nodes),
301 Node::Group(g) => Some(&g.children.nodes),
302 Node::Grid(g) => Some(&g.children.nodes),
303 Node::Tabs(t) => Some(&t.children.nodes),
304 Node::Tab(t) => Some(&t.children.nodes),
305 Node::Field(_) | Node::Embedded(_) => None,
306 }
307 }
308
309 fn children_mut(&mut self) -> Option<&mut Vec<Node>> {
310 match self {
311 Node::Section(s) => Some(&mut s.children.nodes),
312 Node::Group(g) => Some(&mut g.children.nodes),
313 Node::Grid(g) => Some(&mut g.children.nodes),
314 Node::Tabs(t) => Some(&mut t.children.nodes),
315 Node::Tab(t) => Some(&mut t.children.nodes),
316 Node::Field(_) | Node::Embedded(_) => None,
317 }
318 }
319
320 pub(crate) fn offset(&mut self, by: usize) {
322 match self {
323 Node::Field(index) => *index += by,
324 Node::Embedded(e) => e.offset(by),
325 _ => {
326 for child in self.children_mut().into_iter().flatten() {
327 child.offset(by);
328 }
329 }
330 }
331 }
332}
333
334fn shows(field: &Field, source: &Source<'_>) -> bool {
337 !(source.mask.hides(field.name())
338 || (source.mask.locks(field.name()) && field.as_repeater().is_some()))
339}
340
341pub(crate) async fn render_nodes<'a>(
343 cx: &'a Cx,
344 nodes: &[Node],
345 fields: &[Field],
346 source: &Source<'_>,
347) -> Result<BoxView<'a>> {
348 let mut views = Vec::with_capacity(nodes.len());
349 for node in nodes {
350 views.push(node.render(cx, fields, source).await?);
351 }
352 Ok(view! {
353 cx =>
354 for v in views {
355 (v)
356 }
357 }
358 .boxed())
359}
360
361#[diagnostic::on_unimplemented(
366 message = "`{Self}` cannot be placed in a `Schema<{F}>`",
367 label = "not a control, layout block, schema, or tuple of 2 to 12 of them for this schema",
368 note = "a resource's form places its record form's own controls, from `controls()`; \
369 `Field::text` and the other constructors build a page's or an action's controls"
370)]
371pub trait IntoSchema<F = ()> {
372 fn into_schema(self) -> Schema<F>;
373}
374
375#[doc(hidden)]
378pub trait Retype {
379 type As<G>;
380
381 fn retype<G>(self) -> Self::As<G>;
382}
383
384impl<F> IntoSchema<F> for Schema<F> {
385 fn into_schema(self) -> Schema<F> {
386 self
387 }
388}
389
390impl IntoSchema for Field {
391 fn into_schema(self) -> Schema {
392 Schema::from_parts(vec![Node::Field(0)], vec![self])
393 }
394}
395
396macro_rules! container_nodes {
399 ($($ty:ident),+ $(,)?) => {
400 $(
401 impl<F> IntoSchema<F> for $ty<Schema<F>> {
402 fn into_schema(self) -> Schema<F> {
403 let mut fields = Vec::new();
404 let block = self.map(|mut children| {
405 fields = std::mem::take(&mut children.fields);
406 children.retype()
407 });
408 Schema::from_parts(vec![Node::$ty(Box::new(block))], fields)
409 }
410 }
411
412 impl<F> IntoSchema<F> for $ty<()> {
413 fn into_schema(self) -> Schema<F> {
414 self.holding(Schema::<F>::default()).into_schema()
415 }
416 }
417 )+
418 };
419}
420
421container_nodes!(Section, Group, Grid);
422
423impl<F> IntoSchema<F> for Tabs<Schema<F>> {
424 fn into_schema(self) -> Schema<F> {
425 let mut fields = Vec::new();
426 let block = self.map(|mut children| {
427 fields = std::mem::take(&mut children.fields);
428 children.retype()
429 });
430 Schema::from_parts(vec![Node::Tabs(Box::new(block))], fields)
431 }
432}
433
434macro_rules! into_schema_tuples {
436 ($($T:ident => $v:ident),+ $(,)?) => {
437 impl<Form, $($T),+> IntoSchema<Form> for ($($T,)+)
438 where
439 $($T: IntoSchema<Form>,)+
440 {
441 fn into_schema(self) -> Schema<Form> {
442 let ($($v,)+) = self;
443 let mut schema = Schema::default();
444 $( schema.append($v.into_schema()); )+
445 schema
446 }
447 }
448 };
449}
450
451into_schema_tuples!(A => a, B => b);
452into_schema_tuples!(A => a, B => b, C => c);
453into_schema_tuples!(A => a, B => b, C => c, D => d);
454into_schema_tuples!(A => a, B => b, C => c, D => d, E => e);
455into_schema_tuples!(A => a, B => b, C => c, D => d, E => e, F => f);
456into_schema_tuples!(A => a, B => b, C => c, D => d, E => e, F => f, G => g);
457into_schema_tuples!(
458 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h
459);
460into_schema_tuples!(
461 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i
462);
463into_schema_tuples!(
464 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i, J => j
465);
466into_schema_tuples!(
467 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i, J => j, K => k
468);
469into_schema_tuples!(
470 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i, J => j, K => k, L => l
471);