1use crate::{
28 Aggregate, Database, Error, FilterOperator, PaginatedResult, Pagination, QueryBuilder, Result,
29 SearchFilter, Sort,
30};
31use std::collections::HashMap;
32
33use serde::{de::DeserializeOwned, Serialize};
34
35fn mask_id(id: i64) -> String {
37 if id < 100 {
38 return "*".repeat(id.to_string().len());
39 }
40 let id_str = id.to_string();
41 let visible_digits = 2;
42 let masked_digits = id_str.len() - visible_digits;
43 format!("{}{}", &id_str[..visible_digits], "*".repeat(masked_digits))
44}
45
46#[allow(async_fn_in_trait)]
48pub trait Model: Serialize + DeserializeOwned + Send + Sync + Clone {
49 fn table_name() -> &'static str;
51
52 fn primary_key() -> &'static str {
54 "id"
55 }
56
57 fn get_primary_key(&self) -> Option<i64>;
59
60 fn set_primary_key(&mut self, id: i64);
62
63 fn columns() -> Vec<&'static str>;
65
66 fn migration_sql() -> String;
68
69 fn to_map(&self) -> Result<HashMap<String, crate::Value>>;
71
72 fn from_map(map: HashMap<String, crate::Value>) -> Result<Self>;
74
75 async fn create(&self, db: &Database) -> Result<Self> {
77 let map = self.to_map()?;
78 let columns: Vec<String> = map.keys().cloned().collect();
79 let values: Vec<String> = map.keys().map(|_| "?".to_string()).collect();
80
81 let sql = format!(
82 "INSERT INTO {} ({}) VALUES ({})",
83 Self::table_name(),
84 columns.join(", "),
85 values.join(", ")
86 );
87
88 Self::log_info(&format!("Creating record in table: {}", Self::table_name()));
89 Self::log_debug(&format!("SQL: {sql}"));
90
91 let params: Vec<crate::compat::LibsqlValue> = map
92 .values()
93 .map(|v| Self::value_to_libsql_value(v))
94 .collect();
95
96 db.execute(&sql, params).await?;
97 let id = 1i64; let mut result = self.clone();
100 result.set_primary_key(id);
101
102 Self::log_info(&format!(
103 "Successfully created record with ID: {}",
104 mask_id(id)
105 ));
106 Ok(result)
107 }
108
109 async fn create_or_update(&self, db: &Database) -> Result<Self> {
111 if let Some(id) = self.get_primary_key() {
112 Self::log_info(&format!(
113 "Updating existing record with ID: {}",
114 mask_id(id)
115 ));
116 match Self::find_by_id(id, db).await? {
118 Some(_) => {
119 self.update(db).await
121 }
122 None => {
123 Self::log_warn(&format!(
125 "Record with ID {} not found, creating new record",
126 mask_id(id)
127 ));
128 self.create(db).await
129 }
130 }
131 } else {
132 Self::log_info("Creating new record (no primary key provided)");
134 self.create(db).await
135 }
136 }
137
138 async fn upsert(&self, unique_columns: &[&str], db: &Database) -> Result<Self> {
140 let map = self.to_map()?;
141
142 let mut where_conditions = Vec::new();
144 let mut where_params = Vec::new();
145
146 for &column in unique_columns {
147 if let Some(value) = map.get(column) {
148 where_conditions.push(format!("{column} = ?"));
149 where_params.push(Self::value_to_libsql_value(value));
150 }
151 }
152
153 if where_conditions.is_empty() {
154 return Err(Error::Validation(
155 "No unique columns provided for upsert".to_string(),
156 ));
157 }
158
159 let where_clause = where_conditions.join(" AND ");
160 let sql = format!(
161 "SELECT {} FROM {} WHERE {}",
162 Self::primary_key(),
163 Self::table_name(),
164 where_clause
165 );
166
167 Self::log_info(&format!(
168 "Checking for existing record in table: {}",
169 Self::table_name()
170 ));
171 Self::log_debug(&format!("SQL: {sql}"));
172
173 let mut rows = db.query(&sql, where_params).await?;
174
175 if let Some(row) = rows.next().await? {
176 if let Some(existing_id) = row.get_value(0).ok().and_then(|v| match v {
178 crate::compat::LibsqlValue::Integer(i) => Some(i),
179 _ => None,
180 }) {
181 Self::log_info(&format!(
182 "Found existing record with ID: {}, updating",
183 mask_id(existing_id)
184 ));
185 let mut updated_self = self.clone();
186 updated_self.set_primary_key(existing_id);
187 updated_self.update(db).await
188 } else {
189 Err(Error::Query(
190 "Failed to get primary key from existing record".to_string(),
191 ))
192 }
193 } else {
194 Self::log_info("No existing record found, creating new one");
196 self.create(db).await
197 }
198 }
199
200 async fn bulk_create(models: &[Self], db: &Database) -> Result<Vec<Self>> {
202 if models.is_empty() {
203 return Ok(Vec::new());
204 }
205
206 let mut results = Vec::new();
207 db.execute("BEGIN", vec![crate::compat::null_value(); 0])
209 .await?;
210
211 for model in models {
212 let map = model.to_map()?;
213 let columns: Vec<String> = map.keys().cloned().collect();
214 let values: Vec<String> = map.keys().map(|_| "?".to_string()).collect();
215
216 let sql = format!(
217 "INSERT INTO {} ({}) VALUES ({})",
218 Self::table_name(),
219 columns.join(", "),
220 values.join(", ")
221 );
222
223 let params: Vec<crate::compat::LibsqlValue> = map
224 .values()
225 .map(|v| Self::value_to_libsql_value(v))
226 .collect();
227
228 db.execute(&sql, params).await?;
229 let id = 1i64; let mut result = model.clone();
232 result.set_primary_key(id);
233 results.push(result);
234 }
235
236 db.execute("COMMIT", vec![crate::compat::null_value(); 0])
237 .await?;
238 Ok(results)
239 }
240
241 async fn find_by_id(id: i64, db: &Database) -> Result<Option<Self>> {
243 let sql = format!(
244 "SELECT * FROM {} WHERE {} = ?",
245 Self::table_name(),
246 Self::primary_key()
247 );
248
249 Self::log_debug(&format!("Finding record by ID: {}", mask_id(id)));
250 Self::log_debug(&format!("SQL: {sql}"));
251
252 let mut rows = db
253 .inner
254 .query(&sql, vec![crate::compat::integer_value(id)])
255 .await?;
256
257 if let Some(row) = rows.next().await? {
258 let map = Self::row_to_map(&row)?;
259 Self::log_debug(&format!("Found record with ID: {}", mask_id(id)));
260 Ok(Some(Self::from_map(map)?))
261 } else {
262 Self::log_debug(&format!("No record found with ID: {}", mask_id(id)));
263 Ok(None)
264 }
265 }
266
267 async fn find_one(filter: FilterOperator, db: &Database) -> Result<Option<Self>> {
269 let builder = QueryBuilder::new(Self::table_name())
270 .r#where(filter)
271 .limit(1);
272
273 let results = builder.execute::<Self>(db).await?;
274 Ok(results.into_iter().next())
275 }
276
277 async fn find_all(db: &Database) -> Result<Vec<Self>> {
279 let builder = QueryBuilder::new(Self::table_name());
280 builder.execute::<Self>(db).await
281 }
282
283 async fn find_where(filter: FilterOperator, db: &Database) -> Result<Vec<Self>> {
285 let builder = QueryBuilder::new(Self::table_name()).r#where(filter);
286 builder.execute::<Self>(db).await
287 }
288
289 async fn find_paginated(
291 pagination: &Pagination,
292 db: &Database,
293 ) -> Result<PaginatedResult<Self>> {
294 let builder = QueryBuilder::new(Self::table_name());
295 builder.execute_paginated::<Self>(db, pagination).await
296 }
297
298 async fn find_where_paginated(
300 filter: FilterOperator,
301 pagination: &Pagination,
302 db: &Database,
303 ) -> Result<PaginatedResult<Self>> {
304 let builder = QueryBuilder::new(Self::table_name()).r#where(filter);
305 builder.execute_paginated::<Self>(db, pagination).await
306 }
307
308 async fn search(
310 search_filter: &SearchFilter,
311 pagination: Option<&Pagination>,
312 db: &Database,
313 ) -> Result<PaginatedResult<Self>> {
314 let filter = search_filter.to_filter_operator();
315 let pagination = pagination.unwrap_or(&Pagination::default()).clone();
316
317 Self::find_where_paginated(filter, &pagination, db).await
318 }
319
320 async fn count(db: &Database) -> Result<u64> {
322 let sql = format!("SELECT COUNT(*) FROM {}", Self::table_name());
323 let mut rows = db.query(&sql, vec![crate::compat::null_value(); 0]).await?;
324
325 if let Some(row) = rows.next().await? {
326 row.get_value(0)
327 .ok()
328 .and_then(|v| match v {
329 crate::compat::LibsqlValue::Integer(i) => Some(i as u64),
330 _ => None,
331 })
332 .ok_or_else(|| Error::Query("Failed to get count".to_string()))
333 } else {
334 Err(Error::Query("No count result".to_string()))
335 }
336 }
337
338 async fn count_where(filter: FilterOperator, db: &Database) -> Result<u64> {
340 let builder = QueryBuilder::new(Self::table_name()).r#where(filter);
341
342 let (sql, params) = builder.build_count()?;
343 let mut rows = db.query(&sql, params).await?;
344
345 if let Some(row) = rows.next().await? {
346 row.get_value(0)
347 .ok()
348 .and_then(|v| match v {
349 crate::compat::LibsqlValue::Integer(i) => Some(i as u64),
350 _ => None,
351 })
352 .ok_or_else(|| Error::Query("Failed to get count".to_string()))
353 } else {
354 Err(Error::Query("No count result".to_string()))
355 }
356 }
357
358 async fn update(&self, db: &Database) -> Result<Self> {
360 let id = self.get_primary_key().ok_or_else(|| {
361 Error::Validation("Cannot update record without primary key".to_string())
362 })?;
363
364 let map = self.to_map()?;
365 let set_clauses: Vec<String> = map
366 .keys()
367 .filter(|&k| k != Self::primary_key())
368 .map(|k| format!("{k} = ?"))
369 .collect();
370
371 let sql = format!(
372 "UPDATE {} SET {} WHERE {} = ?",
373 Self::table_name(),
374 set_clauses.join(", "),
375 Self::primary_key()
376 );
377
378 Self::log_info(&format!("Updating record with ID: {}", mask_id(id)));
379 Self::log_debug(&format!("SQL: {sql}"));
380
381 let mut params: Vec<crate::compat::LibsqlValue> = map
382 .iter()
383 .filter(|(k, _)| k != &Self::primary_key())
384 .map(|(_, v)| Self::value_to_libsql_value(v))
385 .collect();
386 params.push(crate::compat::integer_value(id));
387
388 db.execute(&sql, params).await?;
389 Self::log_info(&format!(
390 "Successfully updated record with ID: {}",
391 mask_id(id)
392 ));
393 Ok(self.clone())
394 }
395
396 async fn bulk_update(models: &[Self], db: &Database) -> Result<Vec<Self>> {
398 if models.is_empty() {
399 return Ok(Vec::new());
400 }
401
402 let mut results = Vec::new();
403 db.execute("BEGIN", vec![crate::compat::null_value(); 0])
405 .await?;
406
407 for model in models {
408 let result = model.update(db).await?;
409 results.push(result);
410 }
411
412 db.execute("COMMIT", vec![crate::compat::null_value(); 0])
413 .await?;
414 Ok(results)
415 }
416
417 async fn delete(&self, db: &Database) -> Result<bool> {
419 let id = self.get_primary_key().ok_or_else(|| {
420 Error::Validation("Cannot delete record without primary key".to_string())
421 })?;
422
423 let sql = format!(
424 "DELETE FROM {} WHERE {} = ?",
425 Self::table_name(),
426 Self::primary_key()
427 );
428
429 Self::log_info(&format!("Deleting record with ID: {}", mask_id(id)));
430 Self::log_debug(&format!("SQL: {sql}"));
431
432 db.execute(&sql, vec![crate::compat::integer_value(id)])
433 .await?;
434 Self::log_info(&format!(
435 "Successfully deleted record with ID: {}",
436 mask_id(id)
437 ));
438 Ok(true)
439 }
440
441 async fn bulk_delete(ids: &[i64], db: &Database) -> Result<u64> {
443 if ids.is_empty() {
444 return Ok(0);
445 }
446
447 let placeholders: Vec<String> = ids.iter().map(|_| "?".to_string()).collect();
448 let sql = format!(
449 "DELETE FROM {} WHERE {} IN ({})",
450 Self::table_name(),
451 Self::primary_key(),
452 placeholders.join(", ")
453 );
454
455 let params: Vec<crate::compat::LibsqlValue> = ids.iter().map(|&id| crate::compat::integer_value(id)).collect();
456 db.execute(&sql, params).await?;
457 Ok(ids.len() as u64)
458 }
459
460 async fn delete_where(filter: FilterOperator, db: &Database) -> Result<u64> {
462 let builder = QueryBuilder::new(Self::table_name()).r#where(filter);
463
464 let (sql, params) = builder.build()?;
465 let delete_sql = sql.replace("SELECT *", "DELETE");
466 db.execute(&delete_sql, params).await?;
467
468 Ok(1)
471 }
472
473 async fn list(
475 sort: Option<Vec<Sort>>,
476 pagination: Option<&Pagination>,
477 db: &Database,
478 ) -> Result<PaginatedResult<Self>> {
479 let mut builder = QueryBuilder::new(Self::table_name());
480
481 if let Some(sorts) = sort {
482 builder = builder.order_by_multiple(sorts);
483 }
484
485 let pagination = pagination.unwrap_or(&Pagination::default()).clone();
486 builder.execute_paginated::<Self>(db, &pagination).await
487 }
488
489 async fn list_where(
491 filter: FilterOperator,
492 sort: Option<Vec<Sort>>,
493 pagination: Option<&Pagination>,
494 db: &Database,
495 ) -> Result<PaginatedResult<Self>> {
496 let mut builder = QueryBuilder::new(Self::table_name()).r#where(filter);
497
498 if let Some(sorts) = sort {
499 builder = builder.order_by_multiple(sorts);
500 }
501
502 let pagination = pagination.unwrap_or(&Pagination::default()).clone();
503 builder.execute_paginated::<Self>(db, &pagination).await
504 }
505
506 async fn query(builder: QueryBuilder, db: &Database) -> Result<Vec<Self>> {
508 builder.execute::<Self>(db).await
509 }
510
511 async fn query_paginated(
513 builder: QueryBuilder,
514 pagination: &Pagination,
515 db: &Database,
516 ) -> Result<PaginatedResult<Self>> {
517 builder.execute_paginated::<Self>(db, pagination).await
518 }
519
520 async fn aggregate(
522 function: Aggregate,
523 column: &str,
524 filter: Option<FilterOperator>,
525 db: &Database,
526 ) -> Result<Option<f64>> {
527 let mut builder =
528 QueryBuilder::new(Self::table_name()).aggregate(function, column, None::<String>);
529
530 if let Some(filter) = filter {
531 builder = builder.r#where(filter);
532 }
533
534 let (sql, params) = builder.build()?;
535 let mut rows = db.query(&sql, params).await?;
536
537 if let Some(row) = rows.next().await? {
538 let value = row
539 .get_value(0)
540 .ok()
541 .and_then(|v| match v {
542 crate::compat::LibsqlValue::Integer(i) => Some(i as f64),
543 crate::compat::LibsqlValue::Real(f) => Some(f),
544 _ => None,
545 })
546 .ok_or_else(|| Error::Query("Failed to get aggregate value".to_string()))?;
547 Ok(Some(value))
548 } else {
549 Ok(None)
550 }
551 }
552
553 fn row_to_map(row: &crate::compat::LibsqlRow) -> Result<HashMap<String, crate::Value>> {
555 let mut map = HashMap::new();
556 for i in 0..row.column_count() {
557 if let Some(column_name) = row.column_name(i) {
558 let value = row.get_value(i).unwrap_or(crate::compat::null_value());
559 map.insert(column_name.to_string(), Self::libsql_value_to_value(&value));
560 }
561 }
562 Ok(map)
563 }
564
565 fn value_to_libsql_value(value: &crate::Value) -> crate::compat::LibsqlValue {
567 match value {
568 crate::Value::Null => crate::compat::null_value(),
569 crate::Value::Integer(i) => crate::compat::LibsqlValue::Integer(*i),
570 crate::Value::Real(f) => crate::compat::LibsqlValue::Real(*f),
571 crate::Value::Text(s) => crate::compat::LibsqlValue::Text(s.clone()),
572 crate::Value::Blob(b) => crate::compat::LibsqlValue::Blob(b.clone()),
573 crate::Value::Boolean(b) => crate::compat::LibsqlValue::Integer(if *b { 1 } else { 0 }),
574 }
575 }
576
577 fn libsql_value_to_value(value: &crate::compat::LibsqlValue) -> crate::Value {
579 match value {
580 crate::compat::LibsqlValue::Null => crate::Value::Null,
581 crate::compat::LibsqlValue::Integer(i) => crate::Value::Integer(*i),
582 crate::compat::LibsqlValue::Real(f) => crate::Value::Real(*f),
583 crate::compat::LibsqlValue::Text(s) => crate::Value::Text(s.clone()),
584 crate::compat::LibsqlValue::Blob(b) => crate::Value::Blob(b.clone()),
585 }
586 }
587
588 fn log_info(message: &str) {
590 #[cfg(target_arch = "wasm32")]
591 {
592 #[cfg(feature = "web-sys")]
593 web_sys::console::log_1(&format!("[INFO] {}: {}", Self::table_name(), message).into());
594 }
595 #[cfg(not(target_arch = "wasm32"))]
596 {
597 log::info!("[{}] {}", Self::table_name(), message);
598 }
599 }
600
601 fn log_debug(message: &str) {
603 #[cfg(target_arch = "wasm32")]
604 {
605 #[cfg(feature = "web-sys")]
606 web_sys::console::log_1(&format!("[DEBUG] {}: {}", Self::table_name(), message).into());
607 }
608 #[cfg(not(target_arch = "wasm32"))]
609 {
610 log::debug!("[{}] {}", Self::table_name(), message);
611 }
612 }
613
614 fn log_warn(message: &str) {
616 #[cfg(target_arch = "wasm32")]
617 {
618 #[cfg(feature = "web-sys")]
619 web_sys::console::warn_1(&format!("[WARN] {}: {}", Self::table_name(), message).into());
620 }
621 #[cfg(not(target_arch = "wasm32"))]
622 {
623 log::warn!("[{}] {}", Self::table_name(), message);
624 }
625 }
626
627 fn log_error(message: &str) {
629 #[cfg(target_arch = "wasm32")]
630 {
631 #[cfg(feature = "web-sys")]
632 web_sys::console::error_1(
633 &format!("[ERROR] {}: {}", Self::table_name(), message).into(),
634 );
635 }
636 #[cfg(not(target_arch = "wasm32"))]
637 {
638 log::error!("[{}] {}", Self::table_name(), message);
639 }
640 }
641}