Skip to main content

sz_orm_core/
repository.rs

1//! Repository Pattern 仓储模式
2//!
3//! 对应文档 6.8 节改进项 37(Repository Pattern 仓储模式)。
4//!
5//! # 核心概念
6//!
7//! - **Repository trait**:统一的仓储接口,定义 CRUD + 分页 + 条件查询
8//! - **InMemoryRepository**:内存仓储实现(用于测试、原型、无数据库场景)
9//! - **WhereCondition / WhereOp**:查询条件
10//! - **PageResult**:分页结果
11//! - **RepositoryError**:仓储错误
12//!
13//! # 设计灵感
14//!
15//! - Doctrine `EntityRepository`
16//! - Spring Data JPA `@Repository` / `JpaRepository`
17//! - MyBatis-Plus `IService` / `BaseMapper`
18//! - Laravel Eloquent `Repository`
19//! - DDD(领域驱动设计)的 Repository 模式
20//!
21//! # 优势
22//!
23//! 1. **分层解耦**:业务层依赖 Repository 接口,不直接依赖 Model 静态方法
24//! 2. **可替换性**:同一接口可有 InMemory / SQL / NoSQL 等多种实现
25//! 3. **可测试**:单元测试用 InMemoryRepository;生产环境请直接使用 QueryBuilder + Connection
26//! 4. **统一 API**:CRUD + 分页 + 条件查询接口统一
27//!
28//! # 重要说明
29//!
30//! 本模块**仅提供 InMemoryRepository**(内存实现),**不提供 SqlRepository**。
31//! 如需基于 SQL 的仓储实现,请直接使用 `QueryBuilder` 生成 SQL 并通过
32//! `Pool::acquire()` 获取连接后执行。InMemoryRepository 主要用于单元测试与
33//! 业务逻辑原型验证,不执行任何真实 SQL。
34//!
35//! # 使用示例
36//!
37//! ```
38//! use sz_orm_core::repository::{
39//!     InMemoryRepository, Repository, WhereCondition, WhereOp, PageResult,
40//! };
41//! use sz_orm_core::Value;
42//! use std::collections::HashMap;
43//!
44//! // 假设 User 是 Model 的实现
45//! // let repo = InMemoryRepository::<User>::new();
46//! // let user = repo.find_by_id(1)?;
47//! // let adults = repo.find_by(&[WhereCondition::new("age", WhereOp::Ge, Value::I64(18))])?;
48//! ```
49
50use crate::value::Value;
51use std::fmt::Debug;
52use std::sync::RwLock;
53
54// ============================================================================
55// WhereCondition — 查询条件
56// ============================================================================
57
58/// 查询操作符
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub enum WhereOp {
61    /// `=`
62    Eq,
63    /// `!=`
64    Ne,
65    /// `>`
66    Gt,
67    /// `>=`
68    Ge,
69    /// `<`
70    Lt,
71    /// `<=`
72    Le,
73    /// `LIKE`
74    Like,
75    /// `IN`
76    In,
77    /// `NOT IN`
78    NotIn,
79    /// `IS NULL`
80    IsNull,
81    /// `IS NOT NULL`
82    IsNotNull,
83    /// `BETWEEN`
84    Between,
85}
86
87impl WhereOp {
88    /// 操作符名称
89    pub fn name(&self) -> &'static str {
90        match self {
91            WhereOp::Eq => "eq",
92            WhereOp::Ne => "ne",
93            WhereOp::Gt => "gt",
94            WhereOp::Ge => "ge",
95            WhereOp::Lt => "lt",
96            WhereOp::Le => "le",
97            WhereOp::Like => "like",
98            WhereOp::In => "in",
99            WhereOp::NotIn => "not_in",
100            WhereOp::IsNull => "is_null",
101            WhereOp::IsNotNull => "is_not_null",
102            WhereOp::Between => "between",
103        }
104    }
105}
106
107/// 查询条件
108#[derive(Debug, Clone)]
109pub struct WhereCondition {
110    pub field: String,
111    pub op: WhereOp,
112    pub value: Value,
113    /// For Between / In / NotIn,副值列表
114    pub extra_values: Vec<Value>,
115}
116
117impl WhereCondition {
118    /// 创建单值条件(Eq/Ne/Gt/Ge/Lt/Le/Like)
119    pub fn new(field: impl Into<String>, op: WhereOp, value: Value) -> Self {
120        Self {
121            field: field.into(),
122            op,
123            value,
124            extra_values: Vec::new(),
125        }
126    }
127
128    /// 创建 IsNull / IsNotNull 条件
129    pub fn null_check(field: impl Into<String>, op: WhereOp) -> Self {
130        Self {
131            field: field.into(),
132            op,
133            value: Value::Null,
134            extra_values: Vec::new(),
135        }
136    }
137
138    /// 创建 In / NotIn 条件
139    pub fn in_op(field: impl Into<String>, op: WhereOp, values: Vec<Value>) -> Self {
140        Self {
141            field: field.into(),
142            op,
143            value: Value::Null,
144            extra_values: values,
145        }
146    }
147
148    /// 创建 Between 条件
149    pub fn between(field: impl Into<String>, low: Value, high: Value) -> Self {
150        Self {
151            field: field.into(),
152            op: WhereOp::Between,
153            value: low,
154            extra_values: vec![high],
155        }
156    }
157}
158
159// ============================================================================
160// PageResult — 分页结果
161// ============================================================================
162
163/// 分页结果
164#[derive(Debug, Clone)]
165pub struct PageResult<T> {
166    pub items: Vec<T>,
167    pub total: u64,
168    pub page: u64,
169    pub page_size: u64,
170}
171
172impl<T> PageResult<T> {
173    /// 创建分页结果
174    pub fn new(items: Vec<T>, total: u64, page: u64, page_size: u64) -> Self {
175        Self {
176            items,
177            total,
178            page,
179            page_size,
180        }
181    }
182
183    /// 总页数
184    pub fn total_pages(&self) -> u64 {
185        if self.page_size == 0 {
186            return 0;
187        }
188        self.total.div_ceil(self.page_size)
189    }
190
191    /// 是否有下一页
192    pub fn has_next(&self) -> bool {
193        self.page < self.total_pages()
194    }
195
196    /// 是否有上一页
197    pub fn has_prev(&self) -> bool {
198        self.page > 1
199    }
200
201    /// 是否为空
202    pub fn is_empty(&self) -> bool {
203        self.items.is_empty()
204    }
205
206    /// 当前页条数
207    pub fn len(&self) -> usize {
208        self.items.len()
209    }
210
211    /// 映射为其他类型
212    pub fn map<U, F: Fn(T) -> U>(self, f: F) -> PageResult<U> {
213        PageResult {
214            items: self.items.into_iter().map(f).collect(),
215            total: self.total,
216            page: self.page,
217            page_size: self.page_size,
218        }
219    }
220}
221
222// ============================================================================
223// BatchUpdateResult — 批量更新结果(S-1:SeaORM 对标短板补全)
224// ============================================================================
225
226/// 批量更新结果
227///
228/// 由 [`Repository::batch_update`](Repository::batch_update) 返回,
229/// 区分成功更新的实体与因主键不存在而被跳过的实体数量。
230///
231/// # 字段
232///
233/// - `updated`:成功更新的实体列表(按主键匹配命中并完成 UPDATE)
234/// - `skipped`:主键不存在而被跳过的实体数量
235///
236/// # 设计动机
237///
238/// SeaORM 的 `update_many` 不区分"存在"与"不存在",只返回受影响行数。
239/// 本结构明确区分两者,便于调用方感知部分失败(如批量同步外部数据时
240/// 部分记录已被删除)并采取补偿措施(记录日志、重试插入等)。
241#[derive(Debug, Clone)]
242pub struct BatchUpdateResult<E> {
243    /// 成功更新的实体列表
244    pub updated: Vec<E>,
245    /// 主键不存在而被跳过的实体数量
246    pub skipped: usize,
247}
248
249impl<E> BatchUpdateResult<E> {
250    /// 创建批量更新结果
251    pub fn new(updated: Vec<E>, skipped: usize) -> Self {
252        Self { updated, skipped }
253    }
254
255    /// 成功更新数量
256    pub fn updated_count(&self) -> usize {
257        self.updated.len()
258    }
259
260    /// 是否有跳过的实体
261    pub fn has_skipped(&self) -> bool {
262        self.skipped > 0
263    }
264
265    /// 是否全部成功(无跳过)
266    pub fn all_updated(&self) -> bool {
267        self.skipped == 0
268    }
269
270    /// 总计处理数量(已更新 + 跳过)
271    pub fn total(&self) -> usize {
272        self.updated.len() + self.skipped
273    }
274
275    /// 映射已更新实体为其他类型(保留 skipped 计数)
276    pub fn map<U, F: Fn(E) -> U>(self, f: F) -> BatchUpdateResult<U> {
277        BatchUpdateResult {
278            updated: self.updated.into_iter().map(f).collect(),
279            skipped: self.skipped,
280        }
281    }
282}
283
284impl<E> Default for BatchUpdateResult<E> {
285    fn default() -> Self {
286        Self {
287            updated: Vec::new(),
288            skipped: 0,
289        }
290    }
291}
292
293// ============================================================================
294// RepositoryError — 错误类型
295// ============================================================================
296
297/// Repository 错误
298#[derive(Debug, Clone, PartialEq)]
299pub enum RepositoryError {
300    /// 实体未找到
301    NotFound,
302    /// 数据库错误
303    DatabaseError(String),
304    /// 实体无效(如缺主键)
305    InvalidEntity(String),
306    /// 其他错误
307    Other(String),
308}
309
310impl std::fmt::Display for RepositoryError {
311    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
312        match self {
313            RepositoryError::NotFound => write!(f, "entity not found"),
314            RepositoryError::DatabaseError(msg) => write!(f, "database error: {}", msg),
315            RepositoryError::InvalidEntity(msg) => write!(f, "invalid entity: {}", msg),
316            RepositoryError::Other(msg) => write!(f, "repository error: {}", msg),
317        }
318    }
319}
320
321impl std::error::Error for RepositoryError {}
322
323/// Repository 结果类型
324pub type RepositoryResult<T> = Result<T, RepositoryError>;
325
326// ============================================================================
327// Repository trait — 仓储接口
328// ============================================================================
329
330/// 仓储接口(generic over Model-like entity E, primary key K)
331///
332/// E 不必是 `Model` trait 实现,仅需满足存储基本要求。
333/// 这样设计允许存储任意结构(DTO、聚合、领域实体等)。
334pub trait Repository<E>: Send + Sync {
335    /// 主键类型
336    type Key: Clone + Debug + PartialEq + Send + Sync;
337
338    /// 提取实体的主键
339    fn key_of(&self, entity: &E) -> Self::Key;
340
341    /// 按主键查找
342    fn find_by_id(&self, key: &Self::Key) -> RepositoryResult<Option<E>>;
343
344    /// 查询所有
345    fn find_all(&self) -> RepositoryResult<Vec<E>>;
346
347    /// 按条件查询
348    fn find_by(&self, conditions: &[WhereCondition]) -> RepositoryResult<Vec<E>>;
349
350    /// 按条件查询单条
351    fn find_one_by(&self, conditions: &[WhereCondition]) -> RepositoryResult<Option<E>> {
352        let mut items = self.find_by(conditions)?;
353        if items.is_empty() {
354            Ok(None)
355        } else {
356            Ok(Some(items.remove(0)))
357        }
358    }
359
360    /// 保存(INSERT 或 UPDATE)
361    ///
362    /// 返回保存后的实体(可能是克隆,主键可能被填充)
363    fn save(&self, entity: E) -> RepositoryResult<E>;
364
365    /// 批量保存
366    fn save_many(&self, entities: Vec<E>) -> RepositoryResult<Vec<E>> {
367        let mut saved = Vec::with_capacity(entities.len());
368        for e in entities {
369            saved.push(self.save(e)?);
370        }
371        Ok(saved)
372    }
373
374    /// 批量更新(S-1:SeaORM 对标短板补全)
375    ///
376    /// 仅更新已存在的实体(按主键匹配),不存在的实体跳过并计入 `skipped`。
377    /// 与 [`save_many`](Self::save_many) 的区别:`save_many` 是 upsert(存在则更新,不存在则插入),
378    /// `batch_update` 是纯更新(不存在则跳过)。
379    ///
380    /// 默认实现为逐条调用 [`find_by_id`](Self::find_by_id) + [`save`](Self::save),
381    /// 具体实现可重写为真正的批量 UPDATE SQL(如 `UPDATE ... SET ... WHERE id IN (...)`)。
382    ///
383    /// # 参数
384    ///
385    /// - `entities`:待更新的实体列表(主键必须已填充)
386    ///
387    /// # 返回
388    ///
389    /// - `updated`:成功更新的实体列表
390    /// - `skipped`:主键不存在而被跳过的实体数量
391    fn batch_update(&self, entities: Vec<E>) -> RepositoryResult<BatchUpdateResult<E>> {
392        let mut updated = Vec::with_capacity(entities.len());
393        let mut skipped = 0usize;
394        for e in entities {
395            let key = self.key_of(&e);
396            if self.find_by_id(&key)?.is_some() {
397                updated.push(self.save(e)?);
398            } else {
399                skipped += 1;
400            }
401        }
402        Ok(BatchUpdateResult { updated, skipped })
403    }
404
405    /// 按主键删除
406    fn delete(&self, key: &Self::Key) -> RepositoryResult<usize>;
407
408    /// 按条件删除
409    fn delete_by(&self, conditions: &[WhereCondition]) -> RepositoryResult<usize> {
410        let items = self.find_by(conditions)?;
411        let mut count = 0;
412        for item in items {
413            let key = self.key_of(&item);
414            count += self.delete(&key)?;
415        }
416        Ok(count)
417    }
418
419    /// 总数
420    fn count(&self) -> RepositoryResult<u64>;
421
422    /// 按条件计数
423    fn count_by(&self, conditions: &[WhereCondition]) -> RepositoryResult<u64> {
424        let items = self.find_by(conditions)?;
425        Ok(items.len() as u64)
426    }
427
428    /// 主键是否存在
429    fn exists(&self, key: &Self::Key) -> RepositoryResult<bool> {
430        Ok(self.find_by_id(key)?.is_some())
431    }
432
433    /// 分页查询
434    fn paginate(&self, page: u64, page_size: u64) -> RepositoryResult<PageResult<E>>
435    where
436        E: Clone,
437    {
438        let all = self.find_all()?;
439        let total = all.len() as u64;
440        let start = ((page.saturating_sub(1)) * page_size) as usize;
441        let end = (start + page_size as usize).min(all.len());
442
443        let items = if start < all.len() {
444            all[start..end].to_vec()
445        } else {
446            Vec::new()
447        };
448
449        Ok(PageResult::new(items, total, page, page_size))
450    }
451
452    /// 按条件分页查询
453    fn paginate_by(
454        &self,
455        conditions: &[WhereCondition],
456        page: u64,
457        page_size: u64,
458    ) -> RepositoryResult<PageResult<E>>
459    where
460        E: Clone,
461    {
462        let all = self.find_by(conditions)?;
463        let total = all.len() as u64;
464        let start = ((page.saturating_sub(1)) * page_size) as usize;
465        let end = (start + page_size as usize).min(all.len());
466
467        let items = if start < all.len() {
468            all[start..end].to_vec()
469        } else {
470            Vec::new()
471        };
472
473        Ok(PageResult::new(items, total, page, page_size))
474    }
475
476    /// 按 AND 条件查询,并对结果应用额外的 OR 过滤组
477    ///
478    /// 语义:`WHERE (and1 AND and2 AND ...) AND (or1_1 OR or1_2 OR ...)`
479    ///
480    /// 用于多字段 keyword LIKE 搜索等场景(对齐 PHP ThinkPHP
481    /// `where('field1|field2|field3','like','%kw%')` 多字段 OR LIKE 语法)。
482    ///
483    /// # 默认实现
484    ///
485    /// 基于 `find_by` + 内存 OR 过滤;SQL 后端实现可重写以将 OR 下推到 SQL,
486    /// 避免 `SELECT ... WHERE app_id=?` 全表拉取后再内存过滤的性能问题。
487    ///
488    /// # 参数
489    ///
490    /// - `and`:AND 关系的条件列表(同 `find_by`)
491    /// - `or_filter`:OR 关系的条件列表,对 `and` 结果做二次过滤
492    ///
493    /// # 示例
494    ///
495    /// ```
496    /// use sz_orm_core::repository::{Repository, WhereCondition, WhereOp};
497    /// use sz_orm_core::Value;
498    /// # use sz_orm_core::repository::InMemoryRepository;
499    /// # fn example<E: sz_orm_core::repository::EntityAttributes + Clone + Send + Sync + 'static>(repo: &dyn Repository<E, Key = Value>) {
500    /// let and = vec![
501    ///     WhereCondition::new("is_delete", WhereOp::Eq, Value::I64(0)),
502    ///     WhereCondition::new("app_id", WhereOp::Eq, Value::I64(1)),
503    /// ];
504    /// let or = vec![
505    ///     WhereCondition::new("name", WhereOp::Like, Value::String("%kw%".into())),
506    ///     WhereCondition::new("addr", WhereOp::Like, Value::String("%kw%".into())),
507    /// ];
508    /// let items = repo.find_by_with_or_filter(&and, &or).unwrap();
509    /// # }
510    /// ```
511    fn find_by_with_or_filter(
512        &self,
513        and: &[WhereCondition],
514        or_filter: &[WhereCondition],
515    ) -> RepositoryResult<Vec<E>>
516    where
517        E: Clone + EntityAttributes,
518    {
519        let items = self.find_by(and)?;
520        if or_filter.is_empty() {
521            return Ok(items);
522        }
523        Ok(items
524            .into_iter()
525            .filter(|e| {
526                or_filter.iter().any(|c| {
527                    let attr = e.get_attribute(&c.field);
528                    match (attr, c.op) {
529                        (None, WhereOp::IsNull) => true,
530                        (None, _) => false,
531                        (Some(v), _) => value_matches(&v, c.op, &c.value, &c.extra_values),
532                    }
533                })
534            })
535            .collect())
536    }
537}
538
539// ============================================================================
540// InMemoryRepository — 内存仓储实现
541// ============================================================================
542
543/// 内存仓储实现
544///
545/// 使用 `Vec<E>` 存储实体,主键通过 `key_of` 提取。
546/// 适合单元测试、原型开发、无数据库场景。
547pub struct InMemoryRepository<E: Clone + Send + Sync + 'static> {
548    storage: RwLock<Vec<E>>,
549}
550
551impl<E: Clone + Send + Sync + 'static> InMemoryRepository<E> {
552    /// 创建空仓储
553    pub fn new() -> Self {
554        Self {
555            storage: RwLock::new(Vec::new()),
556        }
557    }
558
559    /// 从已有集合创建
560    pub fn from_vec(items: Vec<E>) -> Self {
561        Self {
562            storage: RwLock::new(items),
563        }
564    }
565
566    /// 当前存储条数
567    pub fn len(&self) -> usize {
568        match self.storage.read() {
569            Ok(storage) => storage.len(),
570            Err(_) => 0,
571        }
572    }
573
574    /// 是否为空
575    pub fn is_empty(&self) -> bool {
576        self.len() == 0
577    }
578
579    /// 清空
580    pub fn clear(&self) {
581        if let Ok(mut storage) = self.storage.write() {
582            storage.clear();
583        }
584    }
585}
586
587impl<E: Clone + Send + Sync + 'static> Default for InMemoryRepository<E> {
588    fn default() -> Self {
589        Self::new()
590    }
591}
592
593/// 值比较辅助函数(支持基本类型 + Value 比较)
594fn value_matches(value: &Value, op: WhereOp, target: &Value, extras: &[Value]) -> bool {
595    use Value::*;
596    match op {
597        WhereOp::Eq => value == target,
598        WhereOp::Ne => value != target,
599        WhereOp::Gt => match (value, target) {
600            (I64(a), I64(b)) => a > b,
601            (F64(a), F64(b)) => a > b,
602            (F64(a), I64(b)) => a > &(*b as f64),
603            (I64(a), F64(b)) => a > &(*b as i64),
604            (String(a), String(b)) => a > b,
605            _ => false,
606        },
607        WhereOp::Ge => match (value, target) {
608            (I64(a), I64(b)) => a >= b,
609            (F64(a), F64(b)) => a >= b,
610            (String(a), String(b)) => a >= b,
611            _ => false,
612        },
613        WhereOp::Lt => match (value, target) {
614            (I64(a), I64(b)) => a < b,
615            (F64(a), F64(b)) => a < b,
616            (String(a), String(b)) => a < b,
617            _ => false,
618        },
619        WhereOp::Le => match (value, target) {
620            (I64(a), I64(b)) => a <= b,
621            (F64(a), F64(b)) => a <= b,
622            (String(a), String(b)) => a <= b,
623            _ => false,
624        },
625        WhereOp::Like => match (value, target) {
626            (String(a), String(b)) => {
627                // 简化 LIKE:将 % 转换为 .*,其他字符转义
628                // 大小写不敏感(对齐 MySQL utf8mb4_general_ci / utf8mb4_unicode_ci 默认 collation)
629                let pattern = b.to_lowercase().replace('%', ".*").replace('_', ".");
630                let full_pattern = format!("^{}$", pattern);
631                if let Ok(re) = simple_regex::compile(&full_pattern) {
632                    re.is_match(&a.to_lowercase())
633                } else {
634                    false
635                }
636            }
637            _ => false,
638        },
639        WhereOp::In => extras.iter().any(|v| v == value),
640        WhereOp::NotIn => !extras.iter().any(|v| v == value),
641        WhereOp::IsNull => matches!(value, Null),
642        WhereOp::IsNotNull => !matches!(value, Null),
643        WhereOp::Between => {
644            if extras.is_empty() {
645                return false;
646            }
647            let low = target;
648            let high = &extras[0];
649            // value >= low AND value <= high
650            value_matches(value, WhereOp::Ge, low, &[])
651                && value_matches(value, WhereOp::Le, high, &[])
652        }
653    }
654}
655
656/// 实体属性提取 trait(用户为实体实现此 trait 以支持 find_by)
657pub trait EntityAttributes: Send + Sync {
658    /// 按字段名获取属性值
659    fn get_attribute(&self, field: &str) -> Option<Value>;
660}
661
662/// InMemoryRepository 的 EntityAttributes-based 实现
663impl<E: Clone + Send + Sync + 'static + EntityAttributes> Repository<E> for InMemoryRepository<E> {
664    type Key = Value;
665
666    fn key_of(&self, entity: &E) -> Self::Key {
667        entity.get_attribute("id").unwrap_or(Value::Null)
668    }
669
670    fn find_by_id(&self, key: &Self::Key) -> RepositoryResult<Option<E>> {
671        let storage = match self.storage.read() {
672            Ok(g) => g,
673            Err(_) => return Ok(None),
674        };
675        Ok(storage.iter().find(|e| self.key_of(e) == *key).cloned())
676    }
677
678    fn find_all(&self) -> RepositoryResult<Vec<E>> {
679        let storage = match self.storage.read() {
680            Ok(g) => g,
681            Err(_) => return Ok(Vec::new()),
682        };
683        Ok(storage.clone())
684    }
685
686    fn find_by(&self, conditions: &[WhereCondition]) -> RepositoryResult<Vec<E>> {
687        let storage = match self.storage.read() {
688            Ok(g) => g,
689            Err(_) => return Ok(Vec::new()),
690        };
691        let result: Vec<E> = storage
692            .iter()
693            .filter(|e| {
694                conditions.iter().all(|c| {
695                    let attr = e.get_attribute(&c.field);
696                    match (attr, c.op) {
697                        (None, WhereOp::IsNull) => true,
698                        (None, _) => false,
699                        (Some(v), _) => value_matches(&v, c.op, &c.value, &c.extra_values),
700                    }
701                })
702            })
703            .cloned()
704            .collect();
705        Ok(result)
706    }
707
708    fn save(&self, mut entity: E) -> RepositoryResult<E> {
709        let mut storage = match self.storage.write() {
710            Ok(g) => g,
711            Err(_) => return Ok(entity),
712        };
713        let key = self.key_of(&entity);
714
715        // 查找是否已存在
716        let existing_idx = storage.iter().position(|e| self.key_of(e) == key);
717
718        match existing_idx {
719            Some(idx) => {
720                storage[idx] = entity.clone();
721            }
722            None => {
723                storage.push(entity.clone());
724            }
725        }
726        // 注意:entity 可能被修改(如自增主键),这里返回原值
727        let _ = &mut entity; // 标记 mut 以符合签名
728        Ok(entity)
729    }
730
731    fn delete(&self, key: &Self::Key) -> RepositoryResult<usize> {
732        let mut storage = match self.storage.write() {
733            Ok(g) => g,
734            Err(_) => return Ok(0),
735        };
736        let before = storage.len();
737        storage.retain(|e| self.key_of(e) != *key);
738        Ok(before - storage.len())
739    }
740
741    fn count(&self) -> RepositoryResult<u64> {
742        Ok(self.len() as u64)
743    }
744}
745
746// ============================================================================
747// simple_regex — 极简 LIKE 模式匹配(避免引入正则依赖)
748// ============================================================================
749
750mod simple_regex {
751    /// 极简正则编译器:仅支持 `.*`、`^`、`$`、字面字符
752    pub struct Regex {
753        patterns: Vec<Pattern>,
754    }
755
756    enum Pattern {
757        AnyChars,      // .*(贪婪匹配任意字符)
758        Literal(char), // 字面字符
759        Start,         // ^
760        End,           // $
761    }
762
763    pub fn compile(pattern: &str) -> Result<Regex, String> {
764        let mut patterns = Vec::new();
765        let chars: Vec<char> = pattern.chars().collect();
766        let mut i = 0;
767        while i < chars.len() {
768            match chars[i] {
769                '^' => {
770                    patterns.push(Pattern::Start);
771                    i += 1;
772                }
773                '$' => {
774                    patterns.push(Pattern::End);
775                    i += 1;
776                }
777                '.' if i + 1 < chars.len() && chars[i + 1] == '*' => {
778                    patterns.push(Pattern::AnyChars);
779                    i += 2;
780                }
781                c => {
782                    patterns.push(Pattern::Literal(c));
783                    i += 1;
784                }
785            }
786        }
787        Ok(Regex { patterns })
788    }
789
790    impl Regex {
791        pub fn is_match(&self, text: &str) -> bool {
792            self.match_from(text, 0, 0)
793        }
794
795        fn match_from(&self, text: &str, text_idx: usize, pat_idx: usize) -> bool {
796            let chars: Vec<char> = text.chars().collect();
797            if pat_idx >= self.patterns.len() {
798                return text_idx == chars.len();
799            }
800            match &self.patterns[pat_idx] {
801                Pattern::Start => self.match_from(text, 0, pat_idx + 1),
802                Pattern::End => text_idx == chars.len(),
803                Pattern::Literal(c) => {
804                    if text_idx < chars.len() && chars[text_idx] == *c {
805                        self.match_from(text, text_idx + 1, pat_idx + 1)
806                    } else {
807                        false
808                    }
809                }
810                Pattern::AnyChars => {
811                    // 尝试匹配 0 到 len 个字符
812                    for skip in 0..=(chars.len() - text_idx) {
813                        if self.match_from(text, text_idx + skip, pat_idx + 1) {
814                            return true;
815                        }
816                    }
817                    false
818                }
819            }
820        }
821    }
822}
823
824// ============================================================================
825// GenericKeyRepository — 支持任意 Key 类型的内存仓储
826// ============================================================================
827
828/// 通用 Key 提取 trait(用户为实体实现此 trait 以支持任意 Key 类型)
829pub trait EntityKey<K>: Send + Sync {
830    /// 提取主键
831    fn key(&self) -> K;
832}
833
834/// 通用 Key 内存仓储
835pub struct GenericKeyRepository<E, K>
836where
837    E: Clone + Send + Sync + 'static,
838    K: Clone + Debug + PartialEq + Send + Sync + 'static,
839{
840    storage: RwLock<Vec<E>>,
841    _phantom: std::marker::PhantomData<K>,
842}
843
844impl<E, K> GenericKeyRepository<E, K>
845where
846    E: Clone + Send + Sync + 'static,
847    K: Clone + Debug + PartialEq + Send + Sync + 'static,
848{
849    pub fn new() -> Self {
850        Self {
851            storage: RwLock::new(Vec::new()),
852            _phantom: std::marker::PhantomData,
853        }
854    }
855
856    pub fn from_vec(items: Vec<E>) -> Self {
857        Self {
858            storage: RwLock::new(items),
859            _phantom: std::marker::PhantomData,
860        }
861    }
862
863    pub fn len(&self) -> usize {
864        match self.storage.read() {
865            Ok(g) => g.len(),
866            Err(_) => 0,
867        }
868    }
869
870    pub fn is_empty(&self) -> bool {
871        self.len() == 0
872    }
873
874    pub fn clear(&self) {
875        if let Ok(mut storage) = self.storage.write() {
876            storage.clear();
877        }
878    }
879}
880
881impl<E, K> Default for GenericKeyRepository<E, K>
882where
883    E: Clone + Send + Sync + 'static,
884    K: Clone + Debug + PartialEq + Send + Sync + 'static,
885{
886    fn default() -> Self {
887        Self::new()
888    }
889}
890
891impl<E, K> Repository<E> for GenericKeyRepository<E, K>
892where
893    E: Clone + Send + Sync + 'static + EntityKey<K> + EntityAttributes,
894    K: Clone + Debug + PartialEq + Send + Sync + 'static,
895{
896    type Key = K;
897
898    fn key_of(&self, entity: &E) -> Self::Key {
899        entity.key()
900    }
901
902    fn find_by_id(&self, key: &Self::Key) -> RepositoryResult<Option<E>> {
903        let storage = match self.storage.read() {
904            Ok(g) => g,
905            Err(_) => return Ok(None),
906        };
907        Ok(storage.iter().find(|e| &e.key() == key).cloned())
908    }
909
910    fn find_all(&self) -> RepositoryResult<Vec<E>> {
911        let storage = match self.storage.read() {
912            Ok(g) => g,
913            Err(_) => return Ok(Vec::new()),
914        };
915        Ok(storage.clone())
916    }
917
918    fn find_by(&self, conditions: &[WhereCondition]) -> RepositoryResult<Vec<E>> {
919        let storage = match self.storage.read() {
920            Ok(g) => g,
921            Err(_) => return Ok(Vec::new()),
922        };
923        let result: Vec<E> = storage
924            .iter()
925            .filter(|e| {
926                conditions.iter().all(|c| {
927                    let attr = e.get_attribute(&c.field);
928                    match (attr, c.op) {
929                        (None, WhereOp::IsNull) => true,
930                        (None, _) => false,
931                        (Some(v), _) => value_matches(&v, c.op, &c.value, &c.extra_values),
932                    }
933                })
934            })
935            .cloned()
936            .collect();
937        Ok(result)
938    }
939
940    fn save(&self, entity: E) -> RepositoryResult<E> {
941        let mut storage = match self.storage.write() {
942            Ok(g) => g,
943            Err(_) => return Ok(entity),
944        };
945        let key = entity.key();
946        let existing_idx = storage.iter().position(|e| e.key() == key);
947        match existing_idx {
948            Some(idx) => {
949                storage[idx] = entity.clone();
950            }
951            None => {
952                storage.push(entity.clone());
953            }
954        }
955        Ok(entity)
956    }
957
958    fn delete(&self, key: &Self::Key) -> RepositoryResult<usize> {
959        let mut storage = match self.storage.write() {
960            Ok(g) => g,
961            Err(_) => return Ok(0),
962        };
963        let before = storage.len();
964        storage.retain(|e| &e.key() != key);
965        Ok(before - storage.len())
966    }
967
968    fn count(&self) -> RepositoryResult<u64> {
969        Ok(self.len() as u64)
970    }
971}
972
973// ============================================================================
974// 单元测试
975// ============================================================================
976
977#[cfg(test)]
978mod tests {
979    use super::*;
980
981    // ===== 测试用实体 =====
982
983    #[derive(Debug, Clone, PartialEq)]
984    struct User {
985        id: i64,
986        name: String,
987        age: i64,
988        email: String,
989    }
990
991    impl User {
992        fn new(id: i64, name: &str, age: i64, email: &str) -> Self {
993            Self {
994                id,
995                name: name.to_string(),
996                age,
997                email: email.to_string(),
998            }
999        }
1000    }
1001
1002    impl EntityAttributes for User {
1003        fn get_attribute(&self, field: &str) -> Option<Value> {
1004            match field {
1005                "id" => Some(Value::I64(self.id)),
1006                "name" => Some(Value::String(self.name.clone())),
1007                "age" => Some(Value::I64(self.age)),
1008                "email" => Some(Value::String(self.email.clone())),
1009                _ => None,
1010            }
1011        }
1012    }
1013
1014    impl EntityKey<i64> for User {
1015        fn key(&self) -> i64 {
1016            self.id
1017        }
1018    }
1019
1020    // ===== WhereOp / WhereCondition =====
1021
1022    #[test]
1023    fn test_where_op_name() {
1024        assert_eq!(WhereOp::Eq.name(), "eq");
1025        assert_eq!(WhereOp::Ne.name(), "ne");
1026        assert_eq!(WhereOp::Gt.name(), "gt");
1027        assert_eq!(WhereOp::Like.name(), "like");
1028        assert_eq!(WhereOp::In.name(), "in");
1029        assert_eq!(WhereOp::IsNull.name(), "is_null");
1030        assert_eq!(WhereOp::Between.name(), "between");
1031    }
1032
1033    #[test]
1034    fn test_where_condition_new() {
1035        let c = WhereCondition::new("age", WhereOp::Ge, Value::I64(18));
1036        assert_eq!(c.field, "age");
1037        assert_eq!(c.op, WhereOp::Ge);
1038        assert_eq!(c.value, Value::I64(18));
1039        assert!(c.extra_values.is_empty());
1040    }
1041
1042    #[test]
1043    fn test_where_condition_null_check() {
1044        let c = WhereCondition::null_check("deleted_at", WhereOp::IsNull);
1045        assert_eq!(c.field, "deleted_at");
1046        assert_eq!(c.op, WhereOp::IsNull);
1047        assert_eq!(c.value, Value::Null);
1048    }
1049
1050    #[test]
1051    fn test_where_condition_in() {
1052        let c = WhereCondition::in_op(
1053            "id",
1054            WhereOp::In,
1055            vec![Value::I64(1), Value::I64(2), Value::I64(3)],
1056        );
1057        assert_eq!(c.field, "id");
1058        assert_eq!(c.op, WhereOp::In);
1059        assert_eq!(c.extra_values.len(), 3);
1060    }
1061
1062    #[test]
1063    fn test_where_condition_between() {
1064        let c = WhereCondition::between("age", Value::I64(18), Value::I64(30));
1065        assert_eq!(c.field, "age");
1066        assert_eq!(c.op, WhereOp::Between);
1067        assert_eq!(c.value, Value::I64(18));
1068        assert_eq!(c.extra_values, vec![Value::I64(30)]);
1069    }
1070
1071    // ===== PageResult =====
1072
1073    #[test]
1074    fn test_page_result_total_pages() {
1075        let pr = PageResult::new(vec![1, 2, 3], 100, 1, 10);
1076        assert_eq!(pr.total_pages(), 10);
1077    }
1078
1079    #[test]
1080    fn test_page_result_total_pages_with_remainder() {
1081        let pr: PageResult<i32> = PageResult::new(vec![], 105, 1, 10);
1082        assert_eq!(pr.total_pages(), 11);
1083    }
1084
1085    #[test]
1086    fn test_page_result_total_pages_zero_size() {
1087        let pr: PageResult<i32> = PageResult::new(vec![], 100, 1, 0);
1088        assert_eq!(pr.total_pages(), 0);
1089    }
1090
1091    #[test]
1092    fn test_page_result_has_next() {
1093        let pr = PageResult::new(vec![1, 2, 3], 100, 1, 10);
1094        assert!(pr.has_next());
1095        assert!(!pr.has_prev());
1096    }
1097
1098    #[test]
1099    fn test_page_result_has_prev() {
1100        let pr = PageResult::new(vec![1, 2, 3], 100, 5, 10);
1101        assert!(pr.has_prev());
1102        assert!(pr.has_next()); // page 5 < total_pages 10
1103    }
1104
1105    #[test]
1106    fn test_page_result_is_empty() {
1107        let pr: PageResult<i32> = PageResult::new(vec![], 0, 1, 10);
1108        assert!(pr.is_empty());
1109        assert_eq!(pr.len(), 0);
1110    }
1111
1112    #[test]
1113    fn test_page_result_map() {
1114        let pr = PageResult::new(vec![1, 2, 3], 100, 1, 10);
1115        let mapped = pr.map(|x| x * 2);
1116        assert_eq!(mapped.items, vec![2, 4, 6]);
1117        assert_eq!(mapped.total, 100);
1118    }
1119
1120    // ===== RepositoryError =====
1121
1122    #[test]
1123    fn test_repository_error_display() {
1124        let e = RepositoryError::NotFound;
1125        assert_eq!(e.to_string(), "entity not found");
1126
1127        let e = RepositoryError::DatabaseError("conn refused".to_string());
1128        assert_eq!(e.to_string(), "database error: conn refused");
1129
1130        let e = RepositoryError::InvalidEntity("missing id".to_string());
1131        assert_eq!(e.to_string(), "invalid entity: missing id");
1132
1133        let e = RepositoryError::Other("custom".to_string());
1134        assert_eq!(e.to_string(), "repository error: custom");
1135    }
1136
1137    #[test]
1138    fn test_repository_error_eq() {
1139        assert_eq!(RepositoryError::NotFound, RepositoryError::NotFound);
1140        assert_ne!(
1141            RepositoryError::NotFound,
1142            RepositoryError::Other("x".to_string())
1143        );
1144    }
1145
1146    // ===== InMemoryRepository 基础 =====
1147
1148    #[test]
1149    fn test_inmemory_create_empty() {
1150        let repo = InMemoryRepository::<User>::new();
1151        assert!(repo.is_empty());
1152        assert_eq!(repo.len(), 0);
1153    }
1154
1155    #[test]
1156    fn test_inmemory_from_vec() {
1157        let repo = InMemoryRepository::from_vec(vec![
1158            User::new(1, "Alice", 30, "alice@example.com"),
1159            User::new(2, "Bob", 25, "bob@example.com"),
1160        ]);
1161        assert_eq!(repo.len(), 2);
1162    }
1163
1164    #[test]
1165    fn test_inmemory_clear() {
1166        let repo = InMemoryRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
1167        assert_eq!(repo.len(), 1);
1168        repo.clear();
1169        assert_eq!(repo.len(), 0);
1170    }
1171
1172    // ===== Repository trait CRUD =====
1173
1174    #[test]
1175    fn test_repo_save_and_find_by_id() {
1176        let repo = InMemoryRepository::<User>::new();
1177        let user = User::new(1, "Alice", 30, "alice@example.com");
1178        let saved = repo.save(user.clone()).unwrap();
1179        assert_eq!(saved, user);
1180
1181        let found = repo.find_by_id(&Value::I64(1)).unwrap();
1182        assert_eq!(found, Some(user));
1183    }
1184
1185    #[test]
1186    fn test_repo_find_by_id_missing() {
1187        let repo = InMemoryRepository::<User>::new();
1188        let found = repo.find_by_id(&Value::I64(999)).unwrap();
1189        assert_eq!(found, None);
1190    }
1191
1192    #[test]
1193    fn test_repo_save_many() {
1194        let repo = InMemoryRepository::<User>::new();
1195        let users = vec![
1196            User::new(1, "Alice", 30, "alice@example.com"),
1197            User::new(2, "Bob", 25, "bob@example.com"),
1198            User::new(3, "Carol", 28, "carol@example.com"),
1199        ];
1200        let saved = repo.save_many(users.clone()).unwrap();
1201        assert_eq!(saved.len(), 3);
1202        assert_eq!(repo.len(), 3);
1203    }
1204
1205    // ===== S-1: batch_update 批量更新 =====
1206
1207    #[test]
1208    fn test_batch_update_result_new() {
1209        let result = BatchUpdateResult::new(vec![1, 2, 3], 2);
1210        assert_eq!(result.updated_count(), 3);
1211        assert_eq!(result.skipped, 2);
1212        assert_eq!(result.total(), 5);
1213        assert!(result.has_skipped());
1214        assert!(!result.all_updated());
1215    }
1216
1217    #[test]
1218    fn test_batch_update_result_all_updated() {
1219        let result: BatchUpdateResult<i32> = BatchUpdateResult::new(vec![1, 2], 0);
1220        assert!(!result.has_skipped());
1221        assert!(result.all_updated());
1222        assert_eq!(result.total(), 2);
1223    }
1224
1225    #[test]
1226    fn test_batch_update_result_default() {
1227        let result: BatchUpdateResult<i32> = BatchUpdateResult::default();
1228        assert_eq!(result.updated_count(), 0);
1229        assert_eq!(result.skipped, 0);
1230        assert_eq!(result.total(), 0);
1231    }
1232
1233    #[test]
1234    fn test_batch_update_result_map() {
1235        let result = BatchUpdateResult::new(vec![1, 2, 3], 1);
1236        let mapped = result.map(|x| x * 10);
1237        assert_eq!(mapped.updated, vec![10, 20, 30]);
1238        assert_eq!(mapped.skipped, 1);
1239    }
1240
1241    #[test]
1242    fn test_repo_batch_update_all_existing() {
1243        // 所有实体都存在:全部更新,无跳过
1244        let repo = InMemoryRepository::from_vec(vec![
1245            User::new(1, "Alice", 30, "a@b.com"),
1246            User::new(2, "Bob", 25, "b@b.com"),
1247        ]);
1248        let updates = vec![
1249            User::new(1, "Alice Updated", 31, "a2@b.com"),
1250            User::new(2, "Bob Updated", 26, "b2@b.com"),
1251        ];
1252        let result = repo.batch_update(updates).unwrap();
1253        assert_eq!(result.updated_count(), 2);
1254        assert_eq!(result.skipped, 0);
1255        assert!(result.all_updated());
1256
1257        // 验证实际更新生效
1258        let alice = repo.find_by_id(&Value::I64(1)).unwrap().unwrap();
1259        assert_eq!(alice.name, "Alice Updated");
1260        assert_eq!(alice.age, 31);
1261        let bob = repo.find_by_id(&Value::I64(2)).unwrap().unwrap();
1262        assert_eq!(bob.name, "Bob Updated");
1263        assert_eq!(bob.age, 26);
1264        // 总数不变(不是插入)
1265        assert_eq!(repo.len(), 2);
1266    }
1267
1268    #[test]
1269    fn test_repo_batch_update_partial_missing() {
1270        // 部分实体不存在:仅更新存在的,跳过不存在的
1271        let repo = InMemoryRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
1272        let updates = vec![
1273            User::new(1, "Alice Updated", 31, "a2@b.com"),
1274            User::new(999, "Ghost", 1, "ghost@b.com"), // 不存在
1275        ];
1276        let result = repo.batch_update(updates).unwrap();
1277        assert_eq!(result.updated_count(), 1);
1278        assert_eq!(result.skipped, 1);
1279        assert!(result.has_skipped());
1280
1281        // 验证存在的实体被更新
1282        let alice = repo.find_by_id(&Value::I64(1)).unwrap().unwrap();
1283        assert_eq!(alice.name, "Alice Updated");
1284        // 不存在的实体不会被插入
1285        assert!(repo.find_by_id(&Value::I64(999)).unwrap().is_none());
1286        assert_eq!(repo.len(), 1);
1287    }
1288
1289    #[test]
1290    fn test_repo_batch_update_all_missing() {
1291        // 所有实体都不存在:全部跳过
1292        let repo = InMemoryRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
1293        let updates = vec![
1294            User::new(100, "Ghost1", 1, "g1@b.com"),
1295            User::new(200, "Ghost2", 2, "g2@b.com"),
1296        ];
1297        let result = repo.batch_update(updates).unwrap();
1298        assert_eq!(result.updated_count(), 0);
1299        assert_eq!(result.skipped, 2);
1300        assert_eq!(repo.len(), 1); // 原数据不变
1301    }
1302
1303    #[test]
1304    fn test_repo_batch_update_empty() {
1305        let repo = InMemoryRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
1306        let result = repo.batch_update(vec![]).unwrap();
1307        assert_eq!(result.updated_count(), 0);
1308        assert_eq!(result.skipped, 0);
1309        assert_eq!(result.total(), 0);
1310    }
1311
1312    #[test]
1313    fn test_repo_batch_update_distinct_from_save_many() {
1314        // 验证 batch_update 与 save_many 语义不同:
1315        // - save_many 是 upsert(不存在的会插入)
1316        // - batch_update 是纯更新(不存在的会跳过)
1317        let repo1 = InMemoryRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
1318        let repo2 = InMemoryRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
1319
1320        let updates = vec![
1321            User::new(1, "Alice Updated", 31, "a2@b.com"),
1322            User::new(999, "New User", 1, "new@b.com"),
1323        ];
1324
1325        // save_many:两条都保存(id=999 会插入)
1326        let saved = repo1.save_many(updates.clone()).unwrap();
1327        assert_eq!(saved.len(), 2);
1328        assert_eq!(repo1.len(), 2); // 1 条原始 + 1 条新增
1329
1330        // batch_update:仅 id=1 更新,id=999 跳过
1331        let result = repo2.batch_update(updates).unwrap();
1332        assert_eq!(result.updated_count(), 1);
1333        assert_eq!(result.skipped, 1);
1334        assert_eq!(repo2.len(), 1); // 不增加
1335    }
1336
1337    #[test]
1338    fn test_repo_save_update_existing() {
1339        let repo = InMemoryRepository::<User>::new();
1340        repo.save(User::new(1, "Alice", 30, "alice@example.com"))
1341            .unwrap();
1342
1343        // 更新
1344        repo.save(User::new(1, "Alice Updated", 31, "alice2@example.com"))
1345            .unwrap();
1346
1347        assert_eq!(repo.len(), 1);
1348        let found = repo.find_by_id(&Value::I64(1)).unwrap().unwrap();
1349        assert_eq!(found.name, "Alice Updated");
1350        assert_eq!(found.age, 31);
1351    }
1352
1353    #[test]
1354    fn test_repo_find_all() {
1355        let repo = InMemoryRepository::from_vec(vec![
1356            User::new(1, "Alice", 30, "a@b.com"),
1357            User::new(2, "Bob", 25, "b@b.com"),
1358        ]);
1359        let all = repo.find_all().unwrap();
1360        assert_eq!(all.len(), 2);
1361    }
1362
1363    #[test]
1364    fn test_repo_find_all_empty() {
1365        let repo = InMemoryRepository::<User>::new();
1366        let all = repo.find_all().unwrap();
1367        assert!(all.is_empty());
1368    }
1369
1370    #[test]
1371    fn test_repo_delete() {
1372        let repo = InMemoryRepository::from_vec(vec![
1373            User::new(1, "Alice", 30, "a@b.com"),
1374            User::new(2, "Bob", 25, "b@b.com"),
1375        ]);
1376        let deleted = repo.delete(&Value::I64(1)).unwrap();
1377        assert_eq!(deleted, 1);
1378        assert_eq!(repo.len(), 1);
1379    }
1380
1381    #[test]
1382    fn test_repo_delete_missing() {
1383        let repo = InMemoryRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
1384        let deleted = repo.delete(&Value::I64(999)).unwrap();
1385        assert_eq!(deleted, 0);
1386        assert_eq!(repo.len(), 1);
1387    }
1388
1389    #[test]
1390    fn test_repo_count() {
1391        let repo = InMemoryRepository::from_vec(vec![
1392            User::new(1, "Alice", 30, "a@b.com"),
1393            User::new(2, "Bob", 25, "b@b.com"),
1394            User::new(3, "Carol", 28, "c@b.com"),
1395        ]);
1396        assert_eq!(repo.count().unwrap(), 3);
1397    }
1398
1399    #[test]
1400    fn test_repo_count_empty() {
1401        let repo = InMemoryRepository::<User>::new();
1402        assert_eq!(repo.count().unwrap(), 0);
1403    }
1404
1405    #[test]
1406    fn test_repo_exists() {
1407        let repo = InMemoryRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
1408        assert!(repo.exists(&Value::I64(1)).unwrap());
1409        assert!(!repo.exists(&Value::I64(999)).unwrap());
1410    }
1411
1412    // ===== Repository 条件查询 =====
1413
1414    #[test]
1415    fn test_repo_find_by_eq() {
1416        let repo = InMemoryRepository::from_vec(vec![
1417            User::new(1, "Alice", 30, "a@b.com"),
1418            User::new(2, "Bob", 30, "b@b.com"),
1419            User::new(3, "Carol", 25, "c@b.com"),
1420        ]);
1421
1422        let result = repo
1423            .find_by(&[WhereCondition::new("age", WhereOp::Eq, Value::I64(30))])
1424            .unwrap();
1425        assert_eq!(result.len(), 2);
1426    }
1427
1428    #[test]
1429    fn test_repo_find_by_gt() {
1430        let repo = InMemoryRepository::from_vec(vec![
1431            User::new(1, "Alice", 30, "a@b.com"),
1432            User::new(2, "Bob", 25, "b@b.com"),
1433            User::new(3, "Carol", 35, "c@b.com"),
1434        ]);
1435
1436        let result = repo
1437            .find_by(&[WhereCondition::new("age", WhereOp::Gt, Value::I64(28))])
1438            .unwrap();
1439        assert_eq!(result.len(), 2);
1440    }
1441
1442    #[test]
1443    fn test_repo_find_by_like() {
1444        let repo = InMemoryRepository::from_vec(vec![
1445            User::new(1, "Alice", 30, "alice@example.com"),
1446            User::new(2, "Bob", 25, "bob@example.com"),
1447            User::new(3, "Alicia", 28, "alicia@test.com"),
1448        ]);
1449
1450        let result = repo
1451            .find_by(&[WhereCondition::new(
1452                "name",
1453                WhereOp::Like,
1454                Value::String("Ali%".to_string()),
1455            )])
1456            .unwrap();
1457        assert_eq!(result.len(), 2);
1458    }
1459
1460    #[test]
1461    fn test_repo_find_by_like_case_insensitive() {
1462        // 对齐 MySQL utf8mb4_general_ci / utf8mb4_unicode_ci 默认 collation(大小写不敏感)
1463        let repo = InMemoryRepository::from_vec(vec![
1464            User::new(1, "Alice", 30, "alice@example.com"),
1465            User::new(2, "bob", 25, "bob@example.com"),
1466            User::new(3, "ALICIA", 28, "alicia@test.com"),
1467        ]);
1468
1469        // 小写 pattern 应匹配大小写混合的数据
1470        let result = repo
1471            .find_by(&[WhereCondition::new(
1472                "name",
1473                WhereOp::Like,
1474                Value::String("ali%".to_string()),
1475            )])
1476            .unwrap();
1477        assert_eq!(result.len(), 2, "LIKE should be case-insensitive");
1478
1479        // 大写 pattern 也应匹配小写数据
1480        let result = repo
1481            .find_by(&[WhereCondition::new(
1482                "name",
1483                WhereOp::Like,
1484                Value::String("BOB".to_string()),
1485            )])
1486            .unwrap();
1487        assert_eq!(result.len(), 1, "LIKE exact match should be case-insensitive");
1488    }
1489
1490    #[test]
1491    fn test_repo_find_by_with_or_filter_multi_field_keyword() {
1492        // 对齐 PHP ThinkPHP `where('field1|field2|field3','like','%kw%')` 多字段 OR LIKE
1493        let repo = InMemoryRepository::from_vec(vec![
1494            User::new(1, "Alice", 30, "alice@example.com"),
1495            User::new(2, "Bob", 25, "bob@example.com"),
1496            User::new(3, "Carol", 28, "carol@kw.com"), // email 含 kw
1497            User::new(4, "Dave", 32, "dave@example.com"),
1498        ]);
1499
1500        let and = vec![]; // 无 AND 条件
1501        let or = vec![
1502            WhereCondition::new(
1503                "name",
1504                WhereOp::Like,
1505                Value::String("%kw%".to_string()),
1506            ),
1507            WhereCondition::new(
1508                "email",
1509                WhereOp::Like,
1510                Value::String("%kw%".to_string()),
1511            ),
1512        ];
1513
1514        let result = repo.find_by_with_or_filter(&and, &or).unwrap();
1515        // 只有 Carol 的 email 含 kw
1516        assert_eq!(result.len(), 1);
1517        assert_eq!(result[0].key(), 3);
1518    }
1519
1520    #[test]
1521    fn test_repo_find_by_with_or_filter_combined_with_and() {
1522        // AND + OR 组合:对齐 PHP `where(is_delete=0 AND app_id=1) AND (name LIKE %kw% OR addr LIKE %kw%)`
1523        let repo = InMemoryRepository::from_vec(vec![
1524            User::new(1, "Alice_kw", 30, "alice@example.com"), // app_id=1, name 含 kw
1525            User::new(2, "Bob", 25, "bob@kw.com"),             // app_id=1, email 含 kw
1526            User::new(3, "Carol_kw", 28, "carol@example.com"), // app_id=2, name 含 kw(被 AND 排除)
1527            User::new(4, "Dave", 32, "dave@example.com"),      // app_id=1, 无 kw
1528        ]);
1529
1530        // User struct 没有 app_id 字段,用 age 模拟 AND 条件:age >= 28
1531        let and = vec![WhereCondition::new("age", WhereOp::Ge, Value::I64(28))];
1532        let or = vec![
1533            WhereCondition::new(
1534                "name",
1535                WhereOp::Like,
1536                Value::String("%kw%".to_string()),
1537            ),
1538            WhereCondition::new(
1539                "email",
1540                WhereOp::Like,
1541                Value::String("%kw%".to_string()),
1542            ),
1543        ];
1544
1545        let result = repo.find_by_with_or_filter(&and, &or).unwrap();
1546        // age >= 28: Alice(30), Carol(28), Dave(32)
1547        // 其中 name 或 email 含 kw: Alice(name), Carol(name), Bob(email 不满足 age>=28)
1548        // 但 Carol age=28 满足 >= 28,所以 Carol 也应被选中
1549        assert_eq!(result.len(), 2);
1550        let ids: Vec<i64> = result.iter().map(|u| u.key()).collect();
1551        assert!(ids.contains(&1));
1552        assert!(ids.contains(&3));
1553    }
1554
1555    #[test]
1556    fn test_repo_find_by_with_or_filter_empty_or() {
1557        // or_filter 为空时应等同于 find_by
1558        let repo = InMemoryRepository::from_vec(vec![
1559            User::new(1, "Alice", 30, "alice@example.com"),
1560            User::new(2, "Bob", 25, "bob@example.com"),
1561        ]);
1562
1563        let and = vec![WhereCondition::new("age", WhereOp::Ge, Value::I64(28))];
1564        let or: Vec<WhereCondition> = vec![];
1565
1566        let result = repo.find_by_with_or_filter(&and, &or).unwrap();
1567        assert_eq!(result.len(), 1);
1568        assert_eq!(result[0].key(), 1);
1569    }
1570
1571    #[test]
1572    fn test_repo_find_by_in() {
1573        let repo = InMemoryRepository::from_vec(vec![
1574            User::new(1, "Alice", 30, "a@b.com"),
1575            User::new(2, "Bob", 25, "b@b.com"),
1576            User::new(3, "Carol", 28, "c@b.com"),
1577            User::new(4, "Dave", 32, "d@b.com"),
1578        ]);
1579
1580        let result = repo
1581            .find_by(&[WhereCondition::in_op(
1582                "id",
1583                WhereOp::In,
1584                vec![Value::I64(1), Value::I64(3)],
1585            )])
1586            .unwrap();
1587        assert_eq!(result.len(), 2);
1588    }
1589
1590    #[test]
1591    fn test_repo_find_by_not_in() {
1592        let repo = InMemoryRepository::from_vec(vec![
1593            User::new(1, "Alice", 30, "a@b.com"),
1594            User::new(2, "Bob", 25, "b@b.com"),
1595            User::new(3, "Carol", 28, "c@b.com"),
1596        ]);
1597
1598        let result = repo
1599            .find_by(&[WhereCondition::in_op(
1600                "id",
1601                WhereOp::NotIn,
1602                vec![Value::I64(1)],
1603            )])
1604            .unwrap();
1605        assert_eq!(result.len(), 2);
1606        assert!(result.iter().all(|u| u.id != 1));
1607    }
1608
1609    #[test]
1610    fn test_repo_find_by_between() {
1611        let repo = InMemoryRepository::from_vec(vec![
1612            User::new(1, "Alice", 30, "a@b.com"),
1613            User::new(2, "Bob", 25, "b@b.com"),
1614            User::new(3, "Carol", 35, "c@b.com"),
1615            User::new(4, "Dave", 22, "d@b.com"),
1616        ]);
1617
1618        let result = repo
1619            .find_by(&[WhereCondition::between(
1620                "age",
1621                Value::I64(25),
1622                Value::I64(35),
1623            )])
1624            .unwrap();
1625        assert_eq!(result.len(), 3);
1626    }
1627
1628    #[test]
1629    fn test_repo_find_by_multiple_conditions() {
1630        let repo = InMemoryRepository::from_vec(vec![
1631            User::new(1, "Alice", 30, "a@b.com"),
1632            User::new(2, "Bob", 30, "b@b.com"),
1633            User::new(3, "Alice", 25, "c@b.com"),
1634        ]);
1635
1636        let result = repo
1637            .find_by(&[
1638                WhereCondition::new("name", WhereOp::Eq, Value::String("Alice".to_string())),
1639                WhereCondition::new("age", WhereOp::Ge, Value::I64(30)),
1640            ])
1641            .unwrap();
1642        assert_eq!(result.len(), 1);
1643        assert_eq!(result[0].id, 1);
1644    }
1645
1646    #[test]
1647    fn test_repo_find_one_by() {
1648        let repo = InMemoryRepository::from_vec(vec![
1649            User::new(1, "Alice", 30, "a@b.com"),
1650            User::new(2, "Bob", 25, "b@b.com"),
1651        ]);
1652
1653        let result = repo
1654            .find_one_by(&[WhereCondition::new(
1655                "name",
1656                WhereOp::Eq,
1657                Value::String("Bob".to_string()),
1658            )])
1659            .unwrap();
1660        assert!(result.is_some());
1661        assert_eq!(result.unwrap().id, 2);
1662    }
1663
1664    #[test]
1665    fn test_repo_find_one_by_missing() {
1666        let repo = InMemoryRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
1667
1668        let result = repo
1669            .find_one_by(&[WhereCondition::new(
1670                "name",
1671                WhereOp::Eq,
1672                Value::String("Missing".to_string()),
1673            )])
1674            .unwrap();
1675        assert!(result.is_none());
1676    }
1677
1678    #[test]
1679    fn test_repo_count_by() {
1680        let repo = InMemoryRepository::from_vec(vec![
1681            User::new(1, "Alice", 30, "a@b.com"),
1682            User::new(2, "Bob", 30, "b@b.com"),
1683            User::new(3, "Carol", 25, "c@b.com"),
1684        ]);
1685
1686        let count = repo
1687            .count_by(&[WhereCondition::new("age", WhereOp::Eq, Value::I64(30))])
1688            .unwrap();
1689        assert_eq!(count, 2);
1690    }
1691
1692    #[test]
1693    fn test_repo_delete_by() {
1694        let repo = InMemoryRepository::from_vec(vec![
1695            User::new(1, "Alice", 30, "a@b.com"),
1696            User::new(2, "Bob", 30, "b@b.com"),
1697            User::new(3, "Carol", 25, "c@b.com"),
1698        ]);
1699
1700        let deleted = repo
1701            .delete_by(&[WhereCondition::new("age", WhereOp::Eq, Value::I64(30))])
1702            .unwrap();
1703        assert_eq!(deleted, 2);
1704        assert_eq!(repo.len(), 1);
1705    }
1706
1707    // ===== 分页 =====
1708
1709    #[test]
1710    fn test_repo_paginate() {
1711        let users: Vec<User> = (1..=25)
1712            .map(|i| User::new(i, &format!("User{}", i), 20 + (i % 30), "u@b.com"))
1713            .collect();
1714        let repo = InMemoryRepository::from_vec(users);
1715
1716        let page = repo.paginate(1, 10).unwrap();
1717        assert_eq!(page.page, 1);
1718        assert_eq!(page.page_size, 10);
1719        assert_eq!(page.total, 25);
1720        assert_eq!(page.total_pages(), 3);
1721        assert_eq!(page.items.len(), 10);
1722        assert!(page.has_next());
1723        assert!(!page.has_prev());
1724    }
1725
1726    #[test]
1727    fn test_repo_paginate_last_page() {
1728        let users: Vec<User> = (1..=25)
1729            .map(|i| User::new(i, &format!("User{}", i), 20, "u@b.com"))
1730            .collect();
1731        let repo = InMemoryRepository::from_vec(users);
1732
1733        let page = repo.paginate(3, 10).unwrap();
1734        assert_eq!(page.items.len(), 5);
1735        assert!(page.has_prev());
1736        assert!(!page.has_next());
1737    }
1738
1739    #[test]
1740    fn test_repo_paginate_out_of_range() {
1741        let users: Vec<User> = (1..=5)
1742            .map(|i| User::new(i, &format!("User{}", i), 20, "u@b.com"))
1743            .collect();
1744        let repo = InMemoryRepository::from_vec(users);
1745
1746        let page = repo.paginate(10, 10).unwrap();
1747        assert_eq!(page.items.len(), 0);
1748        assert_eq!(page.total, 5);
1749    }
1750
1751    #[test]
1752    fn test_repo_paginate_by() {
1753        let users: Vec<User> = (1..=20)
1754            .map(|i| User::new(i, &format!("User{}", i), 20 + (i % 5), "u@b.com"))
1755            .collect();
1756        let repo = InMemoryRepository::from_vec(users);
1757
1758        // age=22 的用户:i=2,7,12,17 → 4 个
1759        let page = repo
1760            .paginate_by(
1761                &[WhereCondition::new("age", WhereOp::Eq, Value::I64(22))],
1762                1,
1763                2,
1764            )
1765            .unwrap();
1766        assert_eq!(page.total, 4);
1767        assert_eq!(page.items.len(), 2);
1768        assert_eq!(page.total_pages(), 2);
1769    }
1770
1771    // ===== GenericKeyRepository =====
1772
1773    #[test]
1774    fn test_generic_key_repo_basic() {
1775        let repo: GenericKeyRepository<User, i64> = GenericKeyRepository::new();
1776        assert!(repo.is_empty());
1777
1778        let user = User::new(1, "Alice", 30, "a@b.com");
1779        repo.save(user.clone()).unwrap();
1780        assert_eq!(repo.len(), 1);
1781
1782        let found = repo.find_by_id(&1).unwrap();
1783        assert_eq!(found, Some(user));
1784    }
1785
1786    #[test]
1787    fn test_generic_key_repo_delete() {
1788        let repo: GenericKeyRepository<User, i64> = GenericKeyRepository::from_vec(vec![
1789            User::new(1, "Alice", 30, "a@b.com"),
1790            User::new(2, "Bob", 25, "b@b.com"),
1791        ]);
1792
1793        let deleted = repo.delete(&1).unwrap();
1794        assert_eq!(deleted, 1);
1795        assert_eq!(repo.len(), 1);
1796
1797        let remaining = repo.find_all().unwrap();
1798        assert_eq!(remaining[0].id, 2);
1799    }
1800
1801    #[test]
1802    fn test_generic_key_repo_find_by() {
1803        let repo: GenericKeyRepository<User, i64> = GenericKeyRepository::from_vec(vec![
1804            User::new(1, "Alice", 30, "a@b.com"),
1805            User::new(2, "Bob", 30, "b@b.com"),
1806            User::new(3, "Carol", 25, "c@b.com"),
1807        ]);
1808
1809        let result = repo
1810            .find_by(&[WhereCondition::new("age", WhereOp::Eq, Value::I64(30))])
1811            .unwrap();
1812        assert_eq!(result.len(), 2);
1813    }
1814
1815    #[test]
1816    fn test_generic_key_repo_paginate() {
1817        let users: Vec<User> = (1..=15)
1818            .map(|i| User::new(i, &format!("User{}", i), 20, "u@b.com"))
1819            .collect();
1820        let repo: GenericKeyRepository<User, i64> = GenericKeyRepository::from_vec(users);
1821
1822        let page = repo.paginate(2, 10).unwrap();
1823        assert_eq!(page.items.len(), 5);
1824        assert_eq!(page.total, 15);
1825        assert_eq!(page.page, 2);
1826    }
1827
1828    #[test]
1829    fn test_generic_key_repo_count() {
1830        let repo: GenericKeyRepository<User, i64> = GenericKeyRepository::from_vec(vec![
1831            User::new(1, "Alice", 30, "a@b.com"),
1832            User::new(2, "Bob", 25, "b@b.com"),
1833        ]);
1834        assert_eq!(repo.count().unwrap(), 2);
1835    }
1836
1837    #[test]
1838    fn test_generic_key_repo_exists() {
1839        let repo: GenericKeyRepository<User, i64> =
1840            GenericKeyRepository::from_vec(vec![User::new(1, "Alice", 30, "a@b.com")]);
1841        assert!(repo.exists(&1).unwrap());
1842        assert!(!repo.exists(&999).unwrap());
1843    }
1844
1845    // ===== simple_regex =====
1846
1847    #[test]
1848    fn test_simple_regex_literal() {
1849        let re = simple_regex::compile("^abc$").unwrap();
1850        assert!(re.is_match("abc"));
1851        assert!(!re.is_match("abcd"));
1852    }
1853
1854    #[test]
1855    fn test_simple_regex_wildcard() {
1856        let re = simple_regex::compile("^Ali.*$").unwrap();
1857        assert!(re.is_match("Alice"));
1858        assert!(re.is_match("Alicia"));
1859        assert!(!re.is_match("Bob"));
1860    }
1861
1862    #[test]
1863    fn test_simple_regex_no_anchors() {
1864        let re = simple_regex::compile("ab").unwrap();
1865        assert!(re.is_match("ab"));
1866    }
1867
1868    // ===== 端到端场景 =====
1869
1870    #[test]
1871    fn test_e2e_repository_workflow() {
1872        let repo = InMemoryRepository::<User>::new();
1873
1874        // 1. 批量插入
1875        let users = vec![
1876            User::new(1, "Alice", 30, "alice@example.com"),
1877            User::new(2, "Bob", 25, "bob@example.com"),
1878            User::new(3, "Carol", 35, "carol@example.com"),
1879            User::new(4, "Dave", 28, "dave@example.com"),
1880            User::new(5, "Eve", 32, "eve@example.com"),
1881        ];
1882        repo.save_many(users).unwrap();
1883        assert_eq!(repo.count().unwrap(), 5);
1884
1885        // 2. 查找成年人(age >= 30)
1886        let adults = repo
1887            .find_by(&[WhereCondition::new("age", WhereOp::Ge, Value::I64(30))])
1888            .unwrap();
1889        assert_eq!(adults.len(), 3);
1890
1891        // 3. 分页查询
1892        let page1 = repo.paginate(1, 2).unwrap();
1893        assert_eq!(page1.items.len(), 2);
1894        assert_eq!(page1.total_pages(), 3);
1895
1896        let page2 = repo.paginate(2, 2).unwrap();
1897        assert_eq!(page2.items.len(), 2);
1898
1899        let page3 = repo.paginate(3, 2).unwrap();
1900        assert_eq!(page3.items.len(), 1);
1901
1902        // 4. 条件分页查询(age >= 30)
1903        let adult_page = repo
1904            .paginate_by(
1905                &[WhereCondition::new("age", WhereOp::Ge, Value::I64(30))],
1906                1,
1907                2,
1908            )
1909            .unwrap();
1910        assert_eq!(adult_page.total, 3);
1911        assert_eq!(adult_page.items.len(), 2);
1912
1913        // 5. 更新
1914        repo.save(User::new(1, "Alice Smith", 31, "alice.smith@example.com"))
1915            .unwrap();
1916        let updated = repo.find_by_id(&Value::I64(1)).unwrap().unwrap();
1917        assert_eq!(updated.name, "Alice Smith");
1918        assert_eq!(updated.age, 31);
1919
1920        // 6. 删除
1921        let deleted = repo.delete(&Value::I64(2)).unwrap();
1922        assert_eq!(deleted, 1);
1923        assert_eq!(repo.count().unwrap(), 4);
1924        assert!(!repo.exists(&Value::I64(2)).unwrap());
1925
1926        // 7. 条件删除 age >= 31:Alice(31) + Carol(35) + Eve(32) = 3 个
1927        let deleted_by = repo
1928            .delete_by(&[WhereCondition::new("age", WhereOp::Ge, Value::I64(31))])
1929            .unwrap();
1930        assert_eq!(deleted_by, 3);
1931        assert_eq!(repo.count().unwrap(), 1); // 仅剩 Dave(28)
1932    }
1933
1934    #[test]
1935    fn test_e2e_pagination_navigation() {
1936        let users: Vec<User> = (1..=100)
1937            .map(|i| User::new(i, &format!("User{}", i), 20, "u@b.com"))
1938            .collect();
1939        let repo = InMemoryRepository::from_vec(users);
1940
1941        let mut current_page = 1u64;
1942        let mut visited: Vec<u64> = Vec::new();
1943        loop {
1944            let page = repo.paginate(current_page, 10).unwrap();
1945            visited.push(current_page);
1946            if !page.has_next() {
1947                break;
1948            }
1949            current_page += 1;
1950        }
1951        assert_eq!(visited.len(), 10);
1952        assert_eq!(visited, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
1953    }
1954}