1use crate::dialect::SqlDialect;
2use crate::error::{PremixError, PremixResult};
3use crate::executor::Executor;
4use crate::executor::IntoExecutor;
5use crate::query::QueryBuilder;
6use serde_json::Value;
7use sqlx::{Database, FromRow};
8use std::future::Future;
9
10#[inline(never)]
12fn default_model_hook_result() -> Result<(), sqlx::Error> {
13 Ok(())
14}
15
16pub trait ModelHooks {
18 #[inline(never)]
20 fn before_save(&mut self) -> impl Future<Output = Result<(), sqlx::Error>> + Send {
21 async move { default_model_hook_result() }
22 }
23 #[inline(never)]
25 fn after_save(&mut self) -> impl Future<Output = Result<(), sqlx::Error>> + Send {
26 async move { default_model_hook_result() }
27 }
28}
29
30#[derive(Debug, PartialEq)]
33pub enum UpdateResult {
34 Success,
36 VersionConflict,
38 NotFound,
40 NotImplemented,
42}
43
44#[derive(Debug)]
46pub struct FastRow<DB, T>(T, std::marker::PhantomData<DB>);
47
48impl<DB, T> FastRow<DB, T> {
49 pub fn into_inner(self) -> T {
51 self.0
52 }
53}
54
55impl<'r, DB, T> FromRow<'r, DB::Row> for FastRow<DB, T>
56where
57 DB: Database + SqlDialect,
58 T: Model<DB>,
59 usize: sqlx::ColumnIndex<DB::Row>,
60 for<'c> &'c str: sqlx::ColumnIndex<DB::Row>,
61{
62 fn from_row(row: &'r DB::Row) -> Result<Self, sqlx::Error> {
63 T::from_row_fast(row).map(|value| FastRow(value, std::marker::PhantomData))
64 }
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69pub struct Relation {
70 name: &'static str,
71}
72
73impl Relation {
74 pub const fn new(name: &'static str) -> Self {
76 Self { name }
77 }
78
79 pub const fn name(&self) -> &'static str {
81 self.name
82 }
83}
84
85impl From<Relation> for String {
86 fn from(value: Relation) -> Self {
87 value.name.to_string()
88 }
89}
90
91impl From<&Relation> for String {
92 fn from(value: &Relation) -> Self {
93 value.name.to_string()
94 }
95}
96
97#[derive(Debug, Clone)]
100pub struct ValidationError {
101 pub field: String,
103 pub message: String,
105}
106
107pub trait ModelValidation {
109 fn validate(&self) -> Result<(), Vec<ValidationError>> {
111 Ok(())
112 }
113}
114
115pub trait Model<DB: Database>: Sized + Send + Sync + Unpin
120where
121 DB: SqlDialect,
122 for<'r> Self: FromRow<'r, DB::Row>,
123{
124 fn table_name() -> &'static str;
126 fn create_table_sql() -> String;
128 fn list_columns() -> Vec<String>;
130
131 fn save<'a, E>(
133 &'a mut self,
134 executor: E,
135 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send
136 where
137 E: IntoExecutor<'a, DB = DB>;
138
139 fn save_fast<'a, E>(
141 &'a mut self,
142 executor: E,
143 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send
144 where
145 E: IntoExecutor<'a, DB = DB>,
146 {
147 self.save(executor)
148 }
149
150 fn save_ultra<'a, E>(
153 &'a mut self,
154 executor: E,
155 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send
156 where
157 E: IntoExecutor<'a, DB = DB>,
158 {
159 self.save_fast(executor)
160 }
161
162 fn update<'a, E>(
164 &'a mut self,
165 executor: E,
166 ) -> impl Future<Output = Result<UpdateResult, sqlx::Error>> + Send
167 where
168 E: IntoExecutor<'a, DB = DB>;
169
170 fn update_fast<'a, E>(
172 &'a mut self,
173 executor: E,
174 ) -> impl Future<Output = Result<UpdateResult, sqlx::Error>> + Send
175 where
176 E: IntoExecutor<'a, DB = DB>,
177 {
178 self.update(executor)
179 }
180
181 fn update_ultra<'a, E>(
183 &'a mut self,
184 executor: E,
185 ) -> impl Future<Output = Result<UpdateResult, sqlx::Error>> + Send
186 where
187 E: IntoExecutor<'a, DB = DB>,
188 {
189 self.update_fast(executor)
190 }
191
192 fn delete<'a, E>(
195 &'a mut self,
196 executor: E,
197 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send
198 where
199 E: IntoExecutor<'a, DB = DB>;
200
201 fn delete_fast<'a, E>(
203 &'a mut self,
204 executor: E,
205 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send
206 where
207 E: IntoExecutor<'a, DB = DB>,
208 {
209 self.delete(executor)
210 }
211
212 fn delete_ultra<'a, E>(
214 &'a mut self,
215 executor: E,
216 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send
217 where
218 E: IntoExecutor<'a, DB = DB>,
219 {
220 self.delete_fast(executor)
221 }
222 fn has_soft_delete() -> bool;
224 fn sensitive_fields() -> &'static [&'static str] {
226 &[]
227 }
228
229 fn relation_names() -> &'static [&'static str] {
231 &[]
232 }
233
234 fn default_includes() -> &'static [&'static str] {
236 &[]
237 }
238
239 fn find_by_id<'a, E>(
241 executor: E,
242 id: i32,
243 ) -> impl Future<Output = Result<Option<Self>, sqlx::Error>> + Send
244 where
245 E: IntoExecutor<'a, DB = DB>;
246
247 fn from_row_fast(row: &DB::Row) -> Result<Self, sqlx::Error>
249 where
250 usize: sqlx::ColumnIndex<DB::Row>,
251 for<'c> &'c str: sqlx::ColumnIndex<DB::Row>,
252 for<'r> Self: FromRow<'r, DB::Row>,
253 {
254 <Self as sqlx::FromRow<'_, DB::Row>>::from_row(row)
255 }
256
257 fn raw_sql<'q>(
259 sql: &'q str,
260 ) -> sqlx::query::QueryAs<'q, DB, Self, <DB as Database>::Arguments<'q>> {
261 sqlx::query_as::<DB, Self>(sql)
262 }
263
264 fn raw_sql_fast<'q>(
266 sql: &'q str,
267 ) -> sqlx::query::QueryAs<'q, DB, crate::FastRow<DB, Self>, <DB as Database>::Arguments<'q>>
268 where
269 Self: Sized,
270 usize: sqlx::ColumnIndex<DB::Row>,
271 for<'c> &'c str: sqlx::ColumnIndex<DB::Row>,
272 {
273 sqlx::query_as::<DB, crate::FastRow<DB, Self>>(sql)
274 }
275
276 #[inline(never)]
278 fn eager_load<'a>(
279 _models: &mut [Self],
280 _relation: &str,
281 _executor: Executor<'a, DB>,
282 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send {
283 async move { default_model_hook_result() }
284 }
285 fn find<'a, E>(executor: E) -> QueryBuilder<'a, Self, DB>
287 where
288 E: IntoExecutor<'a, DB = DB>,
289 {
290 QueryBuilder::new(executor.into_executor())
291 }
292
293 fn all<'a, E>(executor: E) -> impl Future<Output = Result<Vec<Self>, sqlx::Error>> + Send
295 where
296 E: IntoExecutor<'a, DB = DB>,
297 for<'q> <DB as Database>::Arguments<'q>: sqlx::IntoArguments<'q, DB>,
298 for<'c> &'c mut <DB as Database>::Connection: sqlx::Executor<'c, Database = DB>,
299 for<'c> &'c str: sqlx::ColumnIndex<DB::Row>,
300 DB::Connection: Send,
301 Self: Send,
302 String: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
303 i64: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
304 f64: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
305 bool: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
306 Option<String>: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
307 uuid::Uuid: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
308 chrono::DateTime<chrono::Utc>: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
309 chrono::NaiveDateTime: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
310 chrono::NaiveDate: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
311 sqlx::types::Json<serde_json::Value>: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
312 {
313 async move { Self::find(executor).all().await }
314 }
315
316 fn find_one<'a, E>(
318 executor: E,
319 id: i32,
320 ) -> impl Future<Output = Result<Option<Self>, sqlx::Error>> + Send
321 where
322 E: IntoExecutor<'a, DB = DB>,
323 {
324 Self::find_by_id(executor, id)
325 }
326
327 fn create<E>(
329 executor: E,
330 mut instance: Self,
331 ) -> impl Future<Output = Result<Self, sqlx::Error>> + Send
332 where
333 for<'e> E: IntoExecutor<'e, DB = DB> + Send,
334 {
335 async move {
336 instance.save(executor).await?;
337 Ok(instance)
338 }
339 }
340
341 fn update_by_id<'a, E>(
343 executor: E,
344 id: i32,
345 json_patch: Value,
346 ) -> impl Future<Output = Result<u64, sqlx::Error>> + Send
347 where
348 E: IntoExecutor<'a, DB = DB>,
349 for<'q> <DB as Database>::Arguments<'q>: sqlx::IntoArguments<'q, DB>,
350 for<'c> &'c mut <DB as Database>::Connection: sqlx::Executor<'c, Database = DB>,
351 for<'c> &'c str: sqlx::ColumnIndex<DB::Row>,
352 DB::Connection: Send,
353 Self: Send,
354 String: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
355 i64: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
356 f64: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
357 bool: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
358 Option<String>: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
359 uuid::Uuid: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
360 chrono::DateTime<chrono::Utc>: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
361 chrono::NaiveDateTime: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
362 chrono::NaiveDate: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
363 sqlx::types::Json<serde_json::Value>: for<'q> sqlx::Encode<'q, DB> + sqlx::Type<DB>,
364 {
365 async move {
366 Self::find(executor)
367 .filter_eq("id", id)
368 .update(json_patch)
369 .await
370 }
371 }
372
373 fn find_in_pool(pool: &sqlx::Pool<DB>) -> QueryBuilder<'_, Self, DB> {
376 QueryBuilder::new(Executor::Pool(pool))
377 }
378
379 fn find_in_tx(conn: &mut DB::Connection) -> QueryBuilder<'_, Self, DB> {
381 QueryBuilder::new(Executor::Conn(conn))
382 }
383}
384
385pub trait ModelResultExt<DB: Database>: Model<DB>
387where
388 DB: SqlDialect,
389 for<'r> Self: FromRow<'r, DB::Row>,
390{
391 fn save_result<'a, E>(&'a mut self, executor: E) -> impl Future<Output = PremixResult<()>>
393 where
394 E: IntoExecutor<'a, DB = DB>;
395
396 fn update_result<'a, E>(
398 &'a mut self,
399 executor: E,
400 ) -> impl Future<Output = PremixResult<UpdateResult>>
401 where
402 E: IntoExecutor<'a, DB = DB>;
403
404 fn delete_result<'a, E>(&'a mut self, executor: E) -> impl Future<Output = PremixResult<()>>
406 where
407 E: IntoExecutor<'a, DB = DB>;
408}
409
410impl<T, DB> ModelResultExt<DB> for T
411where
412 DB: SqlDialect,
413 T: Model<DB>,
414 for<'r> T: FromRow<'r, DB::Row>,
415{
416 #[allow(clippy::manual_async_fn)]
417 fn save_result<'a, E>(&'a mut self, executor: E) -> impl Future<Output = PremixResult<()>>
418 where
419 E: IntoExecutor<'a, DB = DB>,
420 {
421 async move { self.save(executor).await.map_err(PremixError::from) }
422 }
423
424 #[allow(clippy::manual_async_fn)]
425 fn update_result<'a, E>(
426 &'a mut self,
427 executor: E,
428 ) -> impl Future<Output = PremixResult<UpdateResult>>
429 where
430 E: IntoExecutor<'a, DB = DB>,
431 {
432 async move { self.update(executor).await.map_err(PremixError::from) }
433 }
434
435 #[allow(clippy::manual_async_fn)]
436 fn delete_result<'a, E>(&'a mut self, executor: E) -> impl Future<Output = PremixResult<()>>
437 where
438 E: IntoExecutor<'a, DB = DB>,
439 {
440 async move { self.delete(executor).await.map_err(PremixError::from) }
441 }
442}