1use std::marker::PhantomData;
4
5use super::backend::Backend;
6use super::pool::DbPool;
7use super::{DbError, Model, Value};
8
9pub trait Repository<T: Model, ID> {
13 async fn find_by_id(&self, id: ID) -> Result<Option<T>, DbError>;
14 async fn find_all(&self) -> Result<Vec<T>, DbError>;
15 async fn save(&self, entity: &T) -> Result<T, DbError>;
16 async fn save_all(&self, entities: &[T]) -> Result<Vec<T>, DbError>;
17 async fn delete_by_id(&self, id: ID) -> Result<(), DbError>;
18 async fn delete_all_by_id(&self, ids: &[ID]) -> Result<(), DbError>;
19 async fn count(&self) -> Result<i64, DbError>;
20 async fn exists_by_id(&self, id: ID) -> Result<bool, DbError>;
21}
22
23pub struct ModelRepository<'a, T: Model, ID> {
27 #[doc(hidden)]
28 pub pool: &'a DbPool,
29 _phantom: PhantomData<(T, ID)>,
30}
31
32impl<'a, T: Model, ID> ModelRepository<'a, T, ID> {
33 pub fn new(pool: &'a DbPool) -> Self {
34 ModelRepository { pool, _phantom: PhantomData }
35 }
36}
37
38impl<'a, T: Model> Repository<T, i64> for ModelRepository<'a, T, i64> {
41 async fn find_by_id(&self, id: i64) -> Result<Option<T>, DbError> {
42 let sql = format!(
43 "SELECT * FROM {} WHERE {} = {}",
44 T::table_name(),
45 T::primary_key_name(),
46 placeholder(self.pool.backend(), 1),
47 );
48 let rows = self.pool.query_rows(&sql, &[Value::Int(id)]).await?;
49 match rows.into_iter().next() {
50 Some(row) => Ok(Some(T::from_row(&row)?)),
51 None => Ok(None),
52 }
53 }
54
55 async fn find_all(&self) -> Result<Vec<T>, DbError> {
56 let sql = format!("SELECT * FROM {}", T::table_name());
57 let rows = self.pool.query_rows(&sql, &[]).await?;
58 rows.iter().map(|r| T::from_row(r)).collect()
59 }
60
61 async fn save(&self, entity: &T) -> Result<T, DbError> {
62 let pk_val = entity.primary_key_value();
63 let is_new = matches!(&pk_val, Value::Int(n) if *n == 0) || matches!(&pk_val, Value::Null);
64 if is_new {
65 insert_entity(self.pool, entity).await
66 } else {
67 update_entity(self.pool, entity).await
68 }
69 }
70
71 async fn save_all(&self, entities: &[T]) -> Result<Vec<T>, DbError> {
72 let mut result = Vec::with_capacity(entities.len());
73 for e in entities {
74 result.push(self.save(e).await?);
75 }
76 Ok(result)
77 }
78
79 async fn delete_by_id(&self, id: i64) -> Result<(), DbError> {
80 let sql = format!(
81 "DELETE FROM {} WHERE {} = {}",
82 T::table_name(),
83 T::primary_key_name(),
84 placeholder(self.pool.backend(), 1),
85 );
86 self.pool.execute(&sql, &[Value::Int(id)]).await?;
87 Ok(())
88 }
89
90 async fn delete_all_by_id(&self, ids: &[i64]) -> Result<(), DbError> {
91 for &id in ids {
92 self.delete_by_id(id).await?;
93 }
94 Ok(())
95 }
96
97 async fn count(&self) -> Result<i64, DbError> {
98 let sql = format!("SELECT COUNT(*) FROM {}", T::table_name());
99 let rows = self.pool.query_rows(&sql, &[]).await?;
100 extract_count(rows)
101 }
102
103 async fn exists_by_id(&self, id: i64) -> Result<bool, DbError> {
104 Ok(self.find_by_id(id).await?.is_some())
105 }
106}
107
108#[doc(hidden)]
113pub fn placeholder(backend: Backend, _pos: usize) -> String {
114 match backend {
115 #[cfg(feature = "model-sqlite")]
116 Backend::Sqlite => "?".to_string(),
117 #[cfg(feature = "model-postgres")]
118 Backend::Postgres => format!("${}", _pos),
119 #[cfg(feature = "model-mysql")]
120 Backend::MySql => "?".to_string(),
121 }
122}
123
124#[doc(hidden)]
126pub fn placeholders(backend: Backend, count: usize) -> String {
127 (1..=count).map(|i| placeholder(backend, i)).collect::<Vec<_>>().join(", ")
128}
129
130#[doc(hidden)]
133pub async fn insert_entity<T: Model>(pool: &DbPool, entity: &T) -> Result<T, DbError> {
134 let backend = pool.backend();
135 let values = entity.to_values();
136
137 let insert_vals: Vec<(&'static str, Value)> = if T::primary_key_auto_increment() {
138 values.into_iter().filter(|(col, _)| *col != T::primary_key_name()).collect()
139 } else {
140 values
141 };
142
143 let cols: Vec<&str> = insert_vals.iter().map(|(c, _)| *c).collect();
144 let params: Vec<Value> = insert_vals.into_iter().map(|(_, v)| v).collect();
145 let ph = placeholders(backend, cols.len());
146 let col_list = cols.join(", ");
147
148 let insert_sql = format!(
149 "INSERT INTO {} ({}) VALUES ({})",
150 T::table_name(), col_list, ph
151 );
152
153 let new_id = pool.insert_returning_id(&insert_sql, ¶ms, T::primary_key_name()).await?;
154 let select_sql = format!(
155 "SELECT * FROM {} WHERE {} = {}",
156 T::table_name(), T::primary_key_name(), placeholder(backend, 1),
157 );
158 let rows = pool.query_rows(&select_sql, &[Value::Int(new_id)]).await?;
159 rows.into_iter().next()
160 .ok_or_else(|| DbError::new("inserted row not found"))
161 .and_then(|r| T::from_row(&r))
162}
163
164#[doc(hidden)]
165pub async fn update_entity<T: Model>(pool: &DbPool, entity: &T) -> Result<T, DbError> {
166 let backend = pool.backend();
167 let values = entity.to_values();
168 let set_pairs: Vec<(&str, Value)> = values
169 .into_iter()
170 .filter(|(col, _)| *col != T::primary_key_name())
171 .collect();
172
173 let set_clause: String = set_pairs
174 .iter()
175 .enumerate()
176 .map(|(i, (col, _))| format!("{} = {}", col, placeholder(backend, i + 1)))
177 .collect::<Vec<_>>()
178 .join(", ");
179
180 let pk_placeholder = placeholder(backend, set_pairs.len() + 1);
181 let sql = format!(
182 "UPDATE {} SET {} WHERE {} = {}",
183 T::table_name(), set_clause, T::primary_key_name(), pk_placeholder,
184 );
185
186 let mut params: Vec<Value> = set_pairs.into_iter().map(|(_, v)| v).collect();
187 params.push(entity.primary_key_value());
188 pool.execute(&sql, ¶ms).await?;
189
190 let pk_id = match entity.primary_key_value() {
191 Value::Int(n) => n,
192 _ => return Err(DbError::new("primary key must be an integer for UPDATE")),
193 };
194 let select_sql = format!(
195 "SELECT * FROM {} WHERE {} = {}",
196 T::table_name(), T::primary_key_name(), placeholder(backend, 1),
197 );
198 let rows = pool.query_rows(&select_sql, &[Value::Int(pk_id)]).await?;
199 rows.into_iter().next()
200 .ok_or_else(|| DbError::new("updated row not found"))
201 .and_then(|r| T::from_row(&r))
202}
203
204#[doc(hidden)]
205pub fn extract_count(rows: Vec<super::ModelRow>) -> Result<i64, DbError> {
206 match rows.into_iter().next() {
207 Some(row) => {
208 if let Some((_, val)) = row.columns.first() {
209 match val.clone() {
210 Value::Int(n) => return Ok(n),
211 other => return Err(DbError::new(format!("unexpected count value: {:?}", other))),
212 }
213 }
214 Err(DbError::new("count query returned empty row"))
215 }
216 None => Ok(0),
217 }
218}