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
398 .storage
399 .read()
400 .expect("InMemoryRepository storage lock poisoned (len)");
401 storage.len()
402 }
403
404 pub fn is_empty(&self) -> bool {
406 self.len() == 0
407 }
408
409 pub fn clear(&self) {
411 let mut storage = self
412 .storage
413 .write()
414 .expect("InMemoryRepository storage lock poisoned (clear)");
415 storage.clear();
416 }
417}
418
419impl<E: Clone + Send + Sync + 'static> Default for InMemoryRepository<E> {
420 fn default() -> Self {
421 Self::new()
422 }
423}
424
425fn value_matches(value: &Value, op: WhereOp, target: &Value, extras: &[Value]) -> bool {
427 use Value::*;
428 match op {
429 WhereOp::Eq => value == target,
430 WhereOp::Ne => value != target,
431 WhereOp::Gt => match (value, target) {
432 (I64(a), I64(b)) => a > b,
433 (F64(a), F64(b)) => a > b,
434 (F64(a), I64(b)) => a > &(*b as f64),
435 (I64(a), F64(b)) => a > &(*b as i64),
436 (String(a), String(b)) => a > b,
437 _ => false,
438 },
439 WhereOp::Ge => 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::Lt => 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::Le => match (value, target) {
452 (I64(a), I64(b)) => a <= b,
453 (F64(a), F64(b)) => a <= b,
454 (String(a), String(b)) => a <= b,
455 _ => false,
456 },
457 WhereOp::Like => match (value, target) {
458 (String(a), String(b)) => {
459 let pattern = b.replace('%', ".*").replace('_', ".");
461 let full_pattern = format!("^{}$", pattern);
462 if let Ok(re) = simple_regex::compile(&full_pattern) {
463 re.is_match(a)
464 } else {
465 false
466 }
467 }
468 _ => false,
469 },
470 WhereOp::In => extras.iter().any(|v| v == value),
471 WhereOp::NotIn => !extras.iter().any(|v| v == value),
472 WhereOp::IsNull => matches!(value, Null),
473 WhereOp::IsNotNull => !matches!(value, Null),
474 WhereOp::Between => {
475 if extras.is_empty() {
476 return false;
477 }
478 let low = target;
479 let high = &extras[0];
480 value_matches(value, WhereOp::Ge, low, &[])
482 && value_matches(value, WhereOp::Le, high, &[])
483 }
484 }
485}
486
487pub trait EntityAttributes: Send + Sync {
489 fn get_attribute(&self, field: &str) -> Option<Value>;
491}
492
493impl<E: Clone + Send + Sync + 'static + EntityAttributes> Repository<E> for InMemoryRepository<E> {
495 type Key = Value;
496
497 fn key_of(&self, entity: &E) -> Self::Key {
498 entity.get_attribute("id").unwrap_or(Value::Null)
499 }
500
501 fn find_by_id(&self, key: &Self::Key) -> RepositoryResult<Option<E>> {
502 let storage = self
503 .storage
504 .read()
505 .expect("InMemoryRepository storage lock poisoned (find_by_id)");
506 Ok(storage.iter().find(|e| self.key_of(e) == *key).cloned())
507 }
508
509 fn find_all(&self) -> RepositoryResult<Vec<E>> {
510 let storage = self
511 .storage
512 .read()
513 .expect("InMemoryRepository storage lock poisoned (find_all)");
514 Ok(storage.clone())
515 }
516
517 fn find_by(&self, conditions: &[WhereCondition]) -> RepositoryResult<Vec<E>> {
518 let storage = self
519 .storage
520 .read()
521 .expect("InMemoryRepository storage lock poisoned (find_by)");
522 let result: Vec<E> = storage
523 .iter()
524 .filter(|e| {
525 conditions.iter().all(|c| {
526 let attr = e.get_attribute(&c.field);
527 match (attr, c.op) {
528 (None, WhereOp::IsNull) => true,
529 (None, _) => false,
530 (Some(v), _) => value_matches(&v, c.op, &c.value, &c.extra_values),
531 }
532 })
533 })
534 .cloned()
535 .collect();
536 Ok(result)
537 }
538
539 fn save(&self, mut entity: E) -> RepositoryResult<E> {
540 let mut storage = self
541 .storage
542 .write()
543 .expect("InMemoryRepository storage lock poisoned (save)");
544 let key = self.key_of(&entity);
545
546 let existing_idx = storage.iter().position(|e| self.key_of(e) == key);
548
549 match existing_idx {
550 Some(idx) => {
551 storage[idx] = entity.clone();
552 }
553 None => {
554 storage.push(entity.clone());
555 }
556 }
557 let _ = &mut entity; Ok(entity)
560 }
561
562 fn delete(&self, key: &Self::Key) -> RepositoryResult<usize> {
563 let mut storage = self
564 .storage
565 .write()
566 .expect("InMemoryRepository storage lock poisoned (delete)");
567 let before = storage.len();
568 storage.retain(|e| self.key_of(e) != *key);
569 Ok(before - storage.len())
570 }
571
572 fn count(&self) -> RepositoryResult<u64> {
573 Ok(self.len() as u64)
574 }
575}
576
577mod simple_regex {
582 pub struct Regex {
584 patterns: Vec<Pattern>,
585 }
586
587 enum Pattern {
588 AnyChars, Literal(char), Start, End, }
593
594 pub fn compile(pattern: &str) -> Result<Regex, String> {
595 let mut patterns = Vec::new();
596 let chars: Vec<char> = pattern.chars().collect();
597 let mut i = 0;
598 while i < chars.len() {
599 match chars[i] {
600 '^' => {
601 patterns.push(Pattern::Start);
602 i += 1;
603 }
604 '$' => {
605 patterns.push(Pattern::End);
606 i += 1;
607 }
608 '.' if i + 1 < chars.len() && chars[i + 1] == '*' => {
609 patterns.push(Pattern::AnyChars);
610 i += 2;
611 }
612 c => {
613 patterns.push(Pattern::Literal(c));
614 i += 1;
615 }
616 }
617 }
618 Ok(Regex { patterns })
619 }
620
621 impl Regex {
622 pub fn is_match(&self, text: &str) -> bool {
623 self.match_from(text, 0, 0)
624 }
625
626 fn match_from(&self, text: &str, text_idx: usize, pat_idx: usize) -> bool {
627 let chars: Vec<char> = text.chars().collect();
628 if pat_idx >= self.patterns.len() {
629 return text_idx == chars.len();
630 }
631 match &self.patterns[pat_idx] {
632 Pattern::Start => self.match_from(text, 0, pat_idx + 1),
633 Pattern::End => text_idx == chars.len(),
634 Pattern::Literal(c) => {
635 if text_idx < chars.len() && chars[text_idx] == *c {
636 self.match_from(text, text_idx + 1, pat_idx + 1)
637 } else {
638 false
639 }
640 }
641 Pattern::AnyChars => {
642 for skip in 0..=(chars.len() - text_idx) {
644 if self.match_from(text, text_idx + skip, pat_idx + 1) {
645 return true;
646 }
647 }
648 false
649 }
650 }
651 }
652 }
653}
654
655pub trait EntityKey<K>: Send + Sync {
661 fn key(&self) -> K;
663}
664
665pub struct GenericKeyRepository<E, K>
667where
668 E: Clone + Send + Sync + 'static,
669 K: Clone + Debug + PartialEq + Send + Sync + 'static,
670{
671 storage: RwLock<Vec<E>>,
672 _phantom: std::marker::PhantomData<K>,
673}
674
675impl<E, K> GenericKeyRepository<E, K>
676where
677 E: Clone + Send + Sync + 'static,
678 K: Clone + Debug + PartialEq + Send + Sync + 'static,
679{
680 pub fn new() -> Self {
681 Self {
682 storage: RwLock::new(Vec::new()),
683 _phantom: std::marker::PhantomData,
684 }
685 }
686
687 pub fn from_vec(items: Vec<E>) -> Self {
688 Self {
689 storage: RwLock::new(items),
690 _phantom: std::marker::PhantomData,
691 }
692 }
693
694 pub fn len(&self) -> usize {
695 self.storage
696 .read()
697 .expect("GenericKeyRepository storage lock poisoned (len)")
698 .len()
699 }
700
701 pub fn is_empty(&self) -> bool {
702 self.len() == 0
703 }
704
705 pub fn clear(&self) {
706 self.storage
707 .write()
708 .expect("GenericKeyRepository storage lock poisoned (clear)")
709 .clear();
710 }
711}
712
713impl<E, K> Default for GenericKeyRepository<E, K>
714where
715 E: Clone + Send + Sync + 'static,
716 K: Clone + Debug + PartialEq + Send + Sync + 'static,
717{
718 fn default() -> Self {
719 Self::new()
720 }
721}
722
723impl<E, K> Repository<E> for GenericKeyRepository<E, K>
724where
725 E: Clone + Send + Sync + 'static + EntityKey<K> + EntityAttributes,
726 K: Clone + Debug + PartialEq + Send + Sync + 'static,
727{
728 type Key = K;
729
730 fn key_of(&self, entity: &E) -> Self::Key {
731 entity.key()
732 }
733
734 fn find_by_id(&self, key: &Self::Key) -> RepositoryResult<Option<E>> {
735 let storage = self
736 .storage
737 .read()
738 .expect("GenericKeyRepository storage lock poisoned (find_by_id)");
739 Ok(storage.iter().find(|e| &e.key() == key).cloned())
740 }
741
742 fn find_all(&self) -> RepositoryResult<Vec<E>> {
743 Ok(self
744 .storage
745 .read()
746 .expect("GenericKeyRepository storage lock poisoned (find_all)")
747 .clone())
748 }
749
750 fn find_by(&self, conditions: &[WhereCondition]) -> RepositoryResult<Vec<E>> {
751 let storage = self
752 .storage
753 .read()
754 .expect("GenericKeyRepository storage lock poisoned (find_by)");
755 let result: Vec<E> = storage
756 .iter()
757 .filter(|e| {
758 conditions.iter().all(|c| {
759 let attr = e.get_attribute(&c.field);
760 match (attr, c.op) {
761 (None, WhereOp::IsNull) => true,
762 (None, _) => false,
763 (Some(v), _) => value_matches(&v, c.op, &c.value, &c.extra_values),
764 }
765 })
766 })
767 .cloned()
768 .collect();
769 Ok(result)
770 }
771
772 fn save(&self, entity: E) -> RepositoryResult<E> {
773 let mut storage = self
774 .storage
775 .write()
776 .expect("GenericKeyRepository storage lock poisoned (save)");
777 let key = entity.key();
778 let existing_idx = storage.iter().position(|e| e.key() == key);
779 match existing_idx {
780 Some(idx) => {
781 storage[idx] = entity.clone();
782 }
783 None => {
784 storage.push(entity.clone());
785 }
786 }
787 Ok(entity)
788 }
789
790 fn delete(&self, key: &Self::Key) -> RepositoryResult<usize> {
791 let mut storage = self
792 .storage
793 .write()
794 .expect("GenericKeyRepository storage lock poisoned (delete)");
795 let before = storage.len();
796 storage.retain(|e| &e.key() != key);
797 Ok(before - storage.len())
798 }
799
800 fn count(&self) -> RepositoryResult<u64> {
801 Ok(self.len() as u64)
802 }
803}
804
805#[cfg(test)]
810mod tests {
811 use super::*;
812
813 #[derive(Debug, Clone, PartialEq)]
816 struct User {
817 id: i64,
818 name: String,
819 age: i64,
820 email: String,
821 }
822
823 impl User {
824 fn new(id: i64, name: &str, age: i64, email: &str) -> Self {
825 Self {
826 id,
827 name: name.to_string(),
828 age,
829 email: email.to_string(),
830 }
831 }
832 }
833
834 impl EntityAttributes for User {
835 fn get_attribute(&self, field: &str) -> Option<Value> {
836 match field {
837 "id" => Some(Value::I64(self.id)),
838 "name" => Some(Value::String(self.name.clone())),
839 "age" => Some(Value::I64(self.age)),
840 "email" => Some(Value::String(self.email.clone())),
841 _ => None,
842 }
843 }
844 }
845
846 impl EntityKey<i64> for User {
847 fn key(&self) -> i64 {
848 self.id
849 }
850 }
851
852 #[test]
855 fn test_where_op_name() {
856 assert_eq!(WhereOp::Eq.name(), "eq");
857 assert_eq!(WhereOp::Ne.name(), "ne");
858 assert_eq!(WhereOp::Gt.name(), "gt");
859 assert_eq!(WhereOp::Like.name(), "like");
860 assert_eq!(WhereOp::In.name(), "in");
861 assert_eq!(WhereOp::IsNull.name(), "is_null");
862 assert_eq!(WhereOp::Between.name(), "between");
863 }
864
865 #[test]
866 fn test_where_condition_new() {
867 let c = WhereCondition::new("age", WhereOp::Ge, Value::I64(18));
868 assert_eq!(c.field, "age");
869 assert_eq!(c.op, WhereOp::Ge);
870 assert_eq!(c.value, Value::I64(18));
871 assert!(c.extra_values.is_empty());
872 }
873
874 #[test]
875 fn test_where_condition_null_check() {
876 let c = WhereCondition::null_check("deleted_at", WhereOp::IsNull);
877 assert_eq!(c.field, "deleted_at");
878 assert_eq!(c.op, WhereOp::IsNull);
879 assert_eq!(c.value, Value::Null);
880 }
881
882 #[test]
883 fn test_where_condition_in() {
884 let c = WhereCondition::in_op(
885 "id",
886 WhereOp::In,
887 vec![Value::I64(1), Value::I64(2), Value::I64(3)],
888 );
889 assert_eq!(c.field, "id");
890 assert_eq!(c.op, WhereOp::In);
891 assert_eq!(c.extra_values.len(), 3);
892 }
893
894 #[test]
895 fn test_where_condition_between() {
896 let c = WhereCondition::between("age", Value::I64(18), Value::I64(30));
897 assert_eq!(c.field, "age");
898 assert_eq!(c.op, WhereOp::Between);
899 assert_eq!(c.value, Value::I64(18));
900 assert_eq!(c.extra_values, vec![Value::I64(30)]);
901 }
902
903 #[test]
906 fn test_page_result_total_pages() {
907 let pr = PageResult::new(vec![1, 2, 3], 100, 1, 10);
908 assert_eq!(pr.total_pages(), 10);
909 }
910
911 #[test]
912 fn test_page_result_total_pages_with_remainder() {
913 let pr: PageResult<i32> = PageResult::new(vec![], 105, 1, 10);
914 assert_eq!(pr.total_pages(), 11);
915 }
916
917 #[test]
918 fn test_page_result_total_pages_zero_size() {
919 let pr: PageResult<i32> = PageResult::new(vec![], 100, 1, 0);
920 assert_eq!(pr.total_pages(), 0);
921 }
922
923 #[test]
924 fn test_page_result_has_next() {
925 let pr = PageResult::new(vec![1, 2, 3], 100, 1, 10);
926 assert!(pr.has_next());
927 assert!(!pr.has_prev());
928 }
929
930 #[test]
931 fn test_page_result_has_prev() {
932 let pr = PageResult::new(vec![1, 2, 3], 100, 5, 10);
933 assert!(pr.has_prev());
934 assert!(pr.has_next()); }
936
937 #[test]
938 fn test_page_result_is_empty() {
939 let pr: PageResult<i32> = PageResult::new(vec![], 0, 1, 10);
940 assert!(pr.is_empty());
941 assert_eq!(pr.len(), 0);
942 }
943
944 #[test]
945 fn test_page_result_map() {
946 let pr = PageResult::new(vec![1, 2, 3], 100, 1, 10);
947 let mapped = pr.map(|x| x * 2);
948 assert_eq!(mapped.items, vec![2, 4, 6]);
949 assert_eq!(mapped.total, 100);
950 }
951
952 #[test]
955 fn test_repository_error_display() {
956 let e = RepositoryError::NotFound;
957 assert_eq!(e.to_string(), "entity not found");
958
959 let e = RepositoryError::DatabaseError("conn refused".to_string());
960 assert_eq!(e.to_string(), "database error: conn refused");
961
962 let e = RepositoryError::InvalidEntity("missing id".to_string());
963 assert_eq!(e.to_string(), "invalid entity: missing id");
964
965 let e = RepositoryError::Other("custom".to_string());
966 assert_eq!(e.to_string(), "repository error: custom");
967 }
968
969 #[test]
970 fn test_repository_error_eq() {
971 assert_eq!(RepositoryError::NotFound, RepositoryError::NotFound);
972 assert_ne!(
973 RepositoryError::NotFound,
974 RepositoryError::Other("x".to_string())
975 );
976 }
977
978 #[test]
981 fn test_inmemory_create_empty() {
982 let repo = InMemoryRepository::<User>::new();
983 assert!(repo.is_empty());
984 assert_eq!(repo.len(), 0);
985 }
986
987 #[test]
988 fn test_inmemory_from_vec() {
989 let repo = InMemoryRepository::from_vec(vec![
990 User::new(1, "Alice", 30, "alice@example.com"),
991 User::new(2, "Bob", 25, "bob@example.com"),
992 ]);
993 assert_eq!(repo.len(), 2);
994 }
995
996 #[test]
997 fn test_inmemory_clear() {
998 let repo = InMemoryRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
999 assert_eq!(repo.len(), 1);
1000 repo.clear();
1001 assert_eq!(repo.len(), 0);
1002 }
1003
1004 #[test]
1007 fn test_repo_save_and_find_by_id() {
1008 let repo = InMemoryRepository::<User>::new();
1009 let user = User::new(1, "Alice", 30, "alice@example.com");
1010 let saved = repo.save(user.clone()).unwrap();
1011 assert_eq!(saved, user);
1012
1013 let found = repo.find_by_id(&Value::I64(1)).unwrap();
1014 assert_eq!(found, Some(user));
1015 }
1016
1017 #[test]
1018 fn test_repo_find_by_id_missing() {
1019 let repo = InMemoryRepository::<User>::new();
1020 let found = repo.find_by_id(&Value::I64(999)).unwrap();
1021 assert_eq!(found, None);
1022 }
1023
1024 #[test]
1025 fn test_repo_save_many() {
1026 let repo = InMemoryRepository::<User>::new();
1027 let users = vec![
1028 User::new(1, "Alice", 30, "alice@example.com"),
1029 User::new(2, "Bob", 25, "bob@example.com"),
1030 User::new(3, "Carol", 28, "carol@example.com"),
1031 ];
1032 let saved = repo.save_many(users.clone()).unwrap();
1033 assert_eq!(saved.len(), 3);
1034 assert_eq!(repo.len(), 3);
1035 }
1036
1037 #[test]
1038 fn test_repo_save_update_existing() {
1039 let repo = InMemoryRepository::<User>::new();
1040 repo.save(User::new(1, "Alice", 30, "alice@example.com"))
1041 .unwrap();
1042
1043 repo.save(User::new(1, "Alice Updated", 31, "alice2@example.com"))
1045 .unwrap();
1046
1047 assert_eq!(repo.len(), 1);
1048 let found = repo.find_by_id(&Value::I64(1)).unwrap().unwrap();
1049 assert_eq!(found.name, "Alice Updated");
1050 assert_eq!(found.age, 31);
1051 }
1052
1053 #[test]
1054 fn test_repo_find_all() {
1055 let repo = InMemoryRepository::from_vec(vec![
1056 User::new(1, "Alice", 30, "a@b.com"),
1057 User::new(2, "Bob", 25, "b@b.com"),
1058 ]);
1059 let all = repo.find_all().unwrap();
1060 assert_eq!(all.len(), 2);
1061 }
1062
1063 #[test]
1064 fn test_repo_find_all_empty() {
1065 let repo = InMemoryRepository::<User>::new();
1066 let all = repo.find_all().unwrap();
1067 assert!(all.is_empty());
1068 }
1069
1070 #[test]
1071 fn test_repo_delete() {
1072 let repo = InMemoryRepository::from_vec(vec![
1073 User::new(1, "Alice", 30, "a@b.com"),
1074 User::new(2, "Bob", 25, "b@b.com"),
1075 ]);
1076 let deleted = repo.delete(&Value::I64(1)).unwrap();
1077 assert_eq!(deleted, 1);
1078 assert_eq!(repo.len(), 1);
1079 }
1080
1081 #[test]
1082 fn test_repo_delete_missing() {
1083 let repo = InMemoryRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
1084 let deleted = repo.delete(&Value::I64(999)).unwrap();
1085 assert_eq!(deleted, 0);
1086 assert_eq!(repo.len(), 1);
1087 }
1088
1089 #[test]
1090 fn test_repo_count() {
1091 let repo = InMemoryRepository::from_vec(vec![
1092 User::new(1, "Alice", 30, "a@b.com"),
1093 User::new(2, "Bob", 25, "b@b.com"),
1094 User::new(3, "Carol", 28, "c@b.com"),
1095 ]);
1096 assert_eq!(repo.count().unwrap(), 3);
1097 }
1098
1099 #[test]
1100 fn test_repo_count_empty() {
1101 let repo = InMemoryRepository::<User>::new();
1102 assert_eq!(repo.count().unwrap(), 0);
1103 }
1104
1105 #[test]
1106 fn test_repo_exists() {
1107 let repo = InMemoryRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
1108 assert!(repo.exists(&Value::I64(1)).unwrap());
1109 assert!(!repo.exists(&Value::I64(999)).unwrap());
1110 }
1111
1112 #[test]
1115 fn test_repo_find_by_eq() {
1116 let repo = InMemoryRepository::from_vec(vec![
1117 User::new(1, "Alice", 30, "a@b.com"),
1118 User::new(2, "Bob", 30, "b@b.com"),
1119 User::new(3, "Carol", 25, "c@b.com"),
1120 ]);
1121
1122 let result = repo
1123 .find_by(&[WhereCondition::new("age", WhereOp::Eq, Value::I64(30))])
1124 .unwrap();
1125 assert_eq!(result.len(), 2);
1126 }
1127
1128 #[test]
1129 fn test_repo_find_by_gt() {
1130 let repo = InMemoryRepository::from_vec(vec![
1131 User::new(1, "Alice", 30, "a@b.com"),
1132 User::new(2, "Bob", 25, "b@b.com"),
1133 User::new(3, "Carol", 35, "c@b.com"),
1134 ]);
1135
1136 let result = repo
1137 .find_by(&[WhereCondition::new("age", WhereOp::Gt, Value::I64(28))])
1138 .unwrap();
1139 assert_eq!(result.len(), 2);
1140 }
1141
1142 #[test]
1143 fn test_repo_find_by_like() {
1144 let repo = InMemoryRepository::from_vec(vec![
1145 User::new(1, "Alice", 30, "alice@example.com"),
1146 User::new(2, "Bob", 25, "bob@example.com"),
1147 User::new(3, "Alicia", 28, "alicia@test.com"),
1148 ]);
1149
1150 let result = repo
1151 .find_by(&[WhereCondition::new(
1152 "name",
1153 WhereOp::Like,
1154 Value::String("Ali%".to_string()),
1155 )])
1156 .unwrap();
1157 assert_eq!(result.len(), 2);
1158 }
1159
1160 #[test]
1161 fn test_repo_find_by_in() {
1162 let repo = InMemoryRepository::from_vec(vec![
1163 User::new(1, "Alice", 30, "a@b.com"),
1164 User::new(2, "Bob", 25, "b@b.com"),
1165 User::new(3, "Carol", 28, "c@b.com"),
1166 User::new(4, "Dave", 32, "d@b.com"),
1167 ]);
1168
1169 let result = repo
1170 .find_by(&[WhereCondition::in_op(
1171 "id",
1172 WhereOp::In,
1173 vec![Value::I64(1), Value::I64(3)],
1174 )])
1175 .unwrap();
1176 assert_eq!(result.len(), 2);
1177 }
1178
1179 #[test]
1180 fn test_repo_find_by_not_in() {
1181 let repo = InMemoryRepository::from_vec(vec![
1182 User::new(1, "Alice", 30, "a@b.com"),
1183 User::new(2, "Bob", 25, "b@b.com"),
1184 User::new(3, "Carol", 28, "c@b.com"),
1185 ]);
1186
1187 let result = repo
1188 .find_by(&[WhereCondition::in_op(
1189 "id",
1190 WhereOp::NotIn,
1191 vec![Value::I64(1)],
1192 )])
1193 .unwrap();
1194 assert_eq!(result.len(), 2);
1195 assert!(result.iter().all(|u| u.id != 1));
1196 }
1197
1198 #[test]
1199 fn test_repo_find_by_between() {
1200 let repo = InMemoryRepository::from_vec(vec![
1201 User::new(1, "Alice", 30, "a@b.com"),
1202 User::new(2, "Bob", 25, "b@b.com"),
1203 User::new(3, "Carol", 35, "c@b.com"),
1204 User::new(4, "Dave", 22, "d@b.com"),
1205 ]);
1206
1207 let result = repo
1208 .find_by(&[WhereCondition::between(
1209 "age",
1210 Value::I64(25),
1211 Value::I64(35),
1212 )])
1213 .unwrap();
1214 assert_eq!(result.len(), 3);
1215 }
1216
1217 #[test]
1218 fn test_repo_find_by_multiple_conditions() {
1219 let repo = InMemoryRepository::from_vec(vec![
1220 User::new(1, "Alice", 30, "a@b.com"),
1221 User::new(2, "Bob", 30, "b@b.com"),
1222 User::new(3, "Alice", 25, "c@b.com"),
1223 ]);
1224
1225 let result = repo
1226 .find_by(&[
1227 WhereCondition::new("name", WhereOp::Eq, Value::String("Alice".to_string())),
1228 WhereCondition::new("age", WhereOp::Ge, Value::I64(30)),
1229 ])
1230 .unwrap();
1231 assert_eq!(result.len(), 1);
1232 assert_eq!(result[0].id, 1);
1233 }
1234
1235 #[test]
1236 fn test_repo_find_one_by() {
1237 let repo = InMemoryRepository::from_vec(vec![
1238 User::new(1, "Alice", 30, "a@b.com"),
1239 User::new(2, "Bob", 25, "b@b.com"),
1240 ]);
1241
1242 let result = repo
1243 .find_one_by(&[WhereCondition::new(
1244 "name",
1245 WhereOp::Eq,
1246 Value::String("Bob".to_string()),
1247 )])
1248 .unwrap();
1249 assert!(result.is_some());
1250 assert_eq!(result.unwrap().id, 2);
1251 }
1252
1253 #[test]
1254 fn test_repo_find_one_by_missing() {
1255 let repo = InMemoryRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
1256
1257 let result = repo
1258 .find_one_by(&[WhereCondition::new(
1259 "name",
1260 WhereOp::Eq,
1261 Value::String("Missing".to_string()),
1262 )])
1263 .unwrap();
1264 assert!(result.is_none());
1265 }
1266
1267 #[test]
1268 fn test_repo_count_by() {
1269 let repo = InMemoryRepository::from_vec(vec![
1270 User::new(1, "Alice", 30, "a@b.com"),
1271 User::new(2, "Bob", 30, "b@b.com"),
1272 User::new(3, "Carol", 25, "c@b.com"),
1273 ]);
1274
1275 let count = repo
1276 .count_by(&[WhereCondition::new("age", WhereOp::Eq, Value::I64(30))])
1277 .unwrap();
1278 assert_eq!(count, 2);
1279 }
1280
1281 #[test]
1282 fn test_repo_delete_by() {
1283 let repo = InMemoryRepository::from_vec(vec![
1284 User::new(1, "Alice", 30, "a@b.com"),
1285 User::new(2, "Bob", 30, "b@b.com"),
1286 User::new(3, "Carol", 25, "c@b.com"),
1287 ]);
1288
1289 let deleted = repo
1290 .delete_by(&[WhereCondition::new("age", WhereOp::Eq, Value::I64(30))])
1291 .unwrap();
1292 assert_eq!(deleted, 2);
1293 assert_eq!(repo.len(), 1);
1294 }
1295
1296 #[test]
1299 fn test_repo_paginate() {
1300 let users: Vec<User> = (1..=25)
1301 .map(|i| User::new(i, &format!("User{}", i), 20 + (i % 30), "u@b.com"))
1302 .collect();
1303 let repo = InMemoryRepository::from_vec(users);
1304
1305 let page = repo.paginate(1, 10).unwrap();
1306 assert_eq!(page.page, 1);
1307 assert_eq!(page.page_size, 10);
1308 assert_eq!(page.total, 25);
1309 assert_eq!(page.total_pages(), 3);
1310 assert_eq!(page.items.len(), 10);
1311 assert!(page.has_next());
1312 assert!(!page.has_prev());
1313 }
1314
1315 #[test]
1316 fn test_repo_paginate_last_page() {
1317 let users: Vec<User> = (1..=25)
1318 .map(|i| User::new(i, &format!("User{}", i), 20, "u@b.com"))
1319 .collect();
1320 let repo = InMemoryRepository::from_vec(users);
1321
1322 let page = repo.paginate(3, 10).unwrap();
1323 assert_eq!(page.items.len(), 5);
1324 assert!(page.has_prev());
1325 assert!(!page.has_next());
1326 }
1327
1328 #[test]
1329 fn test_repo_paginate_out_of_range() {
1330 let users: Vec<User> = (1..=5)
1331 .map(|i| User::new(i, &format!("User{}", i), 20, "u@b.com"))
1332 .collect();
1333 let repo = InMemoryRepository::from_vec(users);
1334
1335 let page = repo.paginate(10, 10).unwrap();
1336 assert_eq!(page.items.len(), 0);
1337 assert_eq!(page.total, 5);
1338 }
1339
1340 #[test]
1341 fn test_repo_paginate_by() {
1342 let users: Vec<User> = (1..=20)
1343 .map(|i| User::new(i, &format!("User{}", i), 20 + (i % 5), "u@b.com"))
1344 .collect();
1345 let repo = InMemoryRepository::from_vec(users);
1346
1347 let page = repo
1349 .paginate_by(
1350 &[WhereCondition::new("age", WhereOp::Eq, Value::I64(22))],
1351 1,
1352 2,
1353 )
1354 .unwrap();
1355 assert_eq!(page.total, 4);
1356 assert_eq!(page.items.len(), 2);
1357 assert_eq!(page.total_pages(), 2);
1358 }
1359
1360 #[test]
1363 fn test_generic_key_repo_basic() {
1364 let repo: GenericKeyRepository<User, i64> = GenericKeyRepository::new();
1365 assert!(repo.is_empty());
1366
1367 let user = User::new(1, "Alice", 30, "a@b.com");
1368 repo.save(user.clone()).unwrap();
1369 assert_eq!(repo.len(), 1);
1370
1371 let found = repo.find_by_id(&1).unwrap();
1372 assert_eq!(found, Some(user));
1373 }
1374
1375 #[test]
1376 fn test_generic_key_repo_delete() {
1377 let repo: GenericKeyRepository<User, i64> = GenericKeyRepository::from_vec(vec![
1378 User::new(1, "Alice", 30, "a@b.com"),
1379 User::new(2, "Bob", 25, "b@b.com"),
1380 ]);
1381
1382 let deleted = repo.delete(&1).unwrap();
1383 assert_eq!(deleted, 1);
1384 assert_eq!(repo.len(), 1);
1385
1386 let remaining = repo.find_all().unwrap();
1387 assert_eq!(remaining[0].id, 2);
1388 }
1389
1390 #[test]
1391 fn test_generic_key_repo_find_by() {
1392 let repo: GenericKeyRepository<User, i64> = GenericKeyRepository::from_vec(vec![
1393 User::new(1, "Alice", 30, "a@b.com"),
1394 User::new(2, "Bob", 30, "b@b.com"),
1395 User::new(3, "Carol", 25, "c@b.com"),
1396 ]);
1397
1398 let result = repo
1399 .find_by(&[WhereCondition::new("age", WhereOp::Eq, Value::I64(30))])
1400 .unwrap();
1401 assert_eq!(result.len(), 2);
1402 }
1403
1404 #[test]
1405 fn test_generic_key_repo_paginate() {
1406 let users: Vec<User> = (1..=15)
1407 .map(|i| User::new(i, &format!("User{}", i), 20, "u@b.com"))
1408 .collect();
1409 let repo: GenericKeyRepository<User, i64> = GenericKeyRepository::from_vec(users);
1410
1411 let page = repo.paginate(2, 10).unwrap();
1412 assert_eq!(page.items.len(), 5);
1413 assert_eq!(page.total, 15);
1414 assert_eq!(page.page, 2);
1415 }
1416
1417 #[test]
1418 fn test_generic_key_repo_count() {
1419 let repo: GenericKeyRepository<User, i64> = GenericKeyRepository::from_vec(vec![
1420 User::new(1, "Alice", 30, "a@b.com"),
1421 User::new(2, "Bob", 25, "b@b.com"),
1422 ]);
1423 assert_eq!(repo.count().unwrap(), 2);
1424 }
1425
1426 #[test]
1427 fn test_generic_key_repo_exists() {
1428 let repo: GenericKeyRepository<User, i64> =
1429 GenericKeyRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
1430 assert!(repo.exists(&1).unwrap());
1431 assert!(!repo.exists(&999).unwrap());
1432 }
1433
1434 #[test]
1437 fn test_simple_regex_literal() {
1438 let re = simple_regex::compile("^abc$").unwrap();
1439 assert!(re.is_match("abc"));
1440 assert!(!re.is_match("abcd"));
1441 }
1442
1443 #[test]
1444 fn test_simple_regex_wildcard() {
1445 let re = simple_regex::compile("^Ali.*$").unwrap();
1446 assert!(re.is_match("Alice"));
1447 assert!(re.is_match("Alicia"));
1448 assert!(!re.is_match("Bob"));
1449 }
1450
1451 #[test]
1452 fn test_simple_regex_no_anchors() {
1453 let re = simple_regex::compile("ab").unwrap();
1454 assert!(re.is_match("ab"));
1455 }
1456
1457 #[test]
1460 fn test_e2e_repository_workflow() {
1461 let repo = InMemoryRepository::<User>::new();
1462
1463 let users = vec![
1465 User::new(1, "Alice", 30, "alice@example.com"),
1466 User::new(2, "Bob", 25, "bob@example.com"),
1467 User::new(3, "Carol", 35, "carol@example.com"),
1468 User::new(4, "Dave", 28, "dave@example.com"),
1469 User::new(5, "Eve", 32, "eve@example.com"),
1470 ];
1471 repo.save_many(users).unwrap();
1472 assert_eq!(repo.count().unwrap(), 5);
1473
1474 let adults = repo
1476 .find_by(&[WhereCondition::new("age", WhereOp::Ge, Value::I64(30))])
1477 .unwrap();
1478 assert_eq!(adults.len(), 3);
1479
1480 let page1 = repo.paginate(1, 2).unwrap();
1482 assert_eq!(page1.items.len(), 2);
1483 assert_eq!(page1.total_pages(), 3);
1484
1485 let page2 = repo.paginate(2, 2).unwrap();
1486 assert_eq!(page2.items.len(), 2);
1487
1488 let page3 = repo.paginate(3, 2).unwrap();
1489 assert_eq!(page3.items.len(), 1);
1490
1491 let adult_page = repo
1493 .paginate_by(
1494 &[WhereCondition::new("age", WhereOp::Ge, Value::I64(30))],
1495 1,
1496 2,
1497 )
1498 .unwrap();
1499 assert_eq!(adult_page.total, 3);
1500 assert_eq!(adult_page.items.len(), 2);
1501
1502 repo.save(User::new(1, "Alice Smith", 31, "alice.smith@example.com"))
1504 .unwrap();
1505 let updated = repo.find_by_id(&Value::I64(1)).unwrap().unwrap();
1506 assert_eq!(updated.name, "Alice Smith");
1507 assert_eq!(updated.age, 31);
1508
1509 let deleted = repo.delete(&Value::I64(2)).unwrap();
1511 assert_eq!(deleted, 1);
1512 assert_eq!(repo.count().unwrap(), 4);
1513 assert!(!repo.exists(&Value::I64(2)).unwrap());
1514
1515 let deleted_by = repo
1517 .delete_by(&[WhereCondition::new("age", WhereOp::Ge, Value::I64(31))])
1518 .unwrap();
1519 assert_eq!(deleted_by, 3);
1520 assert_eq!(repo.count().unwrap(), 1); }
1522
1523 #[test]
1524 fn test_e2e_pagination_navigation() {
1525 let users: Vec<User> = (1..=100)
1526 .map(|i| User::new(i, &format!("User{}", i), 20, "u@b.com"))
1527 .collect();
1528 let repo = InMemoryRepository::from_vec(users);
1529
1530 let mut current_page = 1u64;
1531 let mut visited: Vec<u64> = Vec::new();
1532 loop {
1533 let page = repo.paginate(current_page, 10).unwrap();
1534 visited.push(current_page);
1535 if !page.has_next() {
1536 break;
1537 }
1538 current_page += 1;
1539 }
1540 assert_eq!(visited.len(), 10);
1541 assert_eq!(visited, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
1542 }
1543}