1use crate::value::Value;
44use std::fmt::Debug;
45use std::sync::RwLock;
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub enum WhereOp {
54 Eq,
56 Ne,
58 Gt,
60 Ge,
62 Lt,
64 Le,
66 Like,
68 In,
70 NotIn,
72 IsNull,
74 IsNotNull,
76 Between,
78}
79
80impl WhereOp {
81 pub fn name(&self) -> &'static str {
83 match self {
84 WhereOp::Eq => "eq",
85 WhereOp::Ne => "ne",
86 WhereOp::Gt => "gt",
87 WhereOp::Ge => "ge",
88 WhereOp::Lt => "lt",
89 WhereOp::Le => "le",
90 WhereOp::Like => "like",
91 WhereOp::In => "in",
92 WhereOp::NotIn => "not_in",
93 WhereOp::IsNull => "is_null",
94 WhereOp::IsNotNull => "is_not_null",
95 WhereOp::Between => "between",
96 }
97 }
98}
99
100#[derive(Debug, Clone)]
102pub struct WhereCondition {
103 pub field: String,
104 pub op: WhereOp,
105 pub value: Value,
106 pub extra_values: Vec<Value>,
108}
109
110impl WhereCondition {
111 pub fn new(field: impl Into<String>, op: WhereOp, value: Value) -> Self {
113 Self {
114 field: field.into(),
115 op,
116 value,
117 extra_values: Vec::new(),
118 }
119 }
120
121 pub fn null_check(field: impl Into<String>, op: WhereOp) -> Self {
123 Self {
124 field: field.into(),
125 op,
126 value: Value::Null,
127 extra_values: Vec::new(),
128 }
129 }
130
131 pub fn in_op(field: impl Into<String>, op: WhereOp, values: Vec<Value>) -> Self {
133 Self {
134 field: field.into(),
135 op,
136 value: Value::Null,
137 extra_values: values,
138 }
139 }
140
141 pub fn between(field: impl Into<String>, low: Value, high: Value) -> Self {
143 Self {
144 field: field.into(),
145 op: WhereOp::Between,
146 value: low,
147 extra_values: vec![high],
148 }
149 }
150}
151
152#[derive(Debug, Clone)]
158pub struct PageResult<T> {
159 pub items: Vec<T>,
160 pub total: u64,
161 pub page: u64,
162 pub page_size: u64,
163}
164
165impl<T> PageResult<T> {
166 pub fn new(items: Vec<T>, total: u64, page: u64, page_size: u64) -> Self {
168 Self {
169 items,
170 total,
171 page,
172 page_size,
173 }
174 }
175
176 pub fn total_pages(&self) -> u64 {
178 if self.page_size == 0 {
179 return 0;
180 }
181 self.total.div_ceil(self.page_size)
182 }
183
184 pub fn has_next(&self) -> bool {
186 self.page < self.total_pages()
187 }
188
189 pub fn has_prev(&self) -> bool {
191 self.page > 1
192 }
193
194 pub fn is_empty(&self) -> bool {
196 self.items.is_empty()
197 }
198
199 pub fn len(&self) -> usize {
201 self.items.len()
202 }
203
204 pub fn map<U, F: Fn(T) -> U>(self, f: F) -> PageResult<U> {
206 PageResult {
207 items: self.items.into_iter().map(f).collect(),
208 total: self.total,
209 page: self.page,
210 page_size: self.page_size,
211 }
212 }
213}
214
215#[derive(Debug, Clone, PartialEq)]
221pub enum RepositoryError {
222 NotFound,
224 DatabaseError(String),
226 InvalidEntity(String),
228 Other(String),
230}
231
232impl std::fmt::Display for RepositoryError {
233 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234 match self {
235 RepositoryError::NotFound => write!(f, "entity not found"),
236 RepositoryError::DatabaseError(msg) => write!(f, "database error: {}", msg),
237 RepositoryError::InvalidEntity(msg) => write!(f, "invalid entity: {}", msg),
238 RepositoryError::Other(msg) => write!(f, "repository error: {}", msg),
239 }
240 }
241}
242
243impl std::error::Error for RepositoryError {}
244
245pub type RepositoryResult<T> = Result<T, RepositoryError>;
247
248pub trait Repository<E>: Send + Sync {
257 type Key: Clone + Debug + PartialEq + Send + Sync;
259
260 fn key_of(&self, entity: &E) -> Self::Key;
262
263 fn find_by_id(&self, key: &Self::Key) -> RepositoryResult<Option<E>>;
265
266 fn find_all(&self) -> RepositoryResult<Vec<E>>;
268
269 fn find_by(&self, conditions: &[WhereCondition]) -> RepositoryResult<Vec<E>>;
271
272 fn find_one_by(&self, conditions: &[WhereCondition]) -> RepositoryResult<Option<E>> {
274 let mut items = self.find_by(conditions)?;
275 if items.is_empty() {
276 Ok(None)
277 } else {
278 Ok(Some(items.remove(0)))
279 }
280 }
281
282 fn save(&self, entity: E) -> RepositoryResult<E>;
286
287 fn save_many(&self, entities: Vec<E>) -> RepositoryResult<Vec<E>> {
289 let mut saved = Vec::with_capacity(entities.len());
290 for e in entities {
291 saved.push(self.save(e)?);
292 }
293 Ok(saved)
294 }
295
296 fn delete(&self, key: &Self::Key) -> RepositoryResult<usize>;
298
299 fn delete_by(&self, conditions: &[WhereCondition]) -> RepositoryResult<usize> {
301 let items = self.find_by(conditions)?;
302 let mut count = 0;
303 for item in items {
304 let key = self.key_of(&item);
305 count += self.delete(&key)?;
306 }
307 Ok(count)
308 }
309
310 fn count(&self) -> RepositoryResult<u64>;
312
313 fn count_by(&self, conditions: &[WhereCondition]) -> RepositoryResult<u64> {
315 let items = self.find_by(conditions)?;
316 Ok(items.len() as u64)
317 }
318
319 fn exists(&self, key: &Self::Key) -> RepositoryResult<bool> {
321 Ok(self.find_by_id(key)?.is_some())
322 }
323
324 fn paginate(&self, page: u64, page_size: u64) -> RepositoryResult<PageResult<E>>
326 where
327 E: Clone,
328 {
329 let all = self.find_all()?;
330 let total = all.len() as u64;
331 let start = ((page.saturating_sub(1)) * page_size) as usize;
332 let end = (start + page_size as usize).min(all.len());
333
334 let items = if start < all.len() {
335 all[start..end].to_vec()
336 } else {
337 Vec::new()
338 };
339
340 Ok(PageResult::new(items, total, page, page_size))
341 }
342
343 fn paginate_by(
345 &self,
346 conditions: &[WhereCondition],
347 page: u64,
348 page_size: u64,
349 ) -> RepositoryResult<PageResult<E>>
350 where
351 E: Clone,
352 {
353 let all = self.find_by(conditions)?;
354 let total = all.len() as u64;
355 let start = ((page.saturating_sub(1)) * page_size) as usize;
356 let end = (start + page_size as usize).min(all.len());
357
358 let items = if start < all.len() {
359 all[start..end].to_vec()
360 } else {
361 Vec::new()
362 };
363
364 Ok(PageResult::new(items, total, page, page_size))
365 }
366}
367
368pub struct InMemoryRepository<E: Clone + Send + Sync + 'static> {
377 storage: RwLock<Vec<E>>,
378}
379
380impl<E: Clone + Send + Sync + 'static> InMemoryRepository<E> {
381 pub fn new() -> Self {
383 Self {
384 storage: RwLock::new(Vec::new()),
385 }
386 }
387
388 pub fn from_vec(items: Vec<E>) -> Self {
390 Self {
391 storage: RwLock::new(items),
392 }
393 }
394
395 pub fn len(&self) -> usize {
397 let storage = self.storage.read().unwrap();
398 storage.len()
399 }
400
401 pub fn is_empty(&self) -> bool {
403 self.len() == 0
404 }
405
406 pub fn clear(&self) {
408 let mut storage = self.storage.write().unwrap();
409 storage.clear();
410 }
411}
412
413impl<E: Clone + Send + Sync + 'static> Default for InMemoryRepository<E> {
414 fn default() -> Self {
415 Self::new()
416 }
417}
418
419fn value_matches(value: &Value, op: WhereOp, target: &Value, extras: &[Value]) -> bool {
421 use Value::*;
422 match op {
423 WhereOp::Eq => value == target,
424 WhereOp::Ne => value != target,
425 WhereOp::Gt => match (value, target) {
426 (I64(a), I64(b)) => a > b,
427 (F64(a), F64(b)) => a > b,
428 (F64(a), I64(b)) => a > &(*b as f64),
429 (I64(a), F64(b)) => a > &(*b as i64),
430 (String(a), String(b)) => a > b,
431 _ => false,
432 },
433 WhereOp::Ge => match (value, target) {
434 (I64(a), I64(b)) => a >= b,
435 (F64(a), F64(b)) => a >= b,
436 (String(a), String(b)) => a >= b,
437 _ => false,
438 },
439 WhereOp::Lt => match (value, target) {
440 (I64(a), I64(b)) => a < b,
441 (F64(a), F64(b)) => a < b,
442 (String(a), String(b)) => a < b,
443 _ => false,
444 },
445 WhereOp::Le => match (value, target) {
446 (I64(a), I64(b)) => a <= b,
447 (F64(a), F64(b)) => a <= b,
448 (String(a), String(b)) => a <= b,
449 _ => false,
450 },
451 WhereOp::Like => match (value, target) {
452 (String(a), String(b)) => {
453 let pattern = b.replace('%', ".*").replace('_', ".");
455 let full_pattern = format!("^{}$", pattern);
456 if let Ok(re) = simple_regex::compile(&full_pattern) {
457 re.is_match(a)
458 } else {
459 false
460 }
461 }
462 _ => false,
463 },
464 WhereOp::In => extras.iter().any(|v| v == value),
465 WhereOp::NotIn => !extras.iter().any(|v| v == value),
466 WhereOp::IsNull => matches!(value, Null),
467 WhereOp::IsNotNull => !matches!(value, Null),
468 WhereOp::Between => {
469 if extras.is_empty() {
470 return false;
471 }
472 let low = target;
473 let high = &extras[0];
474 value_matches(value, WhereOp::Ge, low, &[])
476 && value_matches(value, WhereOp::Le, high, &[])
477 }
478 }
479}
480
481pub trait EntityAttributes: Send + Sync {
483 fn get_attribute(&self, field: &str) -> Option<Value>;
485}
486
487impl<E: Clone + Send + Sync + 'static + EntityAttributes> Repository<E> for InMemoryRepository<E> {
489 type Key = Value;
490
491 fn key_of(&self, entity: &E) -> Self::Key {
492 entity.get_attribute("id").unwrap_or(Value::Null)
493 }
494
495 fn find_by_id(&self, key: &Self::Key) -> RepositoryResult<Option<E>> {
496 let storage = self.storage.read().unwrap();
497 Ok(storage.iter().find(|e| self.key_of(e) == *key).cloned())
498 }
499
500 fn find_all(&self) -> RepositoryResult<Vec<E>> {
501 let storage = self.storage.read().unwrap();
502 Ok(storage.clone())
503 }
504
505 fn find_by(&self, conditions: &[WhereCondition]) -> RepositoryResult<Vec<E>> {
506 let storage = self.storage.read().unwrap();
507 let result: Vec<E> = storage
508 .iter()
509 .filter(|e| {
510 conditions.iter().all(|c| {
511 let attr = e.get_attribute(&c.field);
512 match (attr, c.op) {
513 (None, WhereOp::IsNull) => true,
514 (None, _) => false,
515 (Some(v), _) => value_matches(&v, c.op, &c.value, &c.extra_values),
516 }
517 })
518 })
519 .cloned()
520 .collect();
521 Ok(result)
522 }
523
524 fn save(&self, mut entity: E) -> RepositoryResult<E> {
525 let mut storage = self.storage.write().unwrap();
526 let key = self.key_of(&entity);
527
528 let existing_idx = storage.iter().position(|e| self.key_of(e) == key);
530
531 match existing_idx {
532 Some(idx) => {
533 storage[idx] = entity.clone();
534 }
535 None => {
536 storage.push(entity.clone());
537 }
538 }
539 let _ = &mut entity; Ok(entity)
542 }
543
544 fn delete(&self, key: &Self::Key) -> RepositoryResult<usize> {
545 let mut storage = self.storage.write().unwrap();
546 let before = storage.len();
547 storage.retain(|e| self.key_of(e) != *key);
548 Ok(before - storage.len())
549 }
550
551 fn count(&self) -> RepositoryResult<u64> {
552 Ok(self.len() as u64)
553 }
554}
555
556mod simple_regex {
561 pub struct Regex {
563 patterns: Vec<Pattern>,
564 }
565
566 enum Pattern {
567 AnyChars, Literal(char), Start, End, }
572
573 pub fn compile(pattern: &str) -> Result<Regex, String> {
574 let mut patterns = Vec::new();
575 let chars: Vec<char> = pattern.chars().collect();
576 let mut i = 0;
577 while i < chars.len() {
578 match chars[i] {
579 '^' => {
580 patterns.push(Pattern::Start);
581 i += 1;
582 }
583 '$' => {
584 patterns.push(Pattern::End);
585 i += 1;
586 }
587 '.' if i + 1 < chars.len() && chars[i + 1] == '*' => {
588 patterns.push(Pattern::AnyChars);
589 i += 2;
590 }
591 c => {
592 patterns.push(Pattern::Literal(c));
593 i += 1;
594 }
595 }
596 }
597 Ok(Regex { patterns })
598 }
599
600 impl Regex {
601 pub fn is_match(&self, text: &str) -> bool {
602 self.match_from(text, 0, 0)
603 }
604
605 fn match_from(&self, text: &str, text_idx: usize, pat_idx: usize) -> bool {
606 let chars: Vec<char> = text.chars().collect();
607 if pat_idx >= self.patterns.len() {
608 return text_idx == chars.len();
609 }
610 match &self.patterns[pat_idx] {
611 Pattern::Start => self.match_from(text, 0, pat_idx + 1),
612 Pattern::End => text_idx == chars.len(),
613 Pattern::Literal(c) => {
614 if text_idx < chars.len() && chars[text_idx] == *c {
615 self.match_from(text, text_idx + 1, pat_idx + 1)
616 } else {
617 false
618 }
619 }
620 Pattern::AnyChars => {
621 for skip in 0..=(chars.len() - text_idx) {
623 if self.match_from(text, text_idx + skip, pat_idx + 1) {
624 return true;
625 }
626 }
627 false
628 }
629 }
630 }
631 }
632}
633
634pub trait EntityKey<K>: Send + Sync {
640 fn key(&self) -> K;
642}
643
644pub struct GenericKeyRepository<E, K>
646where
647 E: Clone + Send + Sync + 'static,
648 K: Clone + Debug + PartialEq + Send + Sync + 'static,
649{
650 storage: RwLock<Vec<E>>,
651 _phantom: std::marker::PhantomData<K>,
652}
653
654impl<E, K> GenericKeyRepository<E, K>
655where
656 E: Clone + Send + Sync + 'static,
657 K: Clone + Debug + PartialEq + Send + Sync + 'static,
658{
659 pub fn new() -> Self {
660 Self {
661 storage: RwLock::new(Vec::new()),
662 _phantom: std::marker::PhantomData,
663 }
664 }
665
666 pub fn from_vec(items: Vec<E>) -> Self {
667 Self {
668 storage: RwLock::new(items),
669 _phantom: std::marker::PhantomData,
670 }
671 }
672
673 pub fn len(&self) -> usize {
674 self.storage.read().unwrap().len()
675 }
676
677 pub fn is_empty(&self) -> bool {
678 self.len() == 0
679 }
680
681 pub fn clear(&self) {
682 self.storage.write().unwrap().clear();
683 }
684}
685
686impl<E, K> Default for GenericKeyRepository<E, K>
687where
688 E: Clone + Send + Sync + 'static,
689 K: Clone + Debug + PartialEq + Send + Sync + 'static,
690{
691 fn default() -> Self {
692 Self::new()
693 }
694}
695
696impl<E, K> Repository<E> for GenericKeyRepository<E, K>
697where
698 E: Clone + Send + Sync + 'static + EntityKey<K> + EntityAttributes,
699 K: Clone + Debug + PartialEq + Send + Sync + 'static,
700{
701 type Key = K;
702
703 fn key_of(&self, entity: &E) -> Self::Key {
704 entity.key()
705 }
706
707 fn find_by_id(&self, key: &Self::Key) -> RepositoryResult<Option<E>> {
708 let storage = self.storage.read().unwrap();
709 Ok(storage.iter().find(|e| &e.key() == key).cloned())
710 }
711
712 fn find_all(&self) -> RepositoryResult<Vec<E>> {
713 Ok(self.storage.read().unwrap().clone())
714 }
715
716 fn find_by(&self, conditions: &[WhereCondition]) -> RepositoryResult<Vec<E>> {
717 let storage = self.storage.read().unwrap();
718 let result: Vec<E> = storage
719 .iter()
720 .filter(|e| {
721 conditions.iter().all(|c| {
722 let attr = e.get_attribute(&c.field);
723 match (attr, c.op) {
724 (None, WhereOp::IsNull) => true,
725 (None, _) => false,
726 (Some(v), _) => value_matches(&v, c.op, &c.value, &c.extra_values),
727 }
728 })
729 })
730 .cloned()
731 .collect();
732 Ok(result)
733 }
734
735 fn save(&self, entity: E) -> RepositoryResult<E> {
736 let mut storage = self.storage.write().unwrap();
737 let key = entity.key();
738 let existing_idx = storage.iter().position(|e| e.key() == key);
739 match existing_idx {
740 Some(idx) => {
741 storage[idx] = entity.clone();
742 }
743 None => {
744 storage.push(entity.clone());
745 }
746 }
747 Ok(entity)
748 }
749
750 fn delete(&self, key: &Self::Key) -> RepositoryResult<usize> {
751 let mut storage = self.storage.write().unwrap();
752 let before = storage.len();
753 storage.retain(|e| &e.key() != key);
754 Ok(before - storage.len())
755 }
756
757 fn count(&self) -> RepositoryResult<u64> {
758 Ok(self.len() as u64)
759 }
760}
761
762#[cfg(test)]
767mod tests {
768 use super::*;
769
770 #[derive(Debug, Clone, PartialEq)]
773 struct User {
774 id: i64,
775 name: String,
776 age: i64,
777 email: String,
778 }
779
780 impl User {
781 fn new(id: i64, name: &str, age: i64, email: &str) -> Self {
782 Self {
783 id,
784 name: name.to_string(),
785 age,
786 email: email.to_string(),
787 }
788 }
789 }
790
791 impl EntityAttributes for User {
792 fn get_attribute(&self, field: &str) -> Option<Value> {
793 match field {
794 "id" => Some(Value::I64(self.id)),
795 "name" => Some(Value::String(self.name.clone())),
796 "age" => Some(Value::I64(self.age)),
797 "email" => Some(Value::String(self.email.clone())),
798 _ => None,
799 }
800 }
801 }
802
803 impl EntityKey<i64> for User {
804 fn key(&self) -> i64 {
805 self.id
806 }
807 }
808
809 #[test]
812 fn test_where_op_name() {
813 assert_eq!(WhereOp::Eq.name(), "eq");
814 assert_eq!(WhereOp::Ne.name(), "ne");
815 assert_eq!(WhereOp::Gt.name(), "gt");
816 assert_eq!(WhereOp::Like.name(), "like");
817 assert_eq!(WhereOp::In.name(), "in");
818 assert_eq!(WhereOp::IsNull.name(), "is_null");
819 assert_eq!(WhereOp::Between.name(), "between");
820 }
821
822 #[test]
823 fn test_where_condition_new() {
824 let c = WhereCondition::new("age", WhereOp::Ge, Value::I64(18));
825 assert_eq!(c.field, "age");
826 assert_eq!(c.op, WhereOp::Ge);
827 assert_eq!(c.value, Value::I64(18));
828 assert!(c.extra_values.is_empty());
829 }
830
831 #[test]
832 fn test_where_condition_null_check() {
833 let c = WhereCondition::null_check("deleted_at", WhereOp::IsNull);
834 assert_eq!(c.field, "deleted_at");
835 assert_eq!(c.op, WhereOp::IsNull);
836 assert_eq!(c.value, Value::Null);
837 }
838
839 #[test]
840 fn test_where_condition_in() {
841 let c = WhereCondition::in_op(
842 "id",
843 WhereOp::In,
844 vec![Value::I64(1), Value::I64(2), Value::I64(3)],
845 );
846 assert_eq!(c.field, "id");
847 assert_eq!(c.op, WhereOp::In);
848 assert_eq!(c.extra_values.len(), 3);
849 }
850
851 #[test]
852 fn test_where_condition_between() {
853 let c = WhereCondition::between("age", Value::I64(18), Value::I64(30));
854 assert_eq!(c.field, "age");
855 assert_eq!(c.op, WhereOp::Between);
856 assert_eq!(c.value, Value::I64(18));
857 assert_eq!(c.extra_values, vec![Value::I64(30)]);
858 }
859
860 #[test]
863 fn test_page_result_total_pages() {
864 let pr = PageResult::new(vec![1, 2, 3], 100, 1, 10);
865 assert_eq!(pr.total_pages(), 10);
866 }
867
868 #[test]
869 fn test_page_result_total_pages_with_remainder() {
870 let pr: PageResult<i32> = PageResult::new(vec![], 105, 1, 10);
871 assert_eq!(pr.total_pages(), 11);
872 }
873
874 #[test]
875 fn test_page_result_total_pages_zero_size() {
876 let pr: PageResult<i32> = PageResult::new(vec![], 100, 1, 0);
877 assert_eq!(pr.total_pages(), 0);
878 }
879
880 #[test]
881 fn test_page_result_has_next() {
882 let pr = PageResult::new(vec![1, 2, 3], 100, 1, 10);
883 assert!(pr.has_next());
884 assert!(!pr.has_prev());
885 }
886
887 #[test]
888 fn test_page_result_has_prev() {
889 let pr = PageResult::new(vec![1, 2, 3], 100, 5, 10);
890 assert!(pr.has_prev());
891 assert!(pr.has_next()); }
893
894 #[test]
895 fn test_page_result_is_empty() {
896 let pr: PageResult<i32> = PageResult::new(vec![], 0, 1, 10);
897 assert!(pr.is_empty());
898 assert_eq!(pr.len(), 0);
899 }
900
901 #[test]
902 fn test_page_result_map() {
903 let pr = PageResult::new(vec![1, 2, 3], 100, 1, 10);
904 let mapped = pr.map(|x| x * 2);
905 assert_eq!(mapped.items, vec![2, 4, 6]);
906 assert_eq!(mapped.total, 100);
907 }
908
909 #[test]
912 fn test_repository_error_display() {
913 let e = RepositoryError::NotFound;
914 assert_eq!(e.to_string(), "entity not found");
915
916 let e = RepositoryError::DatabaseError("conn refused".to_string());
917 assert_eq!(e.to_string(), "database error: conn refused");
918
919 let e = RepositoryError::InvalidEntity("missing id".to_string());
920 assert_eq!(e.to_string(), "invalid entity: missing id");
921
922 let e = RepositoryError::Other("custom".to_string());
923 assert_eq!(e.to_string(), "repository error: custom");
924 }
925
926 #[test]
927 fn test_repository_error_eq() {
928 assert_eq!(RepositoryError::NotFound, RepositoryError::NotFound);
929 assert_ne!(
930 RepositoryError::NotFound,
931 RepositoryError::Other("x".to_string())
932 );
933 }
934
935 #[test]
938 fn test_inmemory_create_empty() {
939 let repo = InMemoryRepository::<User>::new();
940 assert!(repo.is_empty());
941 assert_eq!(repo.len(), 0);
942 }
943
944 #[test]
945 fn test_inmemory_from_vec() {
946 let repo = InMemoryRepository::from_vec(vec![
947 User::new(1, "Alice", 30, "alice@example.com"),
948 User::new(2, "Bob", 25, "bob@example.com"),
949 ]);
950 assert_eq!(repo.len(), 2);
951 }
952
953 #[test]
954 fn test_inmemory_clear() {
955 let repo = InMemoryRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
956 assert_eq!(repo.len(), 1);
957 repo.clear();
958 assert_eq!(repo.len(), 0);
959 }
960
961 #[test]
964 fn test_repo_save_and_find_by_id() {
965 let repo = InMemoryRepository::<User>::new();
966 let user = User::new(1, "Alice", 30, "alice@example.com");
967 let saved = repo.save(user.clone()).unwrap();
968 assert_eq!(saved, user);
969
970 let found = repo.find_by_id(&Value::I64(1)).unwrap();
971 assert_eq!(found, Some(user));
972 }
973
974 #[test]
975 fn test_repo_find_by_id_missing() {
976 let repo = InMemoryRepository::<User>::new();
977 let found = repo.find_by_id(&Value::I64(999)).unwrap();
978 assert_eq!(found, None);
979 }
980
981 #[test]
982 fn test_repo_save_many() {
983 let repo = InMemoryRepository::<User>::new();
984 let users = vec![
985 User::new(1, "Alice", 30, "alice@example.com"),
986 User::new(2, "Bob", 25, "bob@example.com"),
987 User::new(3, "Carol", 28, "carol@example.com"),
988 ];
989 let saved = repo.save_many(users.clone()).unwrap();
990 assert_eq!(saved.len(), 3);
991 assert_eq!(repo.len(), 3);
992 }
993
994 #[test]
995 fn test_repo_save_update_existing() {
996 let repo = InMemoryRepository::<User>::new();
997 repo.save(User::new(1, "Alice", 30, "alice@example.com"))
998 .unwrap();
999
1000 repo.save(User::new(1, "Alice Updated", 31, "alice2@example.com"))
1002 .unwrap();
1003
1004 assert_eq!(repo.len(), 1);
1005 let found = repo.find_by_id(&Value::I64(1)).unwrap().unwrap();
1006 assert_eq!(found.name, "Alice Updated");
1007 assert_eq!(found.age, 31);
1008 }
1009
1010 #[test]
1011 fn test_repo_find_all() {
1012 let repo = InMemoryRepository::from_vec(vec![
1013 User::new(1, "Alice", 30, "a@b.com"),
1014 User::new(2, "Bob", 25, "b@b.com"),
1015 ]);
1016 let all = repo.find_all().unwrap();
1017 assert_eq!(all.len(), 2);
1018 }
1019
1020 #[test]
1021 fn test_repo_find_all_empty() {
1022 let repo = InMemoryRepository::<User>::new();
1023 let all = repo.find_all().unwrap();
1024 assert!(all.is_empty());
1025 }
1026
1027 #[test]
1028 fn test_repo_delete() {
1029 let repo = InMemoryRepository::from_vec(vec![
1030 User::new(1, "Alice", 30, "a@b.com"),
1031 User::new(2, "Bob", 25, "b@b.com"),
1032 ]);
1033 let deleted = repo.delete(&Value::I64(1)).unwrap();
1034 assert_eq!(deleted, 1);
1035 assert_eq!(repo.len(), 1);
1036 }
1037
1038 #[test]
1039 fn test_repo_delete_missing() {
1040 let repo = InMemoryRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
1041 let deleted = repo.delete(&Value::I64(999)).unwrap();
1042 assert_eq!(deleted, 0);
1043 assert_eq!(repo.len(), 1);
1044 }
1045
1046 #[test]
1047 fn test_repo_count() {
1048 let repo = InMemoryRepository::from_vec(vec![
1049 User::new(1, "Alice", 30, "a@b.com"),
1050 User::new(2, "Bob", 25, "b@b.com"),
1051 User::new(3, "Carol", 28, "c@b.com"),
1052 ]);
1053 assert_eq!(repo.count().unwrap(), 3);
1054 }
1055
1056 #[test]
1057 fn test_repo_count_empty() {
1058 let repo = InMemoryRepository::<User>::new();
1059 assert_eq!(repo.count().unwrap(), 0);
1060 }
1061
1062 #[test]
1063 fn test_repo_exists() {
1064 let repo = InMemoryRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
1065 assert!(repo.exists(&Value::I64(1)).unwrap());
1066 assert!(!repo.exists(&Value::I64(999)).unwrap());
1067 }
1068
1069 #[test]
1072 fn test_repo_find_by_eq() {
1073 let repo = InMemoryRepository::from_vec(vec![
1074 User::new(1, "Alice", 30, "a@b.com"),
1075 User::new(2, "Bob", 30, "b@b.com"),
1076 User::new(3, "Carol", 25, "c@b.com"),
1077 ]);
1078
1079 let result = repo
1080 .find_by(&[WhereCondition::new("age", WhereOp::Eq, Value::I64(30))])
1081 .unwrap();
1082 assert_eq!(result.len(), 2);
1083 }
1084
1085 #[test]
1086 fn test_repo_find_by_gt() {
1087 let repo = InMemoryRepository::from_vec(vec![
1088 User::new(1, "Alice", 30, "a@b.com"),
1089 User::new(2, "Bob", 25, "b@b.com"),
1090 User::new(3, "Carol", 35, "c@b.com"),
1091 ]);
1092
1093 let result = repo
1094 .find_by(&[WhereCondition::new("age", WhereOp::Gt, Value::I64(28))])
1095 .unwrap();
1096 assert_eq!(result.len(), 2);
1097 }
1098
1099 #[test]
1100 fn test_repo_find_by_like() {
1101 let repo = InMemoryRepository::from_vec(vec![
1102 User::new(1, "Alice", 30, "alice@example.com"),
1103 User::new(2, "Bob", 25, "bob@example.com"),
1104 User::new(3, "Alicia", 28, "alicia@test.com"),
1105 ]);
1106
1107 let result = repo
1108 .find_by(&[WhereCondition::new(
1109 "name",
1110 WhereOp::Like,
1111 Value::String("Ali%".to_string()),
1112 )])
1113 .unwrap();
1114 assert_eq!(result.len(), 2);
1115 }
1116
1117 #[test]
1118 fn test_repo_find_by_in() {
1119 let repo = InMemoryRepository::from_vec(vec![
1120 User::new(1, "Alice", 30, "a@b.com"),
1121 User::new(2, "Bob", 25, "b@b.com"),
1122 User::new(3, "Carol", 28, "c@b.com"),
1123 User::new(4, "Dave", 32, "d@b.com"),
1124 ]);
1125
1126 let result = repo
1127 .find_by(&[WhereCondition::in_op(
1128 "id",
1129 WhereOp::In,
1130 vec![Value::I64(1), Value::I64(3)],
1131 )])
1132 .unwrap();
1133 assert_eq!(result.len(), 2);
1134 }
1135
1136 #[test]
1137 fn test_repo_find_by_not_in() {
1138 let repo = InMemoryRepository::from_vec(vec![
1139 User::new(1, "Alice", 30, "a@b.com"),
1140 User::new(2, "Bob", 25, "b@b.com"),
1141 User::new(3, "Carol", 28, "c@b.com"),
1142 ]);
1143
1144 let result = repo
1145 .find_by(&[WhereCondition::in_op(
1146 "id",
1147 WhereOp::NotIn,
1148 vec![Value::I64(1)],
1149 )])
1150 .unwrap();
1151 assert_eq!(result.len(), 2);
1152 assert!(result.iter().all(|u| u.id != 1));
1153 }
1154
1155 #[test]
1156 fn test_repo_find_by_between() {
1157 let repo = InMemoryRepository::from_vec(vec![
1158 User::new(1, "Alice", 30, "a@b.com"),
1159 User::new(2, "Bob", 25, "b@b.com"),
1160 User::new(3, "Carol", 35, "c@b.com"),
1161 User::new(4, "Dave", 22, "d@b.com"),
1162 ]);
1163
1164 let result = repo
1165 .find_by(&[WhereCondition::between(
1166 "age",
1167 Value::I64(25),
1168 Value::I64(35),
1169 )])
1170 .unwrap();
1171 assert_eq!(result.len(), 3);
1172 }
1173
1174 #[test]
1175 fn test_repo_find_by_multiple_conditions() {
1176 let repo = InMemoryRepository::from_vec(vec![
1177 User::new(1, "Alice", 30, "a@b.com"),
1178 User::new(2, "Bob", 30, "b@b.com"),
1179 User::new(3, "Alice", 25, "c@b.com"),
1180 ]);
1181
1182 let result = repo
1183 .find_by(&[
1184 WhereCondition::new("name", WhereOp::Eq, Value::String("Alice".to_string())),
1185 WhereCondition::new("age", WhereOp::Ge, Value::I64(30)),
1186 ])
1187 .unwrap();
1188 assert_eq!(result.len(), 1);
1189 assert_eq!(result[0].id, 1);
1190 }
1191
1192 #[test]
1193 fn test_repo_find_one_by() {
1194 let repo = InMemoryRepository::from_vec(vec![
1195 User::new(1, "Alice", 30, "a@b.com"),
1196 User::new(2, "Bob", 25, "b@b.com"),
1197 ]);
1198
1199 let result = repo
1200 .find_one_by(&[WhereCondition::new(
1201 "name",
1202 WhereOp::Eq,
1203 Value::String("Bob".to_string()),
1204 )])
1205 .unwrap();
1206 assert!(result.is_some());
1207 assert_eq!(result.unwrap().id, 2);
1208 }
1209
1210 #[test]
1211 fn test_repo_find_one_by_missing() {
1212 let repo = InMemoryRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
1213
1214 let result = repo
1215 .find_one_by(&[WhereCondition::new(
1216 "name",
1217 WhereOp::Eq,
1218 Value::String("Missing".to_string()),
1219 )])
1220 .unwrap();
1221 assert!(result.is_none());
1222 }
1223
1224 #[test]
1225 fn test_repo_count_by() {
1226 let repo = InMemoryRepository::from_vec(vec![
1227 User::new(1, "Alice", 30, "a@b.com"),
1228 User::new(2, "Bob", 30, "b@b.com"),
1229 User::new(3, "Carol", 25, "c@b.com"),
1230 ]);
1231
1232 let count = repo
1233 .count_by(&[WhereCondition::new("age", WhereOp::Eq, Value::I64(30))])
1234 .unwrap();
1235 assert_eq!(count, 2);
1236 }
1237
1238 #[test]
1239 fn test_repo_delete_by() {
1240 let repo = InMemoryRepository::from_vec(vec![
1241 User::new(1, "Alice", 30, "a@b.com"),
1242 User::new(2, "Bob", 30, "b@b.com"),
1243 User::new(3, "Carol", 25, "c@b.com"),
1244 ]);
1245
1246 let deleted = repo
1247 .delete_by(&[WhereCondition::new("age", WhereOp::Eq, Value::I64(30))])
1248 .unwrap();
1249 assert_eq!(deleted, 2);
1250 assert_eq!(repo.len(), 1);
1251 }
1252
1253 #[test]
1256 fn test_repo_paginate() {
1257 let users: Vec<User> = (1..=25)
1258 .map(|i| User::new(i, &format!("User{}", i), 20 + (i % 30), "u@b.com"))
1259 .collect();
1260 let repo = InMemoryRepository::from_vec(users);
1261
1262 let page = repo.paginate(1, 10).unwrap();
1263 assert_eq!(page.page, 1);
1264 assert_eq!(page.page_size, 10);
1265 assert_eq!(page.total, 25);
1266 assert_eq!(page.total_pages(), 3);
1267 assert_eq!(page.items.len(), 10);
1268 assert!(page.has_next());
1269 assert!(!page.has_prev());
1270 }
1271
1272 #[test]
1273 fn test_repo_paginate_last_page() {
1274 let users: Vec<User> = (1..=25)
1275 .map(|i| User::new(i, &format!("User{}", i), 20, "u@b.com"))
1276 .collect();
1277 let repo = InMemoryRepository::from_vec(users);
1278
1279 let page = repo.paginate(3, 10).unwrap();
1280 assert_eq!(page.items.len(), 5);
1281 assert!(page.has_prev());
1282 assert!(!page.has_next());
1283 }
1284
1285 #[test]
1286 fn test_repo_paginate_out_of_range() {
1287 let users: Vec<User> = (1..=5)
1288 .map(|i| User::new(i, &format!("User{}", i), 20, "u@b.com"))
1289 .collect();
1290 let repo = InMemoryRepository::from_vec(users);
1291
1292 let page = repo.paginate(10, 10).unwrap();
1293 assert_eq!(page.items.len(), 0);
1294 assert_eq!(page.total, 5);
1295 }
1296
1297 #[test]
1298 fn test_repo_paginate_by() {
1299 let users: Vec<User> = (1..=20)
1300 .map(|i| User::new(i, &format!("User{}", i), 20 + (i % 5), "u@b.com"))
1301 .collect();
1302 let repo = InMemoryRepository::from_vec(users);
1303
1304 let page = repo
1306 .paginate_by(
1307 &[WhereCondition::new("age", WhereOp::Eq, Value::I64(22))],
1308 1,
1309 2,
1310 )
1311 .unwrap();
1312 assert_eq!(page.total, 4);
1313 assert_eq!(page.items.len(), 2);
1314 assert_eq!(page.total_pages(), 2);
1315 }
1316
1317 #[test]
1320 fn test_generic_key_repo_basic() {
1321 let repo: GenericKeyRepository<User, i64> = GenericKeyRepository::new();
1322 assert!(repo.is_empty());
1323
1324 let user = User::new(1, "Alice", 30, "a@b.com");
1325 repo.save(user.clone()).unwrap();
1326 assert_eq!(repo.len(), 1);
1327
1328 let found = repo.find_by_id(&1).unwrap();
1329 assert_eq!(found, Some(user));
1330 }
1331
1332 #[test]
1333 fn test_generic_key_repo_delete() {
1334 let repo: GenericKeyRepository<User, i64> = GenericKeyRepository::from_vec(vec![
1335 User::new(1, "Alice", 30, "a@b.com"),
1336 User::new(2, "Bob", 25, "b@b.com"),
1337 ]);
1338
1339 let deleted = repo.delete(&1).unwrap();
1340 assert_eq!(deleted, 1);
1341 assert_eq!(repo.len(), 1);
1342
1343 let remaining = repo.find_all().unwrap();
1344 assert_eq!(remaining[0].id, 2);
1345 }
1346
1347 #[test]
1348 fn test_generic_key_repo_find_by() {
1349 let repo: GenericKeyRepository<User, i64> = GenericKeyRepository::from_vec(vec![
1350 User::new(1, "Alice", 30, "a@b.com"),
1351 User::new(2, "Bob", 30, "b@b.com"),
1352 User::new(3, "Carol", 25, "c@b.com"),
1353 ]);
1354
1355 let result = repo
1356 .find_by(&[WhereCondition::new("age", WhereOp::Eq, Value::I64(30))])
1357 .unwrap();
1358 assert_eq!(result.len(), 2);
1359 }
1360
1361 #[test]
1362 fn test_generic_key_repo_paginate() {
1363 let users: Vec<User> = (1..=15)
1364 .map(|i| User::new(i, &format!("User{}", i), 20, "u@b.com"))
1365 .collect();
1366 let repo: GenericKeyRepository<User, i64> = GenericKeyRepository::from_vec(users);
1367
1368 let page = repo.paginate(2, 10).unwrap();
1369 assert_eq!(page.items.len(), 5);
1370 assert_eq!(page.total, 15);
1371 assert_eq!(page.page, 2);
1372 }
1373
1374 #[test]
1375 fn test_generic_key_repo_count() {
1376 let repo: GenericKeyRepository<User, i64> = GenericKeyRepository::from_vec(vec![
1377 User::new(1, "Alice", 30, "a@b.com"),
1378 User::new(2, "Bob", 25, "b@b.com"),
1379 ]);
1380 assert_eq!(repo.count().unwrap(), 2);
1381 }
1382
1383 #[test]
1384 fn test_generic_key_repo_exists() {
1385 let repo: GenericKeyRepository<User, i64> =
1386 GenericKeyRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
1387 assert!(repo.exists(&1).unwrap());
1388 assert!(!repo.exists(&999).unwrap());
1389 }
1390
1391 #[test]
1394 fn test_simple_regex_literal() {
1395 let re = simple_regex::compile("^abc$").unwrap();
1396 assert!(re.is_match("abc"));
1397 assert!(!re.is_match("abcd"));
1398 }
1399
1400 #[test]
1401 fn test_simple_regex_wildcard() {
1402 let re = simple_regex::compile("^Ali.*$").unwrap();
1403 assert!(re.is_match("Alice"));
1404 assert!(re.is_match("Alicia"));
1405 assert!(!re.is_match("Bob"));
1406 }
1407
1408 #[test]
1409 fn test_simple_regex_no_anchors() {
1410 let re = simple_regex::compile("ab").unwrap();
1411 assert!(re.is_match("ab"));
1412 }
1413
1414 #[test]
1417 fn test_e2e_repository_workflow() {
1418 let repo = InMemoryRepository::<User>::new();
1419
1420 let users = vec![
1422 User::new(1, "Alice", 30, "alice@example.com"),
1423 User::new(2, "Bob", 25, "bob@example.com"),
1424 User::new(3, "Carol", 35, "carol@example.com"),
1425 User::new(4, "Dave", 28, "dave@example.com"),
1426 User::new(5, "Eve", 32, "eve@example.com"),
1427 ];
1428 repo.save_many(users).unwrap();
1429 assert_eq!(repo.count().unwrap(), 5);
1430
1431 let adults = repo
1433 .find_by(&[WhereCondition::new("age", WhereOp::Ge, Value::I64(30))])
1434 .unwrap();
1435 assert_eq!(adults.len(), 3);
1436
1437 let page1 = repo.paginate(1, 2).unwrap();
1439 assert_eq!(page1.items.len(), 2);
1440 assert_eq!(page1.total_pages(), 3);
1441
1442 let page2 = repo.paginate(2, 2).unwrap();
1443 assert_eq!(page2.items.len(), 2);
1444
1445 let page3 = repo.paginate(3, 2).unwrap();
1446 assert_eq!(page3.items.len(), 1);
1447
1448 let adult_page = repo
1450 .paginate_by(
1451 &[WhereCondition::new("age", WhereOp::Ge, Value::I64(30))],
1452 1,
1453 2,
1454 )
1455 .unwrap();
1456 assert_eq!(adult_page.total, 3);
1457 assert_eq!(adult_page.items.len(), 2);
1458
1459 repo.save(User::new(1, "Alice Smith", 31, "alice.smith@example.com"))
1461 .unwrap();
1462 let updated = repo.find_by_id(&Value::I64(1)).unwrap().unwrap();
1463 assert_eq!(updated.name, "Alice Smith");
1464 assert_eq!(updated.age, 31);
1465
1466 let deleted = repo.delete(&Value::I64(2)).unwrap();
1468 assert_eq!(deleted, 1);
1469 assert_eq!(repo.count().unwrap(), 4);
1470 assert!(!repo.exists(&Value::I64(2)).unwrap());
1471
1472 let deleted_by = repo
1474 .delete_by(&[WhereCondition::new("age", WhereOp::Ge, Value::I64(31))])
1475 .unwrap();
1476 assert_eq!(deleted_by, 3);
1477 assert_eq!(repo.count().unwrap(), 1); }
1479
1480 #[test]
1481 fn test_e2e_pagination_navigation() {
1482 let users: Vec<User> = (1..=100)
1483 .map(|i| User::new(i, &format!("User{}", i), 20, "u@b.com"))
1484 .collect();
1485 let repo = InMemoryRepository::from_vec(users);
1486
1487 let mut current_page = 1u64;
1488 let mut visited: Vec<u64> = Vec::new();
1489 loop {
1490 let page = repo.paginate(current_page, 10).unwrap();
1491 visited.push(current_page);
1492 if !page.has_next() {
1493 break;
1494 }
1495 current_page += 1;
1496 }
1497 assert_eq!(visited.len(), 10);
1498 assert_eq!(visited, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
1499 }
1500}