elif_orm/model/
abstraction.rs

1//! Database Abstraction - Database-agnostic model operations
2//!
3//! Provides database-abstracted versions of model operations that work
4//! with the DatabasePool trait instead of concrete database types.
5
6use std::sync::Arc;
7
8use crate::backends::{DatabasePool, DatabaseValue};
9use crate::error::{ModelError, ModelResult};
10use crate::model::core_trait::Model;
11
12/// Trait for database-abstracted model operations
13/// This trait provides methods that use the database abstraction layer
14/// instead of hardcoded PostgreSQL types
15#[allow(async_fn_in_trait)]
16pub trait ModelAbstracted: Model {
17    /// Find a model by its primary key using database abstraction
18    async fn find_abstracted(
19        pool: &Arc<dyn DatabasePool>,
20        id: Self::PrimaryKey,
21    ) -> ModelResult<Option<Self>>
22    where
23        Self: Sized,
24    {
25        let sql = format!(
26            "SELECT * FROM {} WHERE {} = $1",
27            Self::table_name(),
28            Self::primary_key_name()
29        );
30        let params = vec![DatabaseValue::String(id.to_string())];
31
32        let row = pool.fetch_optional(&sql, &params).await.map_err(|e| {
33            ModelError::Database(format!("Failed to find {}: {}", Self::table_name(), e))
34        })?;
35
36        match row {
37            Some(row) => {
38                let model = Self::from_database_row(row.as_ref())?;
39                Ok(Some(model))
40            }
41            None => Ok(None),
42        }
43    }
44
45    /// Find all models using database abstraction
46    async fn all_abstracted(pool: &Arc<dyn DatabasePool>) -> ModelResult<Vec<Self>>
47    where
48        Self: Sized,
49    {
50        let sql = if Self::uses_soft_deletes() {
51            format!(
52                "SELECT * FROM {} WHERE deleted_at IS NULL",
53                Self::table_name()
54            )
55        } else {
56            format!("SELECT * FROM {}", Self::table_name())
57        };
58        let params = vec![];
59
60        let rows = pool.fetch_all(&sql, &params).await.map_err(|e| {
61            ModelError::Database(format!("Failed to fetch {}: {}", Self::table_name(), e))
62        })?;
63
64        let mut models = Vec::new();
65        for row in rows {
66            let model = Self::from_database_row(row.as_ref())?;
67            models.push(model);
68        }
69
70        Ok(models)
71    }
72
73    /// Count models using database abstraction
74    async fn count_abstracted(pool: &Arc<dyn DatabasePool>) -> ModelResult<i64>
75    where
76        Self: Sized,
77    {
78        let sql = if Self::uses_soft_deletes() {
79            format!(
80                "SELECT COUNT(*) FROM {} WHERE deleted_at IS NULL",
81                Self::table_name()
82            )
83        } else {
84            format!("SELECT COUNT(*) FROM {}", Self::table_name())
85        };
86        let params = vec![];
87
88        let row = pool.fetch_optional(&sql, &params).await.map_err(|e| {
89            ModelError::Database(format!("Failed to count {}: {}", Self::table_name(), e))
90        })?;
91
92        match row {
93            Some(row) => {
94                let count_value = row.get_by_index(0).map_err(|e| {
95                    ModelError::Database(format!("Failed to get count value: {}", e))
96                })?;
97
98                match count_value {
99                    DatabaseValue::Int64(count) => Ok(count),
100                    DatabaseValue::Int32(count) => Ok(count as i64),
101                    _ => Err(ModelError::Database("Invalid count value type".to_string())),
102                }
103            }
104            None => Ok(0),
105        }
106    }
107
108    /// Create a new model using database abstraction
109    async fn create_abstracted(pool: &Arc<dyn DatabasePool>, model: Self) -> ModelResult<Self>
110    where
111        Self: Sized,
112    {
113        let fields = model.to_fields();
114
115        if fields.is_empty() {
116            let insert_sql = format!(
117                "INSERT INTO {} DEFAULT VALUES RETURNING *",
118                Self::table_name()
119            );
120            let params = vec![];
121
122            let row = pool
123                .fetch_optional(&insert_sql, &params)
124                .await
125                .map_err(|e| {
126                    ModelError::Database(format!("Failed to create {}: {}", Self::table_name(), e))
127                })?;
128
129            match row {
130                Some(row) => Self::from_database_row(row.as_ref()),
131                None => Err(ModelError::Database(
132                    "Failed to get inserted row".to_string(),
133                )),
134            }
135        } else {
136            let field_names: Vec<String> = fields.keys().cloned().collect();
137            let field_placeholders: Vec<String> =
138                (1..=field_names.len()).map(|i| format!("${}", i)).collect();
139
140            let insert_sql = format!(
141                "INSERT INTO {} ({}) VALUES ({}) RETURNING *",
142                Self::table_name(),
143                field_names.join(", "),
144                field_placeholders.join(", ")
145            );
146
147            let params: Vec<DatabaseValue> = field_names
148                .iter()
149                .filter_map(|name| fields.get(name))
150                .map(DatabaseValue::from_json_value)
151                .collect();
152
153            let row = pool
154                .fetch_optional(&insert_sql, &params)
155                .await
156                .map_err(|e| {
157                    ModelError::Database(format!("Failed to create {}: {}", Self::table_name(), e))
158                })?;
159
160            match row {
161                Some(row) => Self::from_database_row(row.as_ref()),
162                None => Err(ModelError::Database(
163                    "Failed to get inserted row".to_string(),
164                )),
165            }
166        }
167    }
168
169    /// Update a model using database abstraction
170    async fn update_abstracted(&self, pool: &Arc<dyn DatabasePool>) -> ModelResult<()>
171    where
172        Self: Sized,
173    {
174        if let Some(pk) = self.primary_key() {
175            let fields = self.to_fields();
176            let pk_name = Self::primary_key_name();
177
178            let update_fields: Vec<String> = fields
179                .keys()
180                .filter(|&field| field != pk_name)
181                .enumerate()
182                .map(|(i, field)| format!("{} = ${}", field, i + 1))
183                .collect();
184
185            if update_fields.is_empty() {
186                return Ok(());
187            }
188
189            let update_sql = format!(
190                "UPDATE {} SET {} WHERE {} = ${}",
191                Self::table_name(),
192                update_fields.join(", "),
193                pk_name,
194                update_fields.len() + 1
195            );
196
197            let mut params: Vec<DatabaseValue> = fields
198                .iter()
199                .filter(|(field, _)| *field != pk_name)
200                .map(|(_, value)| DatabaseValue::from_json_value(value))
201                .collect();
202
203            params.push(DatabaseValue::String(pk.to_string()));
204
205            pool.execute(&update_sql, &params).await.map_err(|e| {
206                ModelError::Database(format!("Failed to update {}: {}", Self::table_name(), e))
207            })?;
208
209            Ok(())
210        } else {
211            Err(ModelError::MissingPrimaryKey)
212        }
213    }
214
215    /// Delete a model using database abstraction  
216    async fn delete_abstracted(self, pool: &Arc<dyn DatabasePool>) -> ModelResult<()>
217    where
218        Self: Sized,
219    {
220        if let Some(pk) = self.primary_key() {
221            let sql = if Self::uses_soft_deletes() {
222                format!(
223                    "UPDATE {} SET deleted_at = NOW() WHERE {} = $1",
224                    Self::table_name(),
225                    Self::primary_key_name()
226                )
227            } else {
228                format!(
229                    "DELETE FROM {} WHERE {} = $1",
230                    Self::table_name(),
231                    Self::primary_key_name()
232                )
233            };
234
235            let params = vec![DatabaseValue::String(pk.to_string())];
236
237            pool.execute(&sql, &params).await.map_err(|e| {
238                ModelError::Database(format!("Failed to delete {}: {}", Self::table_name(), e))
239            })?;
240
241            Ok(())
242        } else {
243            Err(ModelError::MissingPrimaryKey)
244        }
245    }
246}
247
248// Implement ModelAbstracted for all types that implement Model
249impl<T: Model> ModelAbstracted for T {}
250
251impl DatabaseValue {
252    /// Convert a JSON value to a DatabaseValue
253    pub fn from_json_value(value: &serde_json::Value) -> DatabaseValue {
254        match value {
255            serde_json::Value::Null => DatabaseValue::Null,
256            serde_json::Value::Bool(b) => DatabaseValue::Bool(*b),
257            serde_json::Value::Number(n) => {
258                if let Some(i) = n.as_i64() {
259                    // Check if it fits in i32 range
260                    if i >= i32::MIN as i64 && i <= i32::MAX as i64 {
261                        DatabaseValue::Int32(i as i32)
262                    } else {
263                        DatabaseValue::Int64(i)
264                    }
265                } else if let Some(f) = n.as_f64() {
266                    DatabaseValue::Float64(f)
267                } else {
268                    DatabaseValue::String(n.to_string())
269                }
270            }
271            serde_json::Value::String(s) => DatabaseValue::String(s.clone()),
272            serde_json::Value::Array(_) | serde_json::Value::Object(_) => {
273                DatabaseValue::Json(value.clone())
274            }
275        }
276    }
277}