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}