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![]).await?;
209
210 for model in models {
211 let map = model.to_map()?;
212 let columns: Vec<String> = map.keys().cloned().collect();
213 let values: Vec<String> = map.keys().map(|_| "?".to_string()).collect();
214
215 let sql = format!(
216 "INSERT INTO {} ({}) VALUES ({})",
217 Self::table_name(),
218 columns.join(", "),
219 values.join(", ")
220 );
221
222 let params: Vec<crate::compat::LibsqlValue> = map
223 .values()
224 .map(|v| Self::value_to_libsql_value(v))
225 .collect();
226
227 db.execute(&sql, params).await?;
228 let id = 1i64; let mut result = model.clone();
231 result.set_primary_key(id);
232 results.push(result);
233 }
234
235 db.execute("COMMIT", vec![]).await?;
236 Ok(results)
237 }
238
239 async fn find_by_id(id: i64, db: &Database) -> Result<Option<Self>> {
241 let sql = format!(
242 "SELECT * FROM {} WHERE {} = ?",
243 Self::table_name(),
244 Self::primary_key()
245 );
246
247 Self::log_debug(&format!("Finding record by ID: {}", mask_id(id)));
248 Self::log_debug(&format!("SQL: {sql}"));
249
250 let mut rows = db
251 .inner
252 .query(&sql, vec![crate::compat::integer_value(id)])
253 .await?;
254
255 if let Some(row) = rows.next().await? {
256 let map = Self::row_to_map(&row)?;
257 Self::log_debug(&format!("Found record with ID: {}", mask_id(id)));
258 Ok(Some(Self::from_map(map)?))
259 } else {
260 Self::log_debug(&format!("No record found with ID: {}", mask_id(id)));
261 Ok(None)
262 }
263 }
264
265 async fn find_one(filter: FilterOperator, db: &Database) -> Result<Option<Self>> {
267 let builder = QueryBuilder::new(Self::table_name())
268 .r#where(filter)
269 .limit(1);
270
271 let results = builder.execute::<Self>(db).await?;
272 Ok(results.into_iter().next())
273 }
274
275 async fn find_all(db: &Database) -> Result<Vec<Self>> {
277 let builder = QueryBuilder::new(Self::table_name());
278 builder.execute::<Self>(db).await
279 }
280
281 async fn find_where(filter: FilterOperator, db: &Database) -> Result<Vec<Self>> {
283 let builder = QueryBuilder::new(Self::table_name()).r#where(filter);
284 builder.execute::<Self>(db).await
285 }
286
287 async fn find_paginated(
289 pagination: &Pagination,
290 db: &Database,
291 ) -> Result<PaginatedResult<Self>> {
292 let builder = QueryBuilder::new(Self::table_name());
293 builder.execute_paginated::<Self>(db, pagination).await
294 }
295
296 async fn find_where_paginated(
298 filter: FilterOperator,
299 pagination: &Pagination,
300 db: &Database,
301 ) -> Result<PaginatedResult<Self>> {
302 let builder = QueryBuilder::new(Self::table_name()).r#where(filter);
303 builder.execute_paginated::<Self>(db, pagination).await
304 }
305
306 async fn search(
308 search_filter: &SearchFilter,
309 pagination: Option<&Pagination>,
310 db: &Database,
311 ) -> Result<PaginatedResult<Self>> {
312 let filter = search_filter.to_filter_operator();
313 let pagination = pagination.unwrap_or(&Pagination::default()).clone();
314
315 Self::find_where_paginated(filter, &pagination, db).await
316 }
317
318 async fn count(db: &Database) -> Result<u64> {
320 let sql = format!("SELECT COUNT(*) FROM {}", Self::table_name());
321 let mut rows = db.query(&sql, vec![]).await?;
322
323 if let Some(row) = rows.next().await? {
324 row.get_value(0)
325 .ok()
326 .and_then(|v| match v {
327 crate::compat::LibsqlValue::Integer(i) => Some(i as u64),
328 _ => None,
329 })
330 .ok_or_else(|| Error::Query("Failed to get count".to_string()))
331 } else {
332 Err(Error::Query("No count result".to_string()))
333 }
334 }
335
336 async fn count_where(filter: FilterOperator, db: &Database) -> Result<u64> {
338 let builder = QueryBuilder::new(Self::table_name()).r#where(filter);
339
340 let (sql, params) = builder.build_count()?;
341 let mut rows = db.query(&sql, params).await?;
342
343 if let Some(row) = rows.next().await? {
344 row.get_value(0)
345 .ok()
346 .and_then(|v| match v {
347 crate::compat::LibsqlValue::Integer(i) => Some(i as u64),
348 _ => None,
349 })
350 .ok_or_else(|| Error::Query("Failed to get count".to_string()))
351 } else {
352 Err(Error::Query("No count result".to_string()))
353 }
354 }
355
356 async fn update(&self, db: &Database) -> Result<Self> {
358 let id = self.get_primary_key().ok_or_else(|| {
359 Error::Validation("Cannot update record without primary key".to_string())
360 })?;
361
362 let map = self.to_map()?;
363 let set_clauses: Vec<String> = map
364 .keys()
365 .filter(|&k| k != Self::primary_key())
366 .map(|k| format!("{k} = ?"))
367 .collect();
368
369 let sql = format!(
370 "UPDATE {} SET {} WHERE {} = ?",
371 Self::table_name(),
372 set_clauses.join(", "),
373 Self::primary_key()
374 );
375
376 Self::log_info(&format!("Updating record with ID: {}", mask_id(id)));
377 Self::log_debug(&format!("SQL: {sql}"));
378
379 let mut params: Vec<crate::compat::LibsqlValue> = map
380 .iter()
381 .filter(|(k, _)| k != &Self::primary_key())
382 .map(|(_, v)| Self::value_to_libsql_value(v))
383 .collect();
384 params.push(crate::compat::integer_value(id));
385
386 db.execute(&sql, params).await?;
387 Self::log_info(&format!(
388 "Successfully updated record with ID: {}",
389 mask_id(id)
390 ));
391 Ok(self.clone())
392 }
393
394 async fn bulk_update(models: &[Self], db: &Database) -> Result<Vec<Self>> {
396 if models.is_empty() {
397 return Ok(Vec::new());
398 }
399
400 let mut results = Vec::new();
401 db.execute("BEGIN", vec![]).await?;
403
404 for model in models {
405 let result = model.update(db).await?;
406 results.push(result);
407 }
408
409 db.execute("COMMIT", vec![]).await?;
410 Ok(results)
411 }
412
413 async fn delete(&self, db: &Database) -> Result<bool> {
415 let id = self.get_primary_key().ok_or_else(|| {
416 Error::Validation("Cannot delete record without primary key".to_string())
417 })?;
418
419 let sql = format!(
420 "DELETE FROM {} WHERE {} = ?",
421 Self::table_name(),
422 Self::primary_key()
423 );
424
425 Self::log_info(&format!("Deleting record with ID: {}", mask_id(id)));
426 Self::log_debug(&format!("SQL: {sql}"));
427
428 db.execute(&sql, vec![crate::compat::integer_value(id)])
429 .await?;
430 Self::log_info(&format!(
431 "Successfully deleted record with ID: {}",
432 mask_id(id)
433 ));
434 Ok(true)
435 }
436
437 async fn bulk_delete(ids: &[i64], db: &Database) -> Result<u64> {
439 if ids.is_empty() {
440 return Ok(0);
441 }
442
443 let placeholders: Vec<String> = ids.iter().map(|_| "?".to_string()).collect();
444 let sql = format!(
445 "DELETE FROM {} WHERE {} IN ({})",
446 Self::table_name(),
447 Self::primary_key(),
448 placeholders.join(", ")
449 );
450
451 let params: Vec<crate::compat::LibsqlValue> = ids
452 .iter()
453 .map(|&id| crate::compat::integer_value(id))
454 .collect();
455 db.execute(&sql, params).await?;
456 Ok(ids.len() as u64)
457 }
458
459 async fn delete_where(filter: FilterOperator, db: &Database) -> Result<u64> {
461 let builder = QueryBuilder::new(Self::table_name()).r#where(filter);
462
463 let (sql, params) = builder.build()?;
464 let delete_sql = sql.replace("SELECT *", "DELETE");
465 db.execute(&delete_sql, params).await?;
466
467 Ok(1)
470 }
471
472 async fn list(
474 sort: Option<Vec<Sort>>,
475 pagination: Option<&Pagination>,
476 db: &Database,
477 ) -> Result<PaginatedResult<Self>> {
478 let mut builder = QueryBuilder::new(Self::table_name());
479
480 if let Some(sorts) = sort {
481 builder = builder.order_by_multiple(sorts);
482 }
483
484 let pagination = pagination.unwrap_or(&Pagination::default()).clone();
485 builder.execute_paginated::<Self>(db, &pagination).await
486 }
487
488 async fn list_where(
490 filter: FilterOperator,
491 sort: Option<Vec<Sort>>,
492 pagination: Option<&Pagination>,
493 db: &Database,
494 ) -> Result<PaginatedResult<Self>> {
495 let mut builder = QueryBuilder::new(Self::table_name()).r#where(filter);
496
497 if let Some(sorts) = sort {
498 builder = builder.order_by_multiple(sorts);
499 }
500
501 let pagination = pagination.unwrap_or(&Pagination::default()).clone();
502 builder.execute_paginated::<Self>(db, &pagination).await
503 }
504
505 async fn query(builder: QueryBuilder, db: &Database) -> Result<Vec<Self>> {
507 builder.execute::<Self>(db).await
508 }
509
510 async fn query_paginated(
512 builder: QueryBuilder,
513 pagination: &Pagination,
514 db: &Database,
515 ) -> Result<PaginatedResult<Self>> {
516 builder.execute_paginated::<Self>(db, pagination).await
517 }
518
519 async fn aggregate(
521 function: Aggregate,
522 column: &str,
523 filter: Option<FilterOperator>,
524 db: &Database,
525 ) -> Result<Option<f64>> {
526 let mut builder =
527 QueryBuilder::new(Self::table_name()).aggregate(function, column, None::<String>);
528
529 if let Some(filter) = filter {
530 builder = builder.r#where(filter);
531 }
532
533 let (sql, params) = builder.build()?;
534 let mut rows = db.query(&sql, params).await?;
535
536 if let Some(row) = rows.next().await? {
537 let value = row
538 .get_value(0)
539 .ok()
540 .and_then(|v| match v {
541 crate::compat::LibsqlValue::Integer(i) => Some(i as f64),
542 crate::compat::LibsqlValue::Real(f) => Some(f),
543 _ => None,
544 })
545 .ok_or_else(|| Error::Query("Failed to get aggregate value".to_string()))?;
546 Ok(Some(value))
547 } else {
548 Ok(None)
549 }
550 }
551
552 fn row_to_map(row: &crate::compat::LibsqlRow) -> Result<HashMap<String, crate::Value>> {
554 let mut map = HashMap::new();
555 for i in 0..row.column_count() {
556 if let Some(column_name) = row.column_name(i) {
557 let value = row.get_value(i).unwrap_or(crate::compat::null_value());
558 map.insert(column_name.to_string(), Self::libsql_value_to_value(&value));
559 }
560 }
561 Ok(map)
562 }
563
564 fn value_to_libsql_value(value: &crate::Value) -> crate::compat::LibsqlValue {
566 match value {
567 crate::Value::Null => crate::compat::null_value(),
568 crate::Value::Integer(i) => crate::compat::LibsqlValue::Integer(*i),
569 crate::Value::Real(f) => crate::compat::LibsqlValue::Real(*f),
570 crate::Value::Text(s) => crate::compat::LibsqlValue::Text(s.clone()),
571 crate::Value::Blob(b) => crate::compat::LibsqlValue::Blob(b.clone()),
572 crate::Value::Boolean(b) => crate::compat::LibsqlValue::Integer(if *b { 1 } else { 0 }),
573 }
574 }
575
576 fn libsql_value_to_value(value: &crate::compat::LibsqlValue) -> crate::Value {
578 match value {
579 crate::compat::LibsqlValue::Null => crate::Value::Null,
580 crate::compat::LibsqlValue::Integer(i) => crate::Value::Integer(*i),
581 crate::compat::LibsqlValue::Real(f) => crate::Value::Real(*f),
582 crate::compat::LibsqlValue::Text(s) => crate::Value::Text(s.clone()),
583 crate::compat::LibsqlValue::Blob(b) => crate::Value::Blob(b.clone()),
584 }
585 }
586
587 fn log_info(message: &str) {
589 #[cfg(target_arch = "wasm32")]
590 {
591 #[cfg(feature = "web-sys")]
592 web_sys::console::log_1(&format!("[INFO] {}: {}", Self::table_name(), message).into());
593 }
594 #[cfg(not(target_arch = "wasm32"))]
595 {
596 log::info!("[{}] {}", Self::table_name(), message);
597 }
598 }
599
600 fn log_debug(message: &str) {
602 #[cfg(target_arch = "wasm32")]
603 {
604 #[cfg(feature = "web-sys")]
605 web_sys::console::log_1(&format!("[DEBUG] {}: {}", Self::table_name(), message).into());
606 }
607 #[cfg(not(target_arch = "wasm32"))]
608 {
609 log::debug!("[{}] {}", Self::table_name(), message);
610 }
611 }
612
613 fn log_warn(message: &str) {
615 #[cfg(target_arch = "wasm32")]
616 {
617 #[cfg(feature = "web-sys")]
618 web_sys::console::warn_1(&format!("[WARN] {}: {}", Self::table_name(), message).into());
619 }
620 #[cfg(not(target_arch = "wasm32"))]
621 {
622 log::warn!("[{}] {}", Self::table_name(), message);
623 }
624 }
625
626 fn log_error(message: &str) {
628 #[cfg(target_arch = "wasm32")]
629 {
630 #[cfg(feature = "web-sys")]
631 web_sys::console::error_1(
632 &format!("[ERROR] {}: {}", Self::table_name(), message).into(),
633 );
634 }
635 #[cfg(not(target_arch = "wasm32"))]
636 {
637 log::error!("[{}] {}", Self::table_name(), message);
638 }
639 }
640}