use std::path::{Path, PathBuf};
use clap::Subcommand;
use crate::error::CliError;
use crate::stubs::{self, render_template};
#[derive(Subcommand, Debug)]
pub enum MakeCommand {
#[command(name = "model")]
Model {
name: String,
},
#[command(name = "controller")]
Controller {
name: String,
#[arg(long)]
api: bool,
#[arg(long)]
plain: bool,
},
#[command(name = "migration")]
Migration {
name: String,
#[arg(short = 'p', long, default_value = "migrations")]
path: String,
},
#[command(name = "seeder")]
Seeder {
name: String,
#[arg(short = 'p', long, default_value = "seeds")]
path: String,
},
#[command(name = "guard")]
Guard {
name: String,
},
#[command(name = "validate")]
Validate {
name: String,
},
#[command(name = "event")]
Event {
name: String,
},
#[command(name = "listener")]
Listener {
name: String,
#[arg(long)]
event: Option<String>,
},
#[command(name = "command")]
Command {
name: String,
},
#[command(name = "service")]
Service {
name: String,
},
#[command(name = "middleware")]
Middleware {
name: String,
},
#[command(name = "scaffold")]
Scaffold {
name: String,
},
}
pub fn execute(cmd: &MakeCommand) -> Result<(), CliError> {
match cmd {
MakeCommand::Model { name } => execute_make_model(name),
MakeCommand::Controller { name, api, plain } => execute_make_controller(name, *api, *plain),
MakeCommand::Migration { name, path } => execute_make_migration(name, path),
MakeCommand::Seeder { name, path } => execute_make_seeder(name, path),
MakeCommand::Guard { name } => execute_make_guard(name),
MakeCommand::Validate { name } => execute_make_validate(name),
MakeCommand::Event { name } => execute_make_event(name),
MakeCommand::Listener { name, event } => execute_make_listener(name, event.as_deref()),
MakeCommand::Command { name } => execute_make_command(name),
MakeCommand::Service { name } => execute_make_service(name),
MakeCommand::Middleware { name } => execute_make_middleware(name),
MakeCommand::Scaffold { name } => execute_make_scaffold(name),
}
}
fn execute_make_model(name: &str) -> Result<(), CliError> {
let (class_name, module_path, file_path) = resolve_target(name, "model");
check_file_exists(&file_path)?;
let namespace = format!("app::{}", module_path);
let table_name = class_to_snake(&class_name);
let content = render_template(
stubs::MODEL_STUB,
&[
("{%className%}", &class_name),
("{%namespace%}", &namespace),
("{%table_name%}", &table_name),
],
);
write_file(&file_path, &content)?;
println!("Model created: {}", file_path.display());
Ok(())
}
fn execute_make_controller(name: &str, api: bool, plain: bool) -> Result<(), CliError> {
let (class_name, module_path, file_path) = resolve_target(name, "controller");
check_file_exists(&file_path)?;
let namespace = format!("app::{}", module_path);
let route = class_to_snake(&class_name);
let template = if plain {
stubs::CONTROLLER_PLAIN_STUB
} else if api {
stubs::CONTROLLER_API_STUB
} else {
stubs::CONTROLLER_STUB
};
let content = render_template(
template,
&[
("{%className%}", &class_name),
("{%namespace%}", &namespace),
("{%route%}", &route),
],
);
write_file(&file_path, &content)?;
println!("Controller created: {}", file_path.display());
Ok(())
}
fn execute_make_migration(name: &str, path: &str) -> Result<(), CliError> {
let dir = Path::new(path);
std::fs::create_dir_all(dir)?;
let timestamp = chrono::Utc::now().format("%Y%m%d%H%M%S").to_string();
let table_name = name_to_table(name);
let up_file = dir.join(format!("{}_{}_up.sql", timestamp, name));
let down_file = dir.join(format!("{}_{}_down.sql", timestamp, name));
check_file_exists(&up_file)?;
check_file_exists(&down_file)?;
let up_content = render_template(
stubs::MIGRATION_UP_STUB,
&[
("{%name%}", name),
("{%timestamp%}", ×tamp),
("{%table_name%}", &table_name),
],
);
let down_content = render_template(
stubs::MIGRATION_DOWN_STUB,
&[
("{%name%}", name),
("{%timestamp%}", ×tamp),
("{%table_name%}", &table_name),
],
);
write_file(&up_file, &up_content)?;
write_file(&down_file, &down_content)?;
println!(
"Migration created: {} & {}",
up_file.display(),
down_file.display()
);
Ok(())
}
fn execute_make_seeder(name: &str, path: &str) -> Result<(), CliError> {
let dir = Path::new(path);
std::fs::create_dir_all(dir)?;
let file_path = dir.join(format!("{}.sql", name));
check_file_exists(&file_path)?;
let timestamp = chrono::Utc::now()
.format("%Y-%m-%d %H:%M:%S UTC")
.to_string();
let content = render_template(
stubs::SEED_STUB,
&[("{%name%}", name), ("{%timestamp%}", ×tamp)],
);
write_file(&file_path, &content)?;
println!("Seeder created: {}", file_path.display());
Ok(())
}
fn execute_make_guard(name: &str) -> Result<(), CliError> {
let (class_name, _module_path, file_path) = resolve_target(name, "guard");
check_file_exists(&file_path)?;
let content = format!(
"//! Guard: {class_name}\n//!\n//! 由 `sz-rust make:guard` 生成。\n//!\n//! 对齐 NestJS Guard + Spring Security 模式。\n\nuse sz_rust_core::guard::Guard;\nuse sz_rust_core::request::Request;\n\n/// {class_name} Guard\npub struct {class_name};\n\nimpl Guard for {class_name} {{\n async fn can_activate(&self, _req: &Request) -> bool {{\n // 在此实现鉴权逻辑\n true\n }}\n}}\n"
);
write_file(&file_path, &content)?;
println!("Guard created: {}", file_path.display());
Ok(())
}
fn execute_make_validate(name: &str) -> Result<(), CliError> {
let (class_name, module_path, file_path) = resolve_target(name, "validate");
check_file_exists(&file_path)?;
let namespace = format!("app::{}", module_path);
let content = render_template(
stubs::VALIDATE_STUB,
&[
("{%className%}", &class_name),
("{%namespace%}", &namespace),
],
);
write_file(&file_path, &content)?;
println!("Validator created: {}", file_path.display());
Ok(())
}
fn execute_make_event(name: &str) -> Result<(), CliError> {
let (class_name, module_path, file_path) = resolve_target(name, "event");
check_file_exists(&file_path)?;
let namespace = format!("app::{}", module_path);
let event_name = class_name.clone();
let content = render_template(
stubs::EVENT_STUB,
&[
("{%className%}", &class_name),
("{%namespace%}", &namespace),
("{%event_name%}", &event_name),
],
);
write_file(&file_path, &content)?;
println!("Event created: {}", file_path.display());
Ok(())
}
fn execute_make_listener(name: &str, event: Option<&str>) -> Result<(), CliError> {
let (class_name, module_path, file_path) = resolve_target(name, "listener");
check_file_exists(&file_path)?;
let namespace = format!("app::{}", module_path);
let event_name = event.unwrap_or(&class_name).to_string();
let content = render_template(
stubs::LISTENER_STUB,
&[
("{%className%}", &class_name),
("{%namespace%}", &namespace),
("{%event_name%}", &event_name),
],
);
write_file(&file_path, &content)?;
println!("Listener created: {}", file_path.display());
Ok(())
}
fn execute_make_command(name: &str) -> Result<(), CliError> {
let (class_name, module_path, file_path) = resolve_target(name, "command");
check_file_exists(&file_path)?;
let namespace = format!("app::{}", module_path);
let command_name = class_to_snake(&class_name);
let content = render_template(
stubs::COMMAND_STUB,
&[
("{%className%}", &class_name),
("{%namespace%}", &namespace),
("{%command_name%}", &command_name),
],
);
write_file(&file_path, &content)?;
println!("Command created: {}", file_path.display());
Ok(())
}
fn execute_make_service(name: &str) -> Result<(), CliError> {
let (class_name, module_path, file_path) = resolve_target(name, "service");
check_file_exists(&file_path)?;
let namespace = format!("app::{}", module_path);
let content = render_template(
stubs::SERVICE_STUB,
&[
("{%className%}", &class_name),
("{%namespace%}", &namespace),
],
);
write_file(&file_path, &content)?;
println!("Service created: {}", file_path.display());
Ok(())
}
fn execute_make_middleware(name: &str) -> Result<(), CliError> {
let (class_name, module_path, file_path) = resolve_target(name, "middleware");
check_file_exists(&file_path)?;
let namespace = format!("app::{}", module_path);
let content = render_template(
stubs::MIDDLEWARE_STUB,
&[
("{%className%}", &class_name),
("{%namespace%}", &namespace),
],
);
write_file(&file_path, &content)?;
println!("Middleware created: {}", file_path.display());
Ok(())
}
fn execute_make_scaffold(name: &str) -> Result<(), CliError> {
println!("Scaffolding for: {}", name);
execute_make_model(name)?;
execute_make_controller(name, false, false)?;
execute_make_migration(&class_to_snake(name), "migrations")?;
println!("Scaffold complete.");
Ok(())
}
fn resolve_target(name: &str, layer: &str) -> (String, String, PathBuf) {
let (app, class_part) = if let Some(idx) = name.find('@') {
(&name[..idx], &name[idx + 1..])
} else {
("", name)
};
let segments: Vec<&str> = class_part.split('/').collect();
let class_name = segments.last().unwrap_or(&"").to_string();
let parent_segments: Vec<&str> = if segments.len() > 1 {
segments[..segments.len() - 1].to_vec()
} else {
Vec::new()
};
let module_path = if app.is_empty() {
if parent_segments.is_empty() {
layer.to_string()
} else {
format!("{}::{}", layer, parent_segments.join("::"))
}
} else if parent_segments.is_empty() {
format!("{}::{}", app, layer)
} else {
format!("{}::{}::{}", app, layer, parent_segments.join("::"))
};
let mut path = PathBuf::from("app");
if !app.is_empty() {
path.push(app);
}
path.push(layer);
for seg in &parent_segments {
path.push(seg);
}
path.push(format!("{}.rs", class_name));
(class_name, module_path, path)
}
fn check_file_exists(path: &Path) -> Result<(), CliError> {
if path.exists() {
return Err(CliError::FileExists(path.display().to_string()));
}
Ok(())
}
fn write_file(path: &Path, content: &str) -> Result<(), CliError> {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
std::fs::write(path, content)?;
Ok(())
}
fn class_to_snake(s: &str) -> String {
let mut result = String::new();
for (i, ch) in s.chars().enumerate() {
if ch.is_uppercase() && i > 0 {
result.push('_');
}
result.push(ch.to_lowercase().next().unwrap_or(ch));
}
result
}
fn name_to_table(name: &str) -> String {
if let Some(rest) = name.strip_prefix("create_") {
return rest.to_string();
}
if let Some(rest) = name.strip_prefix("add_") {
if let Some(to_pos) = rest.find("_to_") {
return rest[to_pos + 4..].to_string();
}
return rest.to_string();
}
name.to_string()
}
#[cfg(test)]
mod tests {
use super::*;
struct CwdGuard {
original: Option<PathBuf>,
}
impl CwdGuard {
fn switch(new_dir: &Path) -> std::io::Result<Self> {
let original = std::env::current_dir().ok();
std::env::set_current_dir(new_dir)?;
Ok(Self { original })
}
}
impl Drop for CwdGuard {
fn drop(&mut self) {
if let Some(ref orig) = self.original {
let _ = std::env::set_current_dir(orig);
}
}
}
#[test]
fn test_resolve_target_simple_model() {
let (class, module, path) = resolve_target("User", "model");
assert_eq!(class, "User");
assert_eq!(module, "model");
assert_eq!(path, PathBuf::from("app/model/User.rs"));
}
#[test]
fn test_resolve_target_nested_controller() {
let (class, module, path) = resolve_target("admin/User", "controller");
assert_eq!(class, "User");
assert_eq!(module, "controller::admin");
assert_eq!(path, PathBuf::from("app/controller/admin/User.rs"));
}
#[test]
fn test_resolve_target_with_app() {
let (class, module, _path) = resolve_target("admin@User", "model");
assert_eq!(class, "User");
assert_eq!(module, "admin::model");
}
#[test]
fn test_class_to_snake() {
assert_eq!(class_to_snake("User"), "user");
assert_eq!(class_to_snake("OrderItem"), "order_item");
assert_eq!(class_to_snake("API"), "a_p_i");
}
#[test]
fn test_name_to_table_create() {
assert_eq!(name_to_table("create_users"), "users");
assert_eq!(name_to_table("create_orders"), "orders");
}
#[test]
fn test_name_to_table_add() {
assert_eq!(name_to_table("add_index_to_orders"), "orders");
assert_eq!(name_to_table("add_status"), "status");
}
#[test]
fn test_name_to_table_other() {
assert_eq!(name_to_table("custom_migration"), "custom_migration");
}
#[test]
fn test_check_file_exists_nonexistent() {
let result = check_file_exists(Path::new("/nonexistent/path/file.txt"));
assert!(result.is_ok());
}
#[test]
fn test_check_file_exists_existing() {
let temp = tempfile::NamedTempFile::new().unwrap();
let result = check_file_exists(temp.path());
assert!(matches!(result, Err(CliError::FileExists(_))));
}
#[test]
fn test_write_and_read_file() {
let temp_dir = tempfile::tempdir().unwrap();
let file_path = temp_dir.path().join("test_file.txt");
write_file(&file_path, "test content").unwrap();
assert_eq!(std::fs::read_to_string(&file_path).unwrap(), "test content");
}
#[test]
fn test_execute_make_migration_creates_files() {
let temp_dir = tempfile::tempdir().unwrap();
let path = temp_dir.path().to_str().unwrap();
execute_make_migration("create_test_table", path).unwrap();
let entries: Vec<_> = std::fs::read_dir(path).unwrap().collect();
assert_eq!(entries.len(), 2);
let mut has_up = false;
let mut has_down = false;
for entry in entries {
let name = entry.unwrap().file_name();
let name = name.to_string_lossy();
if name.ends_with("_up.sql") {
has_up = true;
}
if name.ends_with("_down.sql") {
has_down = true;
}
}
assert!(has_up);
assert!(has_down);
}
#[test]
fn test_execute_make_model_in_temp() {
let temp_dir = tempfile::tempdir().unwrap();
let _lock = super::super::test_support::acquire_global_lock();
let _guard = CwdGuard::switch(temp_dir.path()).unwrap();
execute_make_model("TestUser").unwrap();
let model_path = temp_dir.path().join("app/model/TestUser.rs");
assert!(model_path.exists());
let content = std::fs::read_to_string(&model_path).unwrap();
assert!(content.contains("TestUser"));
assert!(content.contains("test_user"));
}
#[test]
fn test_execute_make_seeder_creates_file() {
let temp_dir = tempfile::tempdir().unwrap();
let path = temp_dir.path().to_str().unwrap();
execute_make_seeder("001_test_seed", path).unwrap();
let seed_path = Path::new(path).join("001_test_seed.sql");
assert!(seed_path.exists());
let content = std::fs::read_to_string(&seed_path).unwrap();
assert!(content.contains("001_test_seed"));
assert!(content.contains("-- Seed:"));
assert!(!content.contains("{%"));
}
#[test]
fn test_execute_make_seeder_file_already_exists() {
let temp_dir = tempfile::tempdir().unwrap();
let path = temp_dir.path().to_str().unwrap();
execute_make_seeder("001_dup_seed", path).unwrap();
let result = execute_make_seeder("001_dup_seed", path);
assert!(matches!(result, Err(CliError::FileExists(_))));
}
#[test]
fn test_execute_make_seeder_creates_directory() {
let temp_dir = tempfile::tempdir().unwrap();
let nested = temp_dir.path().join("nested").join("seeds");
let path = nested.to_str().unwrap();
execute_make_seeder("001_seed", path).unwrap();
assert!(nested.exists());
}
#[test]
fn test_make_validate_creates_file() {
let temp_dir = tempfile::tempdir().unwrap();
let _lock = super::super::test_support::acquire_global_lock();
let _guard = CwdGuard::switch(temp_dir.path()).unwrap();
let cmd = MakeCommand::Validate {
name: "Order".to_string(),
};
execute(&cmd).unwrap();
let validate_path = temp_dir.path().join("app/validate/Order.rs");
assert!(validate_path.exists());
let content = std::fs::read_to_string(&validate_path).unwrap();
assert!(content.contains("pub struct OrderValidate;"));
assert!(content.contains("use sz_rust_core::validate::Validate"));
assert!(content.contains("app::validate"));
assert!(!content.contains("{%"));
}
#[test]
fn test_validate_stub_contains_required_elements() {
assert!(stubs::VALIDATE_STUB.contains("pub struct {%className%}Validate;"));
assert!(stubs::VALIDATE_STUB.contains("use sz_rust_core::validate::Validate"));
assert!(stubs::VALIDATE_STUB.contains("impl {%className%}Validate"));
assert!(stubs::VALIDATE_STUB.contains("pub fn new() -> Validate"));
assert!(stubs::VALIDATE_STUB.contains("{%className%}"));
assert!(stubs::VALIDATE_STUB.contains("{%namespace%}"));
}
#[test]
fn test_execute_make_validate_creates_file() {
let temp_dir = tempfile::tempdir().unwrap();
let _lock = super::super::test_support::acquire_global_lock();
let _guard = CwdGuard::switch(temp_dir.path()).unwrap();
execute_make_validate("User").unwrap();
let validate_path = temp_dir.path().join("app/validate/User.rs");
assert!(validate_path.exists());
let content = std::fs::read_to_string(&validate_path).unwrap();
assert!(content.contains("UserValidate"));
assert!(content.contains("use sz_rust_core::validate::Validate"));
assert!(content.contains("app::validate"));
assert!(!content.contains("{%"));
}
#[test]
fn test_execute_make_validate_file_already_exists() {
let temp_dir = tempfile::tempdir().unwrap();
let _lock = super::super::test_support::acquire_global_lock();
let _guard = CwdGuard::switch(temp_dir.path()).unwrap();
execute_make_validate("User").unwrap();
let result = execute_make_validate("User");
assert!(matches!(result, Err(CliError::FileExists(_))));
}
#[test]
fn test_execute_make_validate_nested_path() {
let temp_dir = tempfile::tempdir().unwrap();
let _lock = super::super::test_support::acquire_global_lock();
let _guard = CwdGuard::switch(temp_dir.path()).unwrap();
execute_make_validate("admin/User").unwrap();
let validate_path = temp_dir.path().join("app/validate/admin/User.rs");
assert!(validate_path.exists());
let content = std::fs::read_to_string(&validate_path).unwrap();
assert!(content.contains("UserValidate"));
assert!(content.contains("app::validate::admin"));
}
#[test]
fn test_execute_make_event_creates_file() {
let temp_dir = tempfile::tempdir().unwrap();
let _lock = super::super::test_support::acquire_global_lock();
let _guard = CwdGuard::switch(temp_dir.path()).unwrap();
execute_make_event("UserLogin").unwrap();
let event_path = temp_dir.path().join("app/event/UserLogin.rs");
assert!(event_path.exists());
let content = std::fs::read_to_string(&event_path).unwrap();
assert!(content.contains("pub struct UserLogin;"));
assert!(content.contains("app::event"));
assert!(content.contains("UserLogin"));
assert!(content.contains("use serde_json::Value"));
assert!(!content.contains("{%"));
}
#[test]
fn test_execute_make_event_file_already_exists() {
let temp_dir = tempfile::tempdir().unwrap();
let _lock = super::super::test_support::acquire_global_lock();
let _guard = CwdGuard::switch(temp_dir.path()).unwrap();
execute_make_event("UserLogin").unwrap();
let result = execute_make_event("UserLogin");
assert!(matches!(result, Err(CliError::FileExists(_))));
}
#[test]
fn test_execute_make_event_nested_path() {
let temp_dir = tempfile::tempdir().unwrap();
let _lock = super::super::test_support::acquire_global_lock();
let _guard = CwdGuard::switch(temp_dir.path()).unwrap();
execute_make_event("admin/UserLogin").unwrap();
let event_path = temp_dir.path().join("app/event/admin/UserLogin.rs");
assert!(event_path.exists());
let content = std::fs::read_to_string(&event_path).unwrap();
assert!(content.contains("app::event::admin"));
}
#[test]
fn test_execute_make_listener_default_event_name() {
let temp_dir = tempfile::tempdir().unwrap();
let _lock = super::super::test_support::acquire_global_lock();
let _guard = CwdGuard::switch(temp_dir.path()).unwrap();
execute_make_listener("SendWelcomeEmail", None).unwrap();
let listener_path = temp_dir.path().join("app/listener/SendWelcomeEmail.rs");
assert!(listener_path.exists());
let content = std::fs::read_to_string(&listener_path).unwrap();
assert!(content.contains("pub struct SendWelcomeEmail;"));
assert!(content.contains("app::listener"));
assert!(content.contains("use sz_rust_core::event::{EventError, Listener}"));
assert!(content.contains("impl Listener for SendWelcomeEmail"));
assert!(content.contains(r#""SendWelcomeEmail""#));
assert!(!content.contains("{%"));
}
#[test]
fn test_execute_make_listener_custom_event_name() {
let temp_dir = tempfile::tempdir().unwrap();
let _lock = super::super::test_support::acquire_global_lock();
let _guard = CwdGuard::switch(temp_dir.path()).unwrap();
execute_make_listener("SendWelcomeEmail", Some("UserLogin")).unwrap();
let listener_path = temp_dir.path().join("app/listener/SendWelcomeEmail.rs");
assert!(listener_path.exists());
let content = std::fs::read_to_string(&listener_path).unwrap();
assert!(content.contains(r#""UserLogin""#));
assert!(!content.contains(r#""SendWelcomeEmail""#));
assert!(!content.contains("{%"));
}
#[test]
fn test_execute_make_listener_file_already_exists() {
let temp_dir = tempfile::tempdir().unwrap();
let _lock = super::super::test_support::acquire_global_lock();
let _guard = CwdGuard::switch(temp_dir.path()).unwrap();
execute_make_listener("SendWelcomeEmail", None).unwrap();
let result = execute_make_listener("SendWelcomeEmail", None);
assert!(matches!(result, Err(CliError::FileExists(_))));
}
#[test]
fn test_execute_make_command_creates_file() {
let temp_dir = tempfile::tempdir().unwrap();
let _lock = super::super::test_support::acquire_global_lock();
let _guard = CwdGuard::switch(temp_dir.path()).unwrap();
execute_make_command("SyncData").unwrap();
let command_path = temp_dir.path().join("app/command/SyncData.rs");
assert!(command_path.exists());
let content = std::fs::read_to_string(&command_path).unwrap();
assert!(content.contains("pub struct SyncData;"));
assert!(content.contains("app::command"));
assert!(content.contains("use sz_rust_cli::console::{Command, CommandSignature}"));
assert!(content.contains("impl Command for SyncData"));
assert!(content.contains(r#""sync_data""#));
assert!(!content.contains("{%"));
}
#[test]
fn test_execute_make_command_file_already_exists() {
let temp_dir = tempfile::tempdir().unwrap();
let _lock = super::super::test_support::acquire_global_lock();
let _guard = CwdGuard::switch(temp_dir.path()).unwrap();
execute_make_command("SyncData").unwrap();
let result = execute_make_command("SyncData");
assert!(matches!(result, Err(CliError::FileExists(_))));
}
#[test]
fn test_execute_make_command_nested_path() {
let temp_dir = tempfile::tempdir().unwrap();
let _lock = super::super::test_support::acquire_global_lock();
let _guard = CwdGuard::switch(temp_dir.path()).unwrap();
execute_make_command("admin/SyncData").unwrap();
let command_path = temp_dir.path().join("app/command/admin/SyncData.rs");
assert!(command_path.exists());
let content = std::fs::read_to_string(&command_path).unwrap();
assert!(content.contains("app::command::admin"));
}
#[test]
fn test_execute_make_service_creates_file() {
let temp_dir = tempfile::tempdir().unwrap();
let _lock = super::super::test_support::acquire_global_lock();
let _guard = CwdGuard::switch(temp_dir.path()).unwrap();
execute_make_service("UserService").unwrap();
let service_path = temp_dir.path().join("app/service/UserService.rs");
assert!(service_path.exists());
let content = std::fs::read_to_string(&service_path).unwrap();
assert!(content.contains("pub struct UserService;"));
assert!(content.contains("app::service"));
assert!(content.contains("impl Default for UserService"));
assert!(content.contains("pub fn new() -> Self"));
assert!(!content.contains("{%"));
}
#[test]
fn test_execute_make_service_file_already_exists() {
let temp_dir = tempfile::tempdir().unwrap();
let _lock = super::super::test_support::acquire_global_lock();
let _guard = CwdGuard::switch(temp_dir.path()).unwrap();
execute_make_service("UserService").unwrap();
let result = execute_make_service("UserService");
assert!(matches!(result, Err(CliError::FileExists(_))));
}
#[test]
fn test_execute_make_service_nested_path() {
let temp_dir = tempfile::tempdir().unwrap();
let _lock = super::super::test_support::acquire_global_lock();
let _guard = CwdGuard::switch(temp_dir.path()).unwrap();
execute_make_service("admin/UserService").unwrap();
let service_path = temp_dir.path().join("app/service/admin/UserService.rs");
assert!(service_path.exists());
let content = std::fs::read_to_string(&service_path).unwrap();
assert!(content.contains("app::service::admin"));
}
}