use crate::error::{Result, SetupError};
use crate::utils::{create_dir, write_file};
use std::path::Path;
pub fn apply(project_path: &Path, arch_type: &str, force: bool) -> Result<()> {
match arch_type {
"clean" => apply_clean_architecture(project_path, force),
_ => Err(SetupError::InvalidArchitecture(arch_type.to_string())),
}
}
fn apply_clean_architecture(project_path: &Path, force: bool) -> Result<()> {
println!("🏗️ Applying Clean Architecture Pack...");
let src_dir = project_path.join("src");
let domain_dir = src_dir.join("domain");
create_dir(&domain_dir)?;
create_dir(&domain_dir.join("entities"))?;
create_dir(&domain_dir.join("repositories"))?;
create_dir(&domain_dir.join("services"))?;
write_file(&domain_dir.join("mod.rs"), DOMAIN_MOD_RS, force)?;
write_file(
&domain_dir.join("entities").join("mod.rs"),
"//! Domain entities\n",
force,
)?;
write_file(
&domain_dir.join("repositories").join("mod.rs"),
"//! Repository trait definitions\n",
force,
)?;
write_file(
&domain_dir.join("services").join("mod.rs"),
"//! Domain services\n",
force,
)?;
let application_dir = src_dir.join("application");
create_dir(&application_dir)?;
create_dir(&application_dir.join("dto"))?;
create_dir(&application_dir.join("use_cases"))?;
write_file(&application_dir.join("mod.rs"), APPLICATION_MOD_RS, force)?;
write_file(
&application_dir.join("dto").join("mod.rs"),
"//! Data Transfer Objects (DTOs)\n",
force,
)?;
write_file(
&application_dir.join("use_cases").join("mod.rs"),
"//! Application use cases\n",
force,
)?;
let infrastructure_dir = src_dir.join("infrastructure");
create_dir(&infrastructure_dir)?;
create_dir(&infrastructure_dir.join("database"))?;
create_dir(&infrastructure_dir.join("config"))?;
create_dir(&infrastructure_dir.join("http"))?;
write_file(
&infrastructure_dir.join("mod.rs"),
INFRASTRUCTURE_MOD_RS,
force,
)?;
write_file(
&infrastructure_dir.join("database").join("mod.rs"),
"//! Database implementations\n",
force,
)?;
write_file(
&infrastructure_dir.join("config").join("mod.rs"),
"//! Configuration management\n",
force,
)?;
write_file(
&infrastructure_dir.join("http").join("mod.rs"),
"//! HTTP infrastructure\n",
force,
)?;
let presentation_dir = src_dir.join("presentation");
create_dir(&presentation_dir)?;
create_dir(&presentation_dir.join("handlers"))?;
write_file(&presentation_dir.join("mod.rs"), PRESENTATION_MOD_RS, force)?;
write_file(
&presentation_dir.join("handlers").join("mod.rs"),
"//! HTTP handlers\n",
force,
)?;
println!();
Ok(())
}
const DOMAIN_MOD_RS: &str = r#"//! Domain layer
//!
//! This layer contains the core business logic and entities.
//! It has NO dependencies on outer layers.
pub mod entities;
pub mod repositories;
pub mod services;
"#;
const APPLICATION_MOD_RS: &str = r#"//! Application layer
//!
//! This layer contains use cases and application-specific business rules.
//! It orchestrates the flow of data between the domain and presentation layers.
pub mod dto;
pub mod use_cases;
"#;
const INFRASTRUCTURE_MOD_RS: &str = r#"//! Infrastructure layer
//!
//! This layer contains implementations for external concerns:
//! - Database access
//! - External APIs
//! - File systems
//! - Configuration
pub mod config;
pub mod database;
pub mod http;
"#;
const PRESENTATION_MOD_RS: &str = r#"//! Presentation layer
//!
//! This layer handles HTTP requests and responses.
//! It depends on the application layer for business logic.
pub mod handlers;
"#;
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
#[test]
fn test_clean_architecture_creates_structure() {
let temp_dir = tempdir().unwrap();
let project_path = temp_dir.path();
std::fs::create_dir_all(project_path.join("src")).unwrap();
apply(project_path, "clean", false).unwrap();
assert!(project_path.join("src/domain").exists());
assert!(project_path.join("src/domain/entities").exists());
assert!(project_path.join("src/domain/repositories").exists());
assert!(project_path.join("src/domain/services").exists());
assert!(project_path.join("src/application").exists());
assert!(project_path.join("src/application/dto").exists());
assert!(project_path.join("src/application/use_cases").exists());
assert!(project_path.join("src/infrastructure").exists());
assert!(project_path.join("src/infrastructure/database").exists());
assert!(project_path.join("src/infrastructure/config").exists());
assert!(project_path.join("src/infrastructure/http").exists());
assert!(project_path.join("src/presentation").exists());
assert!(project_path.join("src/presentation/handlers").exists());
}
#[test]
fn test_invalid_architecture_returns_error() {
let temp_dir = tempdir().unwrap();
let project_path = temp_dir.path();
let result = apply(project_path, "invalid", false);
assert!(result.is_err());
}
}