Skip to main content

sz_rust_core/
seed.rs

1//! 数据填充(Seed)— 对齐 PHP `think\db\Seed`
2//!
3//! ## PHP 对齐
4//!
5//! PHP `db:seed` 通过 `Seeder` 类的 `run()` 方法执行数据填充:
6//! ```php
7//! class DatabaseSeeder extends Seeder {
8//!     public function run() {
9//!         $this->call('UserSeeder');
10//!         $this->call('RoleSeeder');
11//!     }
12//! }
13//! ```
14//!
15//! Rust 端通过 [`Seeder`] trait 抽象填充器,业务实现 `run()` 方法即可。
16//! 由于 Rust 为静态编译语言,无法像 PHP 那样按类名动态加载,
17//! CLI 层(`sz-rust-cli`)通过加载 `seeds/` 目录下的 SQL 文件实现离线填充。
18//!
19//! ## 使用示例
20//!
21//! ```ignore
22//! use sz_rust_core::orm::Connection;
23//! use sz_rust_core::seed::Seeder;
24//!
25//! struct UserSeeder;
26//!
27//! #[async_trait::async_trait]
28//! impl Seeder for UserSeeder {
29//!     fn name(&self) -> &str { "UserSeeder" }
30//!
31//!     async fn run(&self, conn: &mut Box<dyn Connection>) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
32//!         conn.execute("INSERT INTO users (name, email) VALUES ('admin', 'admin@example.com')").await?;
33//!         Ok(())
34//!     }
35//! }
36//! ```
37
38use std::future::Future;
39use std::pin::Pin;
40
41use crate::orm::Connection;
42
43/// 填充器执行结果:Boxed Future,成功返回 `()`,失败返回可发送的错误
44pub type SeederResult<'a> =
45    Pin<Box<dyn Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>> + Send + 'a>>;
46
47/// 填充器 trait — 对齐 PHP `think\db\Seeder`
48///
49/// 业务实现该 trait,在 `run()` 中执行数据插入。
50/// CLI 层通过加载 `seeds/` 目录下的 SQL 文件实现离线填充,
51/// 程序化场景下可直接实现该 trait 并通过 [`SeedRunner`] 执行。
52pub trait Seeder: Send + Sync {
53    /// 填充器名称(用于日志输出)
54    fn name(&self) -> &str;
55
56    /// 执行数据填充
57    ///
58    /// # 参数
59    ///
60    /// - `conn`:数据库连接(已建立)
61    ///
62    /// # 错误
63    ///
64    /// 返回 `Err` 表示填充失败,调用方决定是否回滚。
65    fn run<'a>(&'a self, conn: &'a mut Box<dyn Connection>) -> SeederResult<'a>;
66}
67
68/// 填充器运行器 — 管理多个填充器的顺序执行
69///
70/// 对齐 PHP `DatabaseSeeder::run()` 中通过 `$this->call()` 串联多个子填充器的模式。
71///
72/// ## 使用示例
73///
74/// ```ignore
75/// let mut runner = SeedRunner::new();
76/// runner.register(Box::new(UserSeeder));
77/// runner.register(Box::new(RoleSeeder));
78/// runner.execute(&mut conn).await?;
79/// ```
80pub struct SeedRunner {
81    seeders: Vec<Box<dyn Seeder>>,
82}
83
84impl SeedRunner {
85    /// 创建空的填充器运行器
86    pub fn new() -> Self {
87        Self {
88            seeders: Vec::new(),
89        }
90    }
91
92    /// 注册填充器(按注册顺序执行)
93    pub fn register(&mut self, seeder: Box<dyn Seeder>) -> &mut Self {
94        self.seeders.push(seeder);
95        self
96    }
97
98    /// 按注册顺序执行所有填充器
99    ///
100    /// # 错误
101    ///
102    /// 任一填充器失败即返回错误,后续填充器不再执行。
103    pub async fn execute(
104        &self,
105        conn: &mut Box<dyn Connection>,
106    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
107        for seeder in &self.seeders {
108            println!("[Seed] Running {}...", seeder.name());
109            seeder.run(conn).await?;
110            println!("[Seed] {} completed.", seeder.name());
111        }
112        Ok(())
113    }
114
115    /// 返回已注册的填充器数量
116    pub fn len(&self) -> usize {
117        self.seeders.len()
118    }
119
120    /// 是否为空
121    pub fn is_empty(&self) -> bool {
122        self.seeders.is_empty()
123    }
124}
125
126impl Default for SeedRunner {
127    fn default() -> Self {
128        Self::new()
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135    use std::sync::atomic::{AtomicUsize, Ordering};
136    use std::sync::Arc;
137
138    /// 测试用的计数填充器
139    struct CounterSeeder {
140        name: String,
141        counter: Arc<AtomicUsize>,
142    }
143
144    impl Seeder for CounterSeeder {
145        fn name(&self) -> &str {
146            &self.name
147        }
148
149        fn run<'a>(&'a self, _conn: &'a mut Box<dyn Connection>) -> SeederResult<'a> {
150            Box::pin(async move {
151                self.counter.fetch_add(1, Ordering::SeqCst);
152                Ok(())
153            })
154        }
155    }
156
157    #[test]
158    fn test_seed_runner_register_and_len() {
159        let counter = Arc::new(AtomicUsize::new(0));
160        let mut runner = SeedRunner::new();
161        assert_eq!(runner.len(), 0);
162        assert!(runner.is_empty());
163
164        runner.register(Box::new(CounterSeeder {
165            name: "A".to_string(),
166            counter: counter.clone(),
167        }));
168        runner.register(Box::new(CounterSeeder {
169            name: "B".to_string(),
170            counter: counter.clone(),
171        }));
172        assert_eq!(runner.len(), 2);
173        assert!(!runner.is_empty());
174    }
175
176    #[test]
177    fn test_seed_runner_default() {
178        let runner = SeedRunner::default();
179        assert!(runner.is_empty());
180    }
181}