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