1use crate::dialect::SqlDialect;
2use crate::error::{PremixError, PremixResult};
3use crate::executor::Executor;
4use crate::executor::IntoExecutor;
5use crate::query::QueryBuilder;
6use sqlx::{Database, FromRow};
7use std::future::Future;
8
9#[inline(never)]
11fn default_model_hook_result() -> Result<(), sqlx::Error> {
12 Ok(())
13}
14
15pub trait ModelHooks {
17 #[inline(never)]
19 fn before_save(&mut self) -> impl Future<Output = Result<(), sqlx::Error>> + Send {
20 async move { default_model_hook_result() }
21 }
22 #[inline(never)]
24 fn after_save(&mut self) -> impl Future<Output = Result<(), sqlx::Error>> + Send {
25 async move { default_model_hook_result() }
26 }
27}
28
29#[derive(Debug, PartialEq)]
32pub enum UpdateResult {
33 Success,
35 VersionConflict,
37 NotFound,
39 NotImplemented,
41}
42
43#[derive(Debug)]
45pub struct FastRow<DB, T>(T, std::marker::PhantomData<DB>);
46
47impl<DB, T> FastRow<DB, T> {
48 pub fn into_inner(self) -> T {
50 self.0
51 }
52}
53
54impl<'r, DB, T> FromRow<'r, DB::Row> for FastRow<DB, T>
55where
56 DB: Database + SqlDialect,
57 T: Model<DB>,
58 usize: sqlx::ColumnIndex<DB::Row>,
59 for<'c> &'c str: sqlx::ColumnIndex<DB::Row>,
60{
61 fn from_row(row: &'r DB::Row) -> Result<Self, sqlx::Error> {
62 T::from_row_fast(row).map(|value| FastRow(value, std::marker::PhantomData))
63 }
64}
65
66#[derive(Debug, Clone)]
69pub struct ValidationError {
70 pub field: String,
72 pub message: String,
74}
75
76pub trait ModelValidation {
78 fn validate(&self) -> Result<(), Vec<ValidationError>> {
80 Ok(())
81 }
82}
83
84pub trait Model<DB: Database>: Sized + Send + Sync + Unpin
89where
90 DB: SqlDialect,
91 for<'r> Self: FromRow<'r, DB::Row>,
92{
93 fn table_name() -> &'static str;
95 fn create_table_sql() -> String;
97 fn list_columns() -> Vec<String>;
99
100 fn save<'a, E>(
102 &'a mut self,
103 executor: E,
104 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send
105 where
106 E: IntoExecutor<'a, DB = DB>;
107
108 fn save_fast<'a, E>(
110 &'a mut self,
111 executor: E,
112 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send
113 where
114 E: IntoExecutor<'a, DB = DB>,
115 {
116 self.save(executor)
117 }
118
119 fn save_ultra<'a, E>(
122 &'a mut self,
123 executor: E,
124 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send
125 where
126 E: IntoExecutor<'a, DB = DB>,
127 {
128 self.save_fast(executor)
129 }
130
131 fn update<'a, E>(
133 &'a mut self,
134 executor: E,
135 ) -> impl Future<Output = Result<UpdateResult, sqlx::Error>> + Send
136 where
137 E: IntoExecutor<'a, DB = DB>;
138
139 fn update_fast<'a, E>(
141 &'a mut self,
142 executor: E,
143 ) -> impl Future<Output = Result<UpdateResult, sqlx::Error>> + Send
144 where
145 E: IntoExecutor<'a, DB = DB>,
146 {
147 self.update(executor)
148 }
149
150 fn update_ultra<'a, E>(
152 &'a mut self,
153 executor: E,
154 ) -> impl Future<Output = Result<UpdateResult, sqlx::Error>> + Send
155 where
156 E: IntoExecutor<'a, DB = DB>,
157 {
158 self.update_fast(executor)
159 }
160
161 fn delete<'a, E>(
164 &'a mut self,
165 executor: E,
166 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send
167 where
168 E: IntoExecutor<'a, DB = DB>;
169
170 fn delete_fast<'a, E>(
172 &'a mut self,
173 executor: E,
174 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send
175 where
176 E: IntoExecutor<'a, DB = DB>,
177 {
178 self.delete(executor)
179 }
180
181 fn delete_ultra<'a, E>(
183 &'a mut self,
184 executor: E,
185 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send
186 where
187 E: IntoExecutor<'a, DB = DB>,
188 {
189 self.delete_fast(executor)
190 }
191 fn has_soft_delete() -> bool;
193 fn sensitive_fields() -> &'static [&'static str] {
195 &[]
196 }
197
198 fn find_by_id<'a, E>(
200 executor: E,
201 id: i32,
202 ) -> impl Future<Output = Result<Option<Self>, sqlx::Error>> + Send
203 where
204 E: IntoExecutor<'a, DB = DB>;
205
206 fn from_row_fast(row: &DB::Row) -> Result<Self, sqlx::Error>
208 where
209 usize: sqlx::ColumnIndex<DB::Row>,
210 for<'c> &'c str: sqlx::ColumnIndex<DB::Row>,
211 for<'r> Self: FromRow<'r, DB::Row>,
212 {
213 <Self as sqlx::FromRow<'_, DB::Row>>::from_row(row)
214 }
215
216 fn raw_sql<'q>(
218 sql: &'q str,
219 ) -> sqlx::query::QueryAs<'q, DB, Self, <DB as Database>::Arguments<'q>> {
220 sqlx::query_as::<DB, Self>(sql)
221 }
222
223 fn raw_sql_fast<'q>(
225 sql: &'q str,
226 ) -> sqlx::query::QueryAs<'q, DB, crate::FastRow<DB, Self>, <DB as Database>::Arguments<'q>>
227 where
228 Self: Sized,
229 usize: sqlx::ColumnIndex<DB::Row>,
230 for<'c> &'c str: sqlx::ColumnIndex<DB::Row>,
231 {
232 sqlx::query_as::<DB, crate::FastRow<DB, Self>>(sql)
233 }
234
235 #[inline(never)]
237 fn eager_load<'a>(
238 _models: &mut [Self],
239 _relation: &str,
240 _executor: Executor<'a, DB>,
241 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send {
242 async move { default_model_hook_result() }
243 }
244 fn find<'a, E>(executor: E) -> QueryBuilder<'a, Self, DB>
246 where
247 E: IntoExecutor<'a, DB = DB>,
248 {
249 QueryBuilder::new(executor.into_executor())
250 }
251
252 fn find_in_pool(pool: &sqlx::Pool<DB>) -> QueryBuilder<'_, Self, DB> {
255 QueryBuilder::new(Executor::Pool(pool))
256 }
257
258 fn find_in_tx(conn: &mut DB::Connection) -> QueryBuilder<'_, Self, DB> {
260 QueryBuilder::new(Executor::Conn(conn))
261 }
262}
263
264pub trait ModelResultExt<DB: Database>: Model<DB>
266where
267 DB: SqlDialect,
268 for<'r> Self: FromRow<'r, DB::Row>,
269{
270 fn save_result<'a, E>(&'a mut self, executor: E) -> impl Future<Output = PremixResult<()>>
272 where
273 E: IntoExecutor<'a, DB = DB>;
274
275 fn update_result<'a, E>(
277 &'a mut self,
278 executor: E,
279 ) -> impl Future<Output = PremixResult<UpdateResult>>
280 where
281 E: IntoExecutor<'a, DB = DB>;
282
283 fn delete_result<'a, E>(&'a mut self, executor: E) -> impl Future<Output = PremixResult<()>>
285 where
286 E: IntoExecutor<'a, DB = DB>;
287}
288
289impl<T, DB> ModelResultExt<DB> for T
290where
291 DB: SqlDialect,
292 T: Model<DB>,
293 for<'r> T: FromRow<'r, DB::Row>,
294{
295 #[allow(clippy::manual_async_fn)]
296 fn save_result<'a, E>(&'a mut self, executor: E) -> impl Future<Output = PremixResult<()>>
297 where
298 E: IntoExecutor<'a, DB = DB>,
299 {
300 async move { self.save(executor).await.map_err(PremixError::from) }
301 }
302
303 #[allow(clippy::manual_async_fn)]
304 fn update_result<'a, E>(
305 &'a mut self,
306 executor: E,
307 ) -> impl Future<Output = PremixResult<UpdateResult>>
308 where
309 E: IntoExecutor<'a, DB = DB>,
310 {
311 async move { self.update(executor).await.map_err(PremixError::from) }
312 }
313
314 #[allow(clippy::manual_async_fn)]
315 fn delete_result<'a, E>(&'a mut self, executor: E) -> impl Future<Output = PremixResult<()>>
316 where
317 E: IntoExecutor<'a, DB = DB>,
318 {
319 async move { self.delete(executor).await.map_err(PremixError::from) }
320 }
321}