tablo_core/table/column/
model.rs1use toasty::{schema::Model, stmt::IntoExpr};
4
5use crate::{Detail, Table};
6
7#[diagnostic::on_unimplemented(
15 message = "`{Self}` declares no default columns",
16 label = "a resource's model implements `ModelColumns`",
17 note = "add `#[derive(tablo::ModelColumns)]` to `{Self}`, or `impl tablo::ModelColumns for \
18 {Self} {{}}` when its resources declare their own table and detail page"
19)]
20pub trait ModelColumns: Model + IntoExpr<Self> + Sized {
21 fn table() -> Table<Self> {
23 Table::new(())
24 }
25
26 fn detail() -> Detail<Self> {
28 Detail::empty()
29 }
30}
31
32#[doc(hidden)]
37pub mod pick {
38 use toasty::{
39 schema::{Field, Model},
40 stmt::List,
41 };
42
43 use crate::{
44 BooleanColumn, Detail, EmbeddedColumn, Lens, RepeaterColumn, Table, TextColumn,
45 form::FormScalar,
46 schema::{EmbeddedForm, RepeaterItem},
47 };
48
49 pub struct ModelField<M, T, P = T>(pub Lens<M, T, P>);
51
52 pub type PathOf<T> = <T as Field>::ExprTarget;
54
55 pub trait PickToggle<M> {
57 fn table(&self, table: Table<M>) -> Table<M>;
58 fn detail(&self, detail: Detail<M>) -> Detail<M>;
59 }
60
61 impl<M> PickToggle<M> for &&&&&ModelField<M, bool>
62 where
63 M: Model + Send + Sync + 'static,
64 {
65 fn table(&self, table: Table<M>) -> Table<M> {
66 table.column(BooleanColumn::new(self.0.clone()).sortable())
67 }
68
69 fn detail(&self, detail: Detail<M>) -> Detail<M> {
70 detail.column(BooleanColumn::new(self.0.clone()))
71 }
72 }
73
74 pub trait PickString<M> {
76 fn table(&self, table: Table<M>) -> Table<M>;
77 fn detail(&self, detail: Detail<M>) -> Detail<M>;
78 }
79
80 impl<M, T> PickString<M> for &&&&ModelField<M, T>
81 where
82 M: Model + Send + Sync + 'static,
83 T: FormScalar + Field<Inner = String> + Send + Sync + 'static,
84 {
85 fn table(&self, table: Table<M>) -> Table<M> {
86 table.column(TextColumn::new(self.0.clone()).sortable().searchable())
87 }
88
89 fn detail(&self, detail: Detail<M>) -> Detail<M> {
90 detail.column(TextColumn::new(self.0.clone()))
91 }
92 }
93
94 pub trait PickScalar<M> {
96 fn table(&self, table: Table<M>) -> Table<M>;
97 fn detail(&self, detail: Detail<M>) -> Detail<M>;
98 }
99
100 impl<M, T> PickScalar<M> for &&&ModelField<M, T>
101 where
102 M: Model + Send + Sync + 'static,
103 T: FormScalar + Send + Sync + 'static,
104 {
105 fn table(&self, table: Table<M>) -> Table<M> {
106 table.column(TextColumn::new(self.0.clone()).sortable())
107 }
108
109 fn detail(&self, detail: Detail<M>) -> Detail<M> {
110 detail.column(TextColumn::new(self.0.clone()))
111 }
112 }
113
114 pub trait PickEmbedded<M> {
116 fn table(&self, table: Table<M>) -> Table<M>;
117 fn detail(&self, detail: Detail<M>) -> Detail<M>;
118 }
119
120 impl<M, T> PickEmbedded<M> for &&ModelField<M, T>
121 where
122 M: Model + Send + Sync + 'static,
123 T: EmbeddedForm + Send + Sync + 'static,
124 {
125 fn table(&self, table: Table<M>) -> Table<M> {
126 table
127 }
128
129 fn detail(&self, detail: Detail<M>) -> Detail<M> {
130 detail.column(EmbeddedColumn::new(self.0.clone()))
131 }
132 }
133
134 pub trait PickRepeater<M> {
136 fn table(&self, table: Table<M>) -> Table<M>;
137 fn detail(&self, detail: Detail<M>) -> Detail<M>;
138 }
139
140 impl<M, T> PickRepeater<M> for &ModelField<M, Vec<T>, List<T>>
141 where
142 M: Model + Send + Sync + 'static,
143 T: RepeaterItem + Send + Sync + 'static,
144 {
145 fn table(&self, table: Table<M>) -> Table<M> {
146 table
147 }
148
149 fn detail(&self, detail: Detail<M>) -> Detail<M> {
150 detail.column(RepeaterColumn::new(self.0.clone()))
151 }
152 }
153
154 pub trait PickNone<M> {
156 fn table(&self, table: Table<M>) -> Table<M>;
157 fn detail(&self, detail: Detail<M>) -> Detail<M>;
158 }
159
160 impl<M, T, P> PickNone<M> for ModelField<M, T, P> {
161 fn table(&self, table: Table<M>) -> Table<M> {
162 table
163 }
164
165 fn detail(&self, detail: Detail<M>) -> Detail<M> {
166 detail
167 }
168 }
169}