1use std::sync::Arc;
4
5use toasty::stmt::{List, Query};
6use topcoat::{context::Cx, view::*};
7
8use crate::{
9 DeclarationErrorKind,
10 schema::{FieldResolver, Grid, Group, Section},
11 table::{BoxColumn, Column, include_relations},
12};
13
14pub struct Detail<M> {
41 nodes: Vec<Node<M>>,
42}
43
44enum Node<M> {
46 Column(BoxColumn<M>),
47 Section(Box<Section<Detail<M>>>),
48 Group(Box<Group<Detail<M>>>),
49 Grid(Box<Grid<Detail<M>>>),
50}
51
52impl<M> Detail<M> {
53 pub fn new(children: impl IntoDetail<M>) -> Self {
55 children.into_detail()
56 }
57
58 pub fn empty() -> Self {
60 Self { nodes: Vec::new() }
61 }
62
63 pub fn is_empty(&self) -> bool {
65 self.nodes.is_empty()
66 }
67
68 pub fn column(mut self, column: impl Column<M> + 'static) -> Self {
70 self.nodes.push(Node::Column(Arc::new(column)));
71 self
72 }
73
74 fn append(&mut self, other: Self) {
76 self.nodes.extend(other.nodes);
77 }
78
79 fn columns(&self) -> Vec<&BoxColumn<M>> {
81 fn walk<'d, M>(nodes: &'d [Node<M>], out: &mut Vec<&'d BoxColumn<M>>) {
82 for node in nodes {
83 match node {
84 Node::Column(column) => out.push(column),
85 Node::Section(section) => walk(§ion.children.nodes, out),
86 Node::Group(group) => walk(&group.children.nodes, out),
87 Node::Grid(grid) => walk(&grid.children.nodes, out),
88 }
89 }
90 }
91 let mut out = Vec::new();
92 walk(&self.nodes, &mut out);
93 out
94 }
95
96 pub fn include_relations(&self, query: Query<List<M>>) -> Query<List<M>>
99 where
100 M: toasty::schema::Model,
101 {
102 include_relations(query, self.columns())
103 }
104
105 pub(crate) fn unavailable_sources(&self, cx: &Cx) -> Vec<DeclarationErrorKind> {
107 crate::table::column::unavailable_sources(cx, self.columns().into_iter().map(|c| &**c))
108 }
109
110 pub fn declaration_errors(&self) -> Vec<DeclarationErrorKind> {
112 self.columns()
113 .into_iter()
114 .filter_map(|column| column.misdeclared())
115 .collect()
116 }
117
118 pub fn bind(self, db: &toasty::Db) -> Self {
124 self.bind_with(&FieldResolver::of_db(db));
125 self
126 }
127
128 pub(crate) fn bind_with(&self, resolver: &FieldResolver) {
130 for column in self.columns() {
131 column.bind(resolver);
132 }
133 }
134
135 pub fn render<'a>(&self, cx: &'a Cx, record: &M) -> BoxView<'a> {
142 let views: Vec<BoxView<'a>> = self
143 .nodes
144 .iter()
145 .map(|node| match node {
146 Node::Column(column) => column.entry(cx, record),
147 Node::Section(section) => {
148 let body =
149 (!section.children.is_empty()).then(|| section.children.render(cx, record));
150 section.chrome(cx, body)
151 }
152 Node::Group(group) => group.chrome(cx, group.children.render(cx, record)),
153 Node::Grid(grid) => grid.chrome(cx, grid.children.render(cx, record)),
154 })
155 .collect();
156 view! {
157 cx =>
158 for v in views {
159 (v)
160 }
161 }
162 .boxed()
163 }
164}
165
166impl<M> Default for Detail<M> {
167 fn default() -> Self {
168 Self::empty()
169 }
170}
171
172impl<M> std::fmt::Debug for Detail<M> {
173 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174 f.debug_struct("Detail")
175 .field(
176 "columns",
177 &self
178 .columns()
179 .into_iter()
180 .map(|column| column.name())
181 .collect::<Vec<_>>(),
182 )
183 .finish()
184 }
185}
186
187pub trait IntoDetail<M> {
215 fn into_detail(self) -> Detail<M>;
216}
217
218impl<M> IntoDetail<M> for Detail<M> {
219 fn into_detail(self) -> Detail<M> {
220 self
221 }
222}
223
224impl<M> IntoDetail<M> for () {
225 fn into_detail(self) -> Detail<M> {
226 Detail::empty()
227 }
228}
229
230impl<M> IntoDetail<M> for BoxColumn<M> {
231 fn into_detail(self) -> Detail<M> {
232 Detail {
233 nodes: vec![Node::Column(self)],
234 }
235 }
236}
237
238macro_rules! block_details {
240 ($($ty:ident),+ $(,)?) => {
241 $(
242 impl<M> IntoDetail<M> for $ty<Detail<M>> {
243 fn into_detail(self) -> Detail<M> {
244 Detail {
245 nodes: vec![Node::$ty(Box::new(self))],
246 }
247 }
248 }
249
250 impl<M> IntoDetail<M> for $ty<()> {
251 fn into_detail(self) -> Detail<M> {
252 self.holding(Detail::empty()).into_detail()
253 }
254 }
255 )+
256 };
257}
258
259block_details!(Section, Group, Grid);
260
261macro_rules! into_detail_tuples {
263 ($($T:ident => $v:ident),+ $(,)?) => {
264 impl<M, $($T),+> IntoDetail<M> for ($($T,)+)
265 where
266 $($T: IntoDetail<M>,)+
267 {
268 fn into_detail(self) -> Detail<M> {
269 let ($($v,)+) = self;
270 let mut detail = Detail::empty();
271 $( detail.append($v.into_detail()); )+
272 detail
273 }
274 }
275 };
276}
277
278into_detail_tuples!(A => a);
279into_detail_tuples!(A => a, B => b);
280into_detail_tuples!(A => a, B => b, C => c);
281into_detail_tuples!(A => a, B => b, C => c, D => d);
282into_detail_tuples!(A => a, B => b, C => c, D => d, E => e);
283into_detail_tuples!(A => a, B => b, C => c, D => d, E => e, F => f);
284into_detail_tuples!(A => a, B => b, C => c, D => d, E => e, F => f, G => g);
285into_detail_tuples!(
286 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h
287);
288into_detail_tuples!(
289 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i
290);
291into_detail_tuples!(
292 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i, J => j
293);
294into_detail_tuples!(
295 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i, J => j, K => k
296);
297into_detail_tuples!(
298 A => a, B => b, C => c, D => d, E => e, F => f, G => g, H => h, I => i, J => j, K => k, L => l
299);
300
301#[cfg(test)]
302mod tests;