tablo_core/table/column/
embedded.rs1use std::sync::{Arc, OnceLock};
4
5use derive_where::derive_where;
6use topcoat::{context::Cx, view::*};
7
8use super::{Column, ColumnBase, ColumnWidth};
9use crate::{
10 Lens,
11 form::FormValues,
12 schema::{EmbeddedForm, FieldResolver, Schema},
13};
14
15#[derive_where(Clone, Debug)]
32pub struct EmbeddedColumn<M, T> {
33 lens: Lens<M, T>,
34 base: ColumnBase,
35 schema: Arc<OnceLock<Schema>>,
37}
38
39impl<M, T> EmbeddedColumn<M, T>
40where
41 M: toasty::schema::Model,
42 T: EmbeddedForm,
43{
44 pub fn new(lens: Lens<M, T>) -> Self {
46 Self {
47 base: ColumnBase::new(lens.path(), ColumnWidth::Wide),
48 lens,
49 schema: Arc::default(),
50 }
51 }
52
53 base_builders!();
54
55 fn spelled(&self, row: &M) -> Option<(&Schema, FormValues)> {
57 let schema = self.schema.get()?;
58 let mut values = FormValues::new();
59 self.lens
60 .read(row)
61 .write_node(schema.embedded_root(), &mut values);
62 Some((schema, values))
63 }
64}
65
66impl<M, T> Column<M> for EmbeddedColumn<M, T>
67where
68 M: toasty::schema::Model + Send + Sync + 'static,
69 T: EmbeddedForm + Send + Sync + 'static,
70{
71 base_column_methods!();
72
73 fn text(&self, _cx: &Cx, row: &M) -> String {
74 let Some((schema, values)) = self.spelled(row) else {
75 return String::new();
76 };
77 schema
78 .embedded_root()
79 .shown(&schema.fields, &values)
80 .into_iter()
81 .filter_map(|index| {
82 let field = &schema.fields[index];
83 let value = values.text(field.name()).unwrap_or_default();
84 let read = field.read(value)?;
85 Some(format!("{}: {read}", field.label_str()))
86 })
87 .collect::<Vec<_>>()
88 .join(", ")
89 }
90
91 fn entry<'a>(&self, cx: &'a Cx, row: &M) -> BoxView<'a> {
92 match self.spelled(row) {
93 Some((schema, values)) => schema.embedded_root().display(cx, &schema.fields, &values),
94 None => ().boxed(),
95 }
96 }
97
98 fn misdeclared(&self) -> Option<crate::DeclarationErrorKind> {
99 if let Some(error) = self.base.binding.misdeclared() {
100 return Some(error);
101 }
102 match self.schema.get() {
103 Some(schema) => schema.declaration_errors().into_iter().next(),
104 None => Some(crate::DeclarationErrorKind::Unbound {
105 item: std::any::type_name::<T>(),
106 }),
107 }
108 }
109
110 fn bind(&self, resolver: &FieldResolver) {
111 self.base.binding.bind(resolver);
112 self.schema
113 .get_or_init(|| T::build_schema(resolver, self.lens.path().clone()));
114 }
115}