1pub const MODEL_STUB: &str = r#"//! {%namespace%}::{%className%}
49//!
50//! 由 `sz-rust make:model` 生成。
51
52use sz_rust_core::orm::Model;
53
54/// {%className%} 模型
55///
56/// 对齐 PHP `class {%className%} extends Model`
57pub struct {%className%};
58
59impl Model for {%className%} {
60 fn table_name() -> &'static str {
61 "{%table_name%}"
62 }
63}
64"#;
65
66pub const CONTROLLER_STUB: &str = r#"//! {%namespace%}::{%className%} 控制器
70//!
71//! 由 `sz-rust make:controller` 生成,对齐 PHP `controller.stub`(7 个 RESTful 方法)。
72
73use sz_rust_core::controller::SzController;
74use sz_rust_core::request::Request;
75use sz_rust_core::response::Response;
76
77/// {%className%} 控制器
78pub struct {%className%};
79
80impl {%className%} {
81 /// 列表(GET /{%route%})
82 pub async fn index(_req: Request) -> Response {
83 Response::success("index")
84 }
85
86 /// 新建表单(GET /{%route%}/create)
87 pub async fn create(_req: Request) -> Response {
88 Response::success("create")
89 }
90
91 /// 保存(POST /{%route%})
92 pub async fn save(_req: Request) -> Response {
93 Response::success("save")
94 }
95
96 /// 详情(GET /{%route%}/{id})
97 pub async fn read(_req: Request) -> Response {
98 Response::success("read")
99 }
100
101 /// 编辑表单(GET /{%route%}/{id}/edit)
102 pub async fn edit(_req: Request) -> Response {
103 Response::success("edit")
104 }
105
106 /// 更新(PUT /{%route%}/{id})
107 pub async fn update(_req: Request) -> Response {
108 Response::success("update")
109 }
110
111 /// 删除(DELETE /{%route%}/{id})
112 pub async fn delete(_req: Request) -> Response {
113 Response::success("delete")
114 }
115}
116"#;
117
118pub const CONTROLLER_API_STUB: &str = r#"//! {%namespace%}::{%className%} API 控制器
122//!
123//! 由 `sz-rust make:controller --api` 生成,对齐 PHP `controller.api.stub`(5 个方法)。
124
125use sz_rust_core::request::Request;
126use sz_rust_core::response::Response;
127
128/// {%className%} API 控制器
129pub struct {%className%};
130
131impl {%className%} {
132 /// 列表(GET /{%route%})
133 pub async fn index(_req: Request) -> Response {
134 Response::success("index")
135 }
136
137 /// 保存(POST /{%route%})
138 pub async fn save(_req: Request) -> Response {
139 Response::success("save")
140 }
141
142 /// 详情(GET /{%route%}/{id})
143 pub async fn read(_req: Request) -> Response {
144 Response::success("read")
145 }
146
147 /// 更新(PUT /{%route%}/{id})
148 pub async fn update(_req: Request) -> Response {
149 Response::success("update")
150 }
151
152 /// 删除(DELETE /{%route%}/{id})
153 pub async fn delete(_req: Request) -> Response {
154 Response::success("delete")
155 }
156}
157"#;
158
159pub const CONTROLLER_PLAIN_STUB: &str = r#"//! {%namespace%}::{%className%} 控制器
163//!
164//! 由 `sz-rust make:controller --plain` 生成,对齐 PHP `controller.plain.stub`(空类)。
165
166/// {%className%} 控制器(空骨架)
167pub struct {%className%};
168"#;
169
170pub const MIGRATION_UP_STUB: &str = r#"-- Migration: {%name%}
172-- Direction: UP
173-- Created: {%timestamp%}
174
175-- 在此编写 up SQL
176-- CREATE TABLE IF NOT EXISTS {%table_name%} (
177-- id BIGINT PRIMARY KEY AUTO_INCREMENT,
178-- created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
179-- updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
180-- );
181"#;
182
183pub const MIGRATION_DOWN_STUB: &str = r#"-- Migration: {%name%}
185-- Direction: DOWN
186-- Created: {%timestamp%}
187
188-- 在此编写 down SQL
189-- DROP TABLE IF EXISTS {%table_name%};
190"#;
191
192pub const SEED_STUB: &str = r#"-- Seed: {%name%}
196-- Created: {%timestamp%}
197
198-- 在此编写数据填充 SQL
199-- INSERT INTO users (name, email) VALUES ('admin', 'admin@example.com');
200"#;
201
202pub const VALIDATE_STUB: &str = r#"//! {%namespace%}::{%className%} 验证器
206//!
207//! 由 `sz-rust make:validate` 生成,对齐 PHP `make:validate`。
208//! 业务实现 `rules()` 返回验证规则,调用 `validate(&data)` 执行验证。
209
210use sz_rust_core::validate::Validate;
211
212/// {%className%} 验证器
213///
214/// 对齐 PHP `class {%className%} extends \think\Validate`。
215pub struct {%className%}Validate;
216
217impl {%className%}Validate {
218 /// 创建验证器实例(含规则定义)
219 ///
220 /// 对齐 PHP `protected $rule = [...]`。
221 pub fn new() -> Validate {
222 Validate::new()
223 .rule("name", "require|max:50")
224 .rule("age", "require|integer|gt:0")
225 // 在此添加更多验证规则
226 }
227}
228
229impl Default for {%className%}Validate {
230 fn default() -> Self {
231 Self
232 }
233}
234"#;
235
236pub const EVENT_STUB: &str = r#"//! {%namespace%}::{%className%} 事件
240//!
241//! 由 `sz-rust make:event` 生成,对齐 PHP `make:event`。
242//! 通过 `EventDispatcher::trigger({%className%}::name(), &payload)` 触发。
243
244use serde_json::Value;
245
246/// {%className%} 事件
247///
248/// 对齐 PHP `class {%className%} extends \think\Event`。
249pub struct {%className%};
250
251impl {%className%} {
252 /// 事件名称(用于监听器注册与触发)
253 pub fn name() -> &'static str {
254 "{%event_name%}"
255 }
256
257 /// 构造事件负载
258 ///
259 /// 在此组装事件需要传递给监听器的数据。
260 pub fn payload(&self) -> Value {
261 Value::Null
262 }
263}
264"#;
265
266pub const LISTENER_STUB: &str = r#"//! {%namespace%}::{%className%} 监听器
270//!
271//! 由 `sz-rust make:listener` 生成,对齐 PHP `make:listener`。
272//! 通过 `EventDispatcher::listen("{%event_name%}", Arc::new({%className%}))` 注册。
273
274use serde_json::Value;
275use sz_rust_core::event::{EventError, Listener};
276
277/// {%className%} 监听器
278///
279/// 对齐 PHP `class {%className%} { public function handle($params) {} }`。
280pub struct {%className%};
281
282impl Listener for {%className%} {
283 /// 处理事件(对齐 PHP `Listener::handle($params)`)
284 ///
285 /// # 参数
286 ///
287 /// - `params`:事件负载
288 ///
289 /// # 返回
290 ///
291 /// - `Ok(Value::Null)`:继续执行后续监听器
292 /// - `Ok(其他值)`:在 `once=true` 模式下停止后续监听器
293 /// - `Err(_)`:停止后续监听器执行
294 fn handle(&self, _params: &Value) -> Result<Value, EventError> {
295 // 在此实现监听逻辑
296 Ok(Value::Null)
297 }
298}
299"#;
300
301pub const COMMAND_STUB: &str = r#"//! {%namespace%}::{%className%} 命令
305//!
306//! 由 `sz-rust make:command` 生成,对齐 PHP `make:command`。
307//! 通过 `Console::register(Box::new({%className%}))` 注册后可被 `sz-rust {%command_name%}` 调用。
308
309use sz_rust_cli::console::{Command, CommandSignature};
310use sz_rust_cli::error::CliError;
311
312/// {%className%} 命令
313///
314/// 对齐 PHP `class {%className%} extends \think\console\Command`。
315pub struct {%className%};
316
317impl Command for {%className%} {
318 /// 命令签名(对齐 PHP `Command::configure()`)
319 fn signature(&self) -> CommandSignature {
320 CommandSignature::new(
321 "{%command_name%}",
322 "{%className%} 自定义命令",
323 )
324 .usage("sz-rust {%command_name%} [options]")
325 }
326
327 /// 执行命令(对齐 PHP `Command::execute(Input $input, Output $output)`)
328 fn execute(&self, _args: &[String]) -> Result<i32, CliError> {
329 println!("{%className%} executed");
330 Ok(0)
331 }
332}
333"#;
334
335pub const SERVICE_STUB: &str = r#"//! {%namespace%}::{%className%} 服务
339//!
340//! 由 `sz-rust make:service` 生成,对齐 PHP `make:service`。
341//! 通过 `Container::singleton::<{%className%}>()` 注册后可在控制器中自动注入。
342
343/// {%className%} 服务
344///
345/// 对齐 PHP `class {%className%} { public function __construct() {} }`。
346pub struct {%className%};
347
348impl {%className%} {
349 /// 创建服务实例
350 pub fn new() -> Self {
351 Self
352 }
353
354 /// 业务方法示例
355 ///
356 /// 在此实现具体业务逻辑。
357 pub fn execute(&self) -> Result<(), String> {
358 // 在此实现业务逻辑
359 Ok(())
360 }
361}
362
363impl Default for {%className%} {
364 fn default() -> Self {
365 Self::new()
366 }
367}
368"#;
369
370pub const MIDDLEWARE_STUB: &str = r#"//! {%namespace%}::{%className%} 中间件
374//!
375//! 由 `sz-rust make:middleware` 生成。
376//! 在中间件链配置中通过 `MiddlewareChain::add(MiddlewareKind::{%className%})` 注册。
377
378use sz_rust_core::middleware::SzMiddleware;
379use axum::{body::Body, http::Request, response::Response};
380
381/// {%className%} 中间件
382pub struct {%className%};
383
384#[sz_rust_core::middleware]
385impl SzMiddleware for {%className%} {
386 async fn handle(
387 &self,
388 req: Request<Body>,
389 next: impl FnOnce(Request<Body>) -> std::pin::Pin<Box<dyn std::future::Future<Output = Response> + Send>>
390 ) -> Response {
391 // 前置处理
392 // tracing::info!("请求进入 {%className%}: {}", req.uri().path());
393
394 let response = next(req).await;
395
396 // 后置处理
397 // tracing::info!("响应离开 {%className%}: {}", response.status());
398
399 response
400 }
401}
402"#;
403
404pub fn render_template(template: &str, replacements: &[(&str, &str)]) -> String {
413 let mut result = template.to_string();
414 for (placeholder, value) in replacements {
415 result = result.replace(placeholder, value);
416 }
417 result
418}
419
420#[cfg(test)]
421mod tests {
422 use super::*;
423
424 #[test]
425 fn test_model_stub_contains_class_placeholder() {
426 assert!(MODEL_STUB.contains("{%className%}"));
427 assert!(MODEL_STUB.contains("{%namespace%}"));
428 assert!(MODEL_STUB.contains("{%table_name%}"));
429 }
430
431 #[test]
432 fn test_controller_stub_has_seven_methods() {
433 assert!(CONTROLLER_STUB.contains("index"));
434 assert!(CONTROLLER_STUB.contains("create"));
435 assert!(CONTROLLER_STUB.contains("save"));
436 assert!(CONTROLLER_STUB.contains("read"));
437 assert!(CONTROLLER_STUB.contains("edit"));
438 assert!(CONTROLLER_STUB.contains("update"));
439 assert!(CONTROLLER_STUB.contains("delete"));
440 }
441
442 #[test]
443 fn test_controller_api_stub_has_five_methods() {
444 assert!(CONTROLLER_API_STUB.contains("index"));
445 assert!(CONTROLLER_API_STUB.contains("save"));
446 assert!(CONTROLLER_API_STUB.contains("read"));
447 assert!(CONTROLLER_API_STUB.contains("update"));
448 assert!(CONTROLLER_API_STUB.contains("delete"));
449 assert!(!CONTROLLER_API_STUB.contains("pub async fn create"));
451 assert!(!CONTROLLER_API_STUB.contains("pub async fn edit"));
452 }
453
454 #[test]
455 fn test_controller_plain_stub_is_empty_class() {
456 assert!(CONTROLLER_PLAIN_STUB.contains("struct {%className%}"));
457 }
458
459 #[test]
460 fn test_migration_stubs_contain_placeholders() {
461 assert!(MIGRATION_UP_STUB.contains("{%name%}"));
462 assert!(MIGRATION_UP_STUB.contains("{%timestamp%}"));
463 assert!(MIGRATION_DOWN_STUB.contains("{%name%}"));
464 assert!(MIGRATION_DOWN_STUB.contains("{%timestamp%}"));
465 }
466
467 #[test]
468 fn test_seed_stub_contains_placeholders() {
469 assert!(SEED_STUB.contains("{%name%}"));
470 assert!(SEED_STUB.contains("{%timestamp%}"));
471 }
472
473 #[test]
474 fn test_seed_stub_render() {
475 let result = render_template(
476 SEED_STUB,
477 &[
478 ("{%name%}", "001_users"),
479 ("{%timestamp%}", "2026-07-31 00:00:00 UTC"),
480 ],
481 );
482 assert!(result.contains("001_users"));
483 assert!(result.contains("2026-07-31"));
484 assert!(!result.contains("{%"));
486 }
487
488 #[test]
489 fn test_validate_stub_contains_placeholders() {
490 assert!(VALIDATE_STUB.contains("{%className%}"));
491 assert!(VALIDATE_STUB.contains("{%namespace%}"));
492 }
493
494 #[test]
495 fn test_validate_stub_render() {
496 let result = render_template(
497 VALIDATE_STUB,
498 &[
499 ("{%className%}", "User"),
500 ("{%namespace%}", "app::validate"),
501 ],
502 );
503 assert!(result.contains("UserValidate"));
504 assert!(result.contains("app::validate"));
505 assert!(result.contains("use sz_rust_core::validate::Validate"));
506 assert!(!result.contains("{%"));
508 }
509
510 #[test]
511 fn test_render_template_basic() {
512 let result = render_template("Hello {%name%}!", &[("{%name%}", "World")]);
513 assert_eq!(result, "Hello World!");
514 }
515
516 #[test]
517 fn test_render_template_multiple_placeholders() {
518 let result = render_template(
519 "{%className%} in {%namespace%}",
520 &[("{%className%}", "User"), ("{%namespace%}", "app::model")],
521 );
522 assert_eq!(result, "User in app::model");
523 }
524
525 #[test]
526 fn test_render_template_no_placeholders() {
527 let result = render_template("no placeholders", &[]);
528 assert_eq!(result, "no placeholders");
529 }
530
531 #[test]
532 fn test_render_template_repeated_placeholders() {
533 let result = render_template("{%x%} and {%x%}", &[("{%x%}", "A")]);
534 assert_eq!(result, "A and A");
535 }
536
537 #[test]
540 fn test_event_stub_contains_placeholders() {
541 assert!(EVENT_STUB.contains("{%className%}"));
542 assert!(EVENT_STUB.contains("{%namespace%}"));
543 assert!(EVENT_STUB.contains("{%event_name%}"));
544 }
545
546 #[test]
547 fn test_event_stub_render() {
548 let result = render_template(
549 EVENT_STUB,
550 &[
551 ("{%className%}", "UserLogin"),
552 ("{%namespace%}", "app::event"),
553 ("{%event_name%}", "UserLogin"),
554 ],
555 );
556 assert!(result.contains("UserLogin"));
557 assert!(result.contains("app::event"));
558 assert!(result.contains("use serde_json::Value"));
559 assert!(!result.contains("{%"));
561 }
562
563 #[test]
564 fn test_listener_stub_contains_placeholders() {
565 assert!(LISTENER_STUB.contains("{%className%}"));
566 assert!(LISTENER_STUB.contains("{%namespace%}"));
567 assert!(LISTENER_STUB.contains("{%event_name%}"));
568 }
569
570 #[test]
571 fn test_listener_stub_render() {
572 let result = render_template(
573 LISTENER_STUB,
574 &[
575 ("{%className%}", "SendWelcomeEmail"),
576 ("{%namespace%}", "app::listener"),
577 ("{%event_name%}", "UserLogin"),
578 ],
579 );
580 assert!(result.contains("SendWelcomeEmail"));
581 assert!(result.contains("app::listener"));
582 assert!(result.contains("use sz_rust_core::event::{EventError, Listener}"));
583 assert!(result.contains("impl Listener for SendWelcomeEmail"));
584 assert!(!result.contains("{%"));
586 }
587
588 #[test]
589 fn test_command_stub_contains_placeholders() {
590 assert!(COMMAND_STUB.contains("{%className%}"));
591 assert!(COMMAND_STUB.contains("{%namespace%}"));
592 assert!(COMMAND_STUB.contains("{%command_name%}"));
593 }
594
595 #[test]
596 fn test_command_stub_render() {
597 let result = render_template(
598 COMMAND_STUB,
599 &[
600 ("{%className%}", "SyncData"),
601 ("{%namespace%}", "app::command"),
602 ("{%command_name%}", "sync:data"),
603 ],
604 );
605 assert!(result.contains("SyncData"));
606 assert!(result.contains("app::command"));
607 assert!(result.contains("sync:data"));
608 assert!(result.contains("use sz_rust_cli::console::{Command, CommandSignature}"));
609 assert!(result.contains("impl Command for SyncData"));
610 assert!(!result.contains("{%"));
612 }
613
614 #[test]
615 fn test_service_stub_contains_placeholders() {
616 assert!(SERVICE_STUB.contains("{%className%}"));
617 assert!(SERVICE_STUB.contains("{%namespace%}"));
618 }
619
620 #[test]
621 fn test_service_stub_render() {
622 let result = render_template(
623 SERVICE_STUB,
624 &[
625 ("{%className%}", "UserService"),
626 ("{%namespace%}", "app::service"),
627 ],
628 );
629 assert!(result.contains("UserService"));
630 assert!(result.contains("app::service"));
631 assert!(result.contains("impl Default for UserService"));
632 assert!(!result.contains("{%"));
634 }
635}