1use crate::dialect::SqlDialect;
2use crate::executor::Executor;
3use crate::executor::IntoExecutor;
4use crate::query::QueryBuilder;
5use sqlx::{Database, FromRow};
6use std::future::Future;
7
8#[inline(never)]
10fn default_model_hook_result() -> Result<(), sqlx::Error> {
11 Ok(())
12}
13
14pub trait ModelHooks {
16 #[inline(never)]
18 fn before_save(&mut self) -> impl Future<Output = Result<(), sqlx::Error>> + Send {
19 async move { default_model_hook_result() }
20 }
21 #[inline(never)]
23 fn after_save(&mut self) -> impl Future<Output = Result<(), sqlx::Error>> + Send {
24 async move { default_model_hook_result() }
25 }
26}
27
28#[derive(Debug, PartialEq)]
31pub enum UpdateResult {
32 Success,
34 VersionConflict,
36 NotFound,
38 NotImplemented,
40}
41
42#[derive(Debug)]
44pub struct FastRow<DB, T>(T, std::marker::PhantomData<DB>);
45
46impl<DB, T> FastRow<DB, T> {
47 pub fn into_inner(self) -> T {
49 self.0
50 }
51}
52
53impl<'r, DB, T> FromRow<'r, DB::Row> for FastRow<DB, T>
54where
55 DB: Database + SqlDialect,
56 T: Model<DB>,
57 usize: sqlx::ColumnIndex<DB::Row>,
58 for<'c> &'c str: sqlx::ColumnIndex<DB::Row>,
59{
60 fn from_row(row: &'r DB::Row) -> Result<Self, sqlx::Error> {
61 T::from_row_fast(row).map(|value| FastRow(value, std::marker::PhantomData))
62 }
63}
64
65#[derive(Debug, Clone)]
68pub struct ValidationError {
69 pub field: String,
71 pub message: String,
73}
74
75pub trait ModelValidation {
77 fn validate(&self) -> Result<(), Vec<ValidationError>> {
79 Ok(())
80 }
81}
82
83pub trait Model<DB: Database>: Sized + Send + Sync + Unpin
88where
89 DB: SqlDialect,
90 for<'r> Self: FromRow<'r, DB::Row>,
91{
92 fn table_name() -> &'static str;
94 fn create_table_sql() -> String;
96 fn list_columns() -> Vec<String>;
98
99 fn save<'a, E>(
101 &'a mut self,
102 executor: E,
103 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send
104 where
105 E: IntoExecutor<'a, DB = DB>;
106
107 fn save_fast<'a, E>(
109 &'a mut self,
110 executor: E,
111 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send
112 where
113 E: IntoExecutor<'a, DB = DB>,
114 {
115 self.save(executor)
116 }
117
118 fn save_ultra<'a, E>(
121 &'a mut self,
122 executor: E,
123 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send
124 where
125 E: IntoExecutor<'a, DB = DB>,
126 {
127 self.save_fast(executor)
128 }
129
130 fn update<'a, E>(
132 &'a mut self,
133 executor: E,
134 ) -> impl Future<Output = Result<UpdateResult, sqlx::Error>> + Send
135 where
136 E: IntoExecutor<'a, DB = DB>;
137
138 fn update_fast<'a, E>(
140 &'a mut self,
141 executor: E,
142 ) -> impl Future<Output = Result<UpdateResult, sqlx::Error>> + Send
143 where
144 E: IntoExecutor<'a, DB = DB>,
145 {
146 self.update(executor)
147 }
148
149 fn update_ultra<'a, E>(
151 &'a mut self,
152 executor: E,
153 ) -> impl Future<Output = Result<UpdateResult, sqlx::Error>> + Send
154 where
155 E: IntoExecutor<'a, DB = DB>,
156 {
157 self.update_fast(executor)
158 }
159
160 fn delete<'a, E>(
163 &'a mut self,
164 executor: E,
165 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send
166 where
167 E: IntoExecutor<'a, DB = DB>;
168
169 fn delete_fast<'a, E>(
171 &'a mut self,
172 executor: E,
173 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send
174 where
175 E: IntoExecutor<'a, DB = DB>,
176 {
177 self.delete(executor)
178 }
179
180 fn delete_ultra<'a, E>(
182 &'a mut self,
183 executor: E,
184 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send
185 where
186 E: IntoExecutor<'a, DB = DB>,
187 {
188 self.delete_fast(executor)
189 }
190 fn has_soft_delete() -> bool;
192 fn sensitive_fields() -> &'static [&'static str] {
194 &[]
195 }
196
197 fn find_by_id<'a, E>(
199 executor: E,
200 id: i32,
201 ) -> impl Future<Output = Result<Option<Self>, sqlx::Error>> + Send
202 where
203 E: IntoExecutor<'a, DB = DB>;
204
205 fn from_row_fast(row: &DB::Row) -> Result<Self, sqlx::Error>
207 where
208 usize: sqlx::ColumnIndex<DB::Row>,
209 for<'c> &'c str: sqlx::ColumnIndex<DB::Row>,
210 for<'r> Self: FromRow<'r, DB::Row>,
211 {
212 <Self as sqlx::FromRow<'_, DB::Row>>::from_row(row)
213 }
214
215 fn raw_sql<'q>(
217 sql: &'q str,
218 ) -> sqlx::query::QueryAs<'q, DB, Self, <DB as Database>::Arguments<'q>> {
219 sqlx::query_as::<DB, Self>(sql)
220 }
221
222 fn raw_sql_fast<'q>(
224 sql: &'q str,
225 ) -> sqlx::query::QueryAs<'q, DB, crate::FastRow<DB, Self>, <DB as Database>::Arguments<'q>>
226 where
227 Self: Sized,
228 usize: sqlx::ColumnIndex<DB::Row>,
229 for<'c> &'c str: sqlx::ColumnIndex<DB::Row>,
230 {
231 sqlx::query_as::<DB, crate::FastRow<DB, Self>>(sql)
232 }
233
234 #[inline(never)]
236 fn eager_load<'a>(
237 _models: &mut [Self],
238 _relation: &str,
239 _executor: Executor<'a, DB>,
240 ) -> impl Future<Output = Result<(), sqlx::Error>> + Send {
241 async move { default_model_hook_result() }
242 }
243 fn find<'a, E>(executor: E) -> QueryBuilder<'a, Self, DB>
245 where
246 E: IntoExecutor<'a, DB = DB>,
247 {
248 QueryBuilder::new(executor.into_executor())
249 }
250
251 fn find_in_pool(pool: &sqlx::Pool<DB>) -> QueryBuilder<'_, Self, DB> {
254 QueryBuilder::new(Executor::Pool(pool))
255 }
256
257 fn find_in_tx(conn: &mut DB::Connection) -> QueryBuilder<'_, Self, DB> {
259 QueryBuilder::new(Executor::Conn(conn))
260 }
261}