1use std::collections::HashMap;
7use std::fmt::Debug;
8use chrono::{DateTime, Utc};
9use serde::{Serialize, Deserialize};
10use sqlx::{Pool, Postgres, Row};
11use uuid::Uuid;
12
13use crate::error::{ModelError, ModelResult};
14use crate::query::QueryBuilder;
15
16#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18pub enum PrimaryKey {
19 Integer(i64),
21 Uuid(Uuid),
23 Composite(HashMap<String, String>),
25}
26
27impl std::fmt::Display for PrimaryKey {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 match self {
30 PrimaryKey::Integer(id) => write!(f, "{}", id),
31 PrimaryKey::Uuid(id) => write!(f, "{}", id),
32 PrimaryKey::Composite(fields) => {
33 let pairs: Vec<String> = fields.iter()
34 .map(|(k, v)| format!("{}:{}", k, v))
35 .collect();
36 write!(f, "{}", pairs.join(","))
37 }
38 }
39 }
40}
41
42impl PrimaryKey {
43 pub fn as_i64(&self) -> Option<i64> {
44 match self {
45 PrimaryKey::Integer(id) => Some(*id),
46 _ => None,
47 }
48 }
49
50 pub fn as_uuid(&self) -> Option<Uuid> {
51 match self {
52 PrimaryKey::Uuid(id) => Some(*id),
53 _ => None,
54 }
55 }
56}
57
58pub trait Model: Send + Sync + Debug + Serialize + for<'de> Deserialize<'de> {
60 type PrimaryKey: Clone + Send + Sync + Debug + std::fmt::Display;
62
63 fn table_name() -> &'static str;
65
66 fn primary_key_name() -> &'static str {
68 "id"
69 }
70
71 fn primary_key(&self) -> Option<Self::PrimaryKey>;
73
74 fn set_primary_key(&mut self, key: Self::PrimaryKey);
76
77 fn uses_timestamps() -> bool {
79 false
80 }
81
82 fn uses_soft_deletes() -> bool {
84 false
85 }
86
87 fn created_at(&self) -> Option<DateTime<Utc>> {
89 None
90 }
91
92 fn set_created_at(&mut self, _timestamp: DateTime<Utc>) {}
94
95 fn updated_at(&self) -> Option<DateTime<Utc>> {
97 None
98 }
99
100 fn set_updated_at(&mut self, _timestamp: DateTime<Utc>) {}
102
103 fn deleted_at(&self) -> Option<DateTime<Utc>> {
105 None
106 }
107
108 fn set_deleted_at(&mut self, _timestamp: Option<DateTime<Utc>>) {}
110
111 fn is_soft_deleted(&self) -> bool {
113 self.deleted_at().is_some()
114 }
115
116 async fn find(pool: &Pool<Postgres>, id: Self::PrimaryKey) -> ModelResult<Option<Self>>
119 where
120 Self: Sized,
121 {
122 let query: QueryBuilder<Self> = QueryBuilder::new()
123 .select("*")
124 .from(Self::table_name())
125 .where_eq(Self::primary_key_name(), id.to_string());
126
127 let sql = query.to_sql();
128 let row = sqlx::query(&sql)
129 .fetch_optional(pool)
130 .await?;
131
132 match row {
133 Some(row) => {
134 let model = Self::from_row(&row)?;
135 Ok(Some(model))
136 }
137 None => Ok(None),
138 }
139 }
140
141 async fn find_or_fail(pool: &Pool<Postgres>, id: Self::PrimaryKey) -> ModelResult<Self>
143 where
144 Self: Sized,
145 {
146 Self::find(pool, id)
147 .await?
148 .ok_or_else(|| ModelError::NotFound(Self::table_name().to_string()))
149 }
150
151 async fn create(pool: &Pool<Postgres>, mut model: Self) -> ModelResult<Self>
153 where
154 Self: Sized,
155 {
156 if Self::uses_timestamps() {
158 let now = Utc::now();
159 model.set_created_at(now);
160 model.set_updated_at(now);
161 }
162
163 let insert_sql = format!("INSERT INTO {} DEFAULT VALUES RETURNING *", Self::table_name());
168 let row = sqlx::query(&insert_sql)
169 .fetch_one(pool)
170 .await?;
171
172 Self::from_row(&row)
173 }
174
175 async fn update(&mut self, pool: &Pool<Postgres>) -> ModelResult<()> {
177 if let Some(pk) = self.primary_key() {
178 if Self::uses_timestamps() {
180 self.set_updated_at(Utc::now());
181 }
182
183 let update_sql = format!(
186 "UPDATE {} SET updated_at = NOW() WHERE {} = $1",
187 Self::table_name(),
188 Self::primary_key_name()
189 );
190
191 sqlx::query(&update_sql)
192 .bind(pk.to_string())
193 .execute(pool)
194 .await?;
195
196 Ok(())
197 } else {
198 Err(ModelError::MissingPrimaryKey)
199 }
200 }
201
202 async fn delete(self, pool: &Pool<Postgres>) -> ModelResult<()> {
204 if let Some(pk) = self.primary_key() {
205 if Self::uses_soft_deletes() {
206 let soft_delete_sql = format!(
208 "UPDATE {} SET deleted_at = NOW() WHERE {} = $1",
209 Self::table_name(),
210 Self::primary_key_name()
211 );
212
213 sqlx::query(&soft_delete_sql)
214 .bind(pk.to_string())
215 .execute(pool)
216 .await?;
217 } else {
218 let delete_sql = format!(
220 "DELETE FROM {} WHERE {} = $1",
221 Self::table_name(),
222 Self::primary_key_name()
223 );
224
225 sqlx::query(&delete_sql)
226 .bind(pk.to_string())
227 .execute(pool)
228 .await?;
229 }
230
231 Ok(())
232 } else {
233 Err(ModelError::MissingPrimaryKey)
234 }
235 }
236 fn query() -> QueryBuilder<Self>
241 where
242 Self: Sized,
243 {
244 let builder = QueryBuilder::new()
245 .from(Self::table_name());
246
247 if Self::uses_soft_deletes() {
249 builder.where_null("deleted_at")
250 } else {
251 builder
252 }
253 }
254
255 async fn all(pool: &Pool<Postgres>) -> ModelResult<Vec<Self>>
257 where
258 Self: Sized,
259 {
260 let query = Self::query().select("*");
261 let sql = query.to_sql();
262
263 let rows = sqlx::query(&sql)
264 .fetch_all(pool)
265 .await?;
266
267 let mut models = Vec::new();
268 for row in rows {
269 models.push(Self::from_row(&row)?);
270 }
271
272 Ok(models)
273 }
274
275 async fn count(pool: &Pool<Postgres>) -> ModelResult<i64>
277 where
278 Self: Sized,
279 {
280 let query = Self::query().select("COUNT(*)");
281 let sql = query.to_sql();
282
283 let row = sqlx::query(&sql)
284 .fetch_one(pool)
285 .await?;
286
287 let count: i64 = row.try_get(0)?;
288 Ok(count)
289 }
290 fn from_row(row: &sqlx::postgres::PgRow) -> ModelResult<Self>
295 where
296 Self: Sized;
297
298 fn to_fields(&self) -> HashMap<String, serde_json::Value>;
301}
302
303pub trait ModelExtensions: Model {
306 async fn refresh(&mut self, pool: &Pool<Postgres>) -> ModelResult<()>
308 where
309 Self: Sized,
310 {
311 if let Some(pk) = self.primary_key() {
312 if let Some(refreshed) = Self::find(pool, pk).await? {
313 *self = refreshed;
314 Ok(())
315 } else {
316 Err(ModelError::NotFound(Self::table_name().to_string()))
317 }
318 } else {
319 Err(ModelError::MissingPrimaryKey)
320 }
321 }
322
323 async fn exists(&self, pool: &Pool<Postgres>) -> ModelResult<bool>
325 where
326 Self: Sized,
327 {
328 if let Some(pk) = self.primary_key() {
329 let exists = Self::find(pool, pk).await?.is_some();
330 Ok(exists)
331 } else {
332 Ok(false)
333 }
334 }
335
336 async fn save(&mut self, pool: &Pool<Postgres>) -> ModelResult<()>
338 where
339 Self: Sized,
340 {
341 if self.primary_key().is_some() && self.exists(pool).await? {
342 self.update(pool).await
344 } else {
345 Err(ModelError::Validation("Cannot save new model without primary key support from derive macro".to_string()))
348 }
349 }
350}
351
352impl<T: Model> ModelExtensions for T {}
354