cargo-setupx 0.1.0

Rust-based CLI and library that automates the initial setup of new Rust projects with modular configuration packs
Documentation
//! Architecture pack - scaffolds project structure based on patterns

use crate::error::{Result, SetupError};
use crate::utils::{create_dir, write_file};
use std::path::Path;

/// Apply the architecture pack to the project
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())),
    }
}

/// Apply Clean Architecture scaffolding
fn apply_clean_architecture(project_path: &Path, force: bool) -> Result<()> {
    println!("🏗️  Applying Clean Architecture Pack...");

    let src_dir = project_path.join("src");

    // Create domain layer
    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,
    )?;

    // Create application layer
    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,
    )?;

    // Create infrastructure layer
    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,
    )?;

    // Create presentation layer
    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();

        // Create src directory first
        std::fs::create_dir_all(project_path.join("src")).unwrap();

        apply(project_path, "clean", false).unwrap();

        // Check domain layer
        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());

        // Check application layer
        assert!(project_path.join("src/application").exists());
        assert!(project_path.join("src/application/dto").exists());
        assert!(project_path.join("src/application/use_cases").exists());

        // Check infrastructure layer
        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());

        // Check presentation layer
        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());
    }
}