tablo_core/table/
model.rs1use toasty::{schema::Model, stmt::IntoExpr};
4
5use crate::{Detail, Table};
6
7#[diagnostic::on_unimplemented(
16 message = "`{Self}` declares no default columns",
17 label = "a resource's model implements `ModelColumns`",
18 note = "add `#[derive(tablo::ModelColumns)]` to `{Self}`, or `impl tablo::ModelColumns for \
19 {Self} {{ const TENANT_COLUMNS: &[&str] = &[..]; }}`, naming its `TenantId` fields, \
20 when its resources declare their own table and detail page"
21)]
22pub trait ModelColumns: Model + IntoExpr<Self> + Sized {
23 const TENANT_COLUMNS: &'static [&'static str];
28
29 fn table() -> Table<Self> {
31 Table::new(())
32 }
33
34 fn detail() -> Detail<Self> {
36 Detail::empty()
37 }
38
39 fn version(&self) -> Option<u64> {
45 None
46 }
47}
48
49#[doc(hidden)]
54pub mod pick {
55 use toasty::{
56 schema::{Field, Model},
57 stmt::List,
58 };
59
60 use crate::{
61 BooleanColumn, DateColumn, DateValue, Detail, EmbeddedColumn, Lens, RepeaterColumn, Table,
62 TextColumn,
63 schema::{EmbeddedForm, FormInput},
64 values::FormScalar,
65 };
66
67 pub struct ModelField<M, T, P = T>(pub Lens<M, T, P>);
69
70 pub type PathOf<T> = <T as Field>::ExprTarget;
72
73 pub trait PickDate<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> PickDate<M> for &&&&&&ModelField<M, T>
81 where
82 M: Model + Send + Sync + 'static,
83 T: DateValue + FormScalar,
84 {
85 fn table(&self, table: Table<M>) -> Table<M> {
86 table.column(DateColumn::new(self.0.clone()).sortable())
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 PickToggle<M> {
96 fn table(&self, table: Table<M>) -> Table<M>;
97 fn detail(&self, detail: Detail<M>) -> Detail<M>;
98 }
99
100 impl<M> PickToggle<M> for &&&&&ModelField<M, bool>
101 where
102 M: Model + Send + Sync + 'static,
103 {
104 fn table(&self, table: Table<M>) -> Table<M> {
105 table.column(BooleanColumn::new(self.0.clone()).sortable())
106 }
107
108 fn detail(&self, detail: Detail<M>) -> Detail<M> {
109 detail.column(BooleanColumn::new(self.0.clone()))
110 }
111 }
112
113 pub trait PickString<M> {
115 fn table(&self, table: Table<M>) -> Table<M>;
116 fn detail(&self, detail: Detail<M>) -> Detail<M>;
117 }
118
119 impl<M, T> PickString<M> for &&&&ModelField<M, T>
120 where
121 M: Model + Send + Sync + 'static,
122 T: FormScalar + Field<Inner = String> + Send + Sync + 'static,
123 {
124 fn table(&self, table: Table<M>) -> Table<M> {
125 table.column(TextColumn::new(self.0.clone()).sortable().searchable())
126 }
127
128 fn detail(&self, detail: Detail<M>) -> Detail<M> {
129 detail.column(TextColumn::new(self.0.clone()))
130 }
131 }
132
133 pub trait PickScalar<M> {
135 fn table(&self, table: Table<M>) -> Table<M>;
136 fn detail(&self, detail: Detail<M>) -> Detail<M>;
137 }
138
139 impl<M, T> PickScalar<M> for &&&ModelField<M, T>
140 where
141 M: Model + Send + Sync + 'static,
142 T: FormScalar + Send + Sync + 'static,
143 {
144 fn table(&self, table: Table<M>) -> Table<M> {
145 table.column(TextColumn::new(self.0.clone()).sortable())
146 }
147
148 fn detail(&self, detail: Detail<M>) -> Detail<M> {
149 detail.column(TextColumn::new(self.0.clone()))
150 }
151 }
152
153 pub trait PickEmbedded<M> {
155 fn table(&self, table: Table<M>) -> Table<M>;
156 fn detail(&self, detail: Detail<M>) -> Detail<M>;
157 }
158
159 impl<M, T> PickEmbedded<M> for &&ModelField<M, T>
160 where
161 M: Model + Send + Sync + 'static,
162 T: EmbeddedForm + Send + Sync + 'static,
163 {
164 fn table(&self, table: Table<M>) -> Table<M> {
165 table
166 }
167
168 fn detail(&self, detail: Detail<M>) -> Detail<M> {
169 detail.column(EmbeddedColumn::new(self.0.clone()))
170 }
171 }
172
173 pub trait PickRepeater<M> {
175 fn table(&self, table: Table<M>) -> Table<M>;
176 fn detail(&self, detail: Detail<M>) -> Detail<M>;
177 }
178
179 impl<M, T> PickRepeater<M> for &ModelField<M, Vec<T>, List<T>>
180 where
181 M: Model + Send + Sync + 'static,
182 T: FormInput,
183 {
184 fn table(&self, table: Table<M>) -> Table<M> {
185 table
186 }
187
188 fn detail(&self, detail: Detail<M>) -> Detail<M> {
189 detail.column(RepeaterColumn::new(self.0.clone()))
190 }
191 }
192
193 pub trait PickNone<M> {
195 fn table(&self, table: Table<M>) -> Table<M>;
196 fn detail(&self, detail: Detail<M>) -> Detail<M>;
197 }
198
199 impl<M, T, P> PickNone<M> for ModelField<M, T, P> {
200 fn table(&self, table: Table<M>) -> Table<M> {
201 table
202 }
203
204 fn detail(&self, detail: Detail<M>) -> Detail<M> {
205 detail
206 }
207 }
208}
209
210#[cfg(test)]
211mod tests;