russel 0.0.2

A simple static site builder I built for myself.
Documentation
//! Contains the build command execution logic
use std::fs;
use std::path::Path;

use crate::error::{Result, RussError};

/// Executes the 'build' command which builds the project
pub fn execute() -> Result<()> {
    println!("Building the project...");

    // Define source and destination paths
    let source_dir = Path::new("./content");
    let dest_dir = Path::new("./site");

    // Check if source directory exists
    if !source_dir.exists() {
        return Err(RussError::build(format!(
            "Source directory '{}' does not exist",
            source_dir.display()
        )));
    }

    // Clean the destination directory if it exists
    if dest_dir.exists() {
        println!("Removing existing site directory...");
        fs::remove_dir_all(dest_dir).map_err(|err| {
            RussError::build_with_source(
                format!(
                    "Failed to remove destination directory '{}'",
                    dest_dir.display()
                ),
                err,
            )
        })?;
    }

    // Create the destination directory
    println!("Creating site directory...");
    fs::create_dir_all(dest_dir).map_err(|err| {
        RussError::build_with_source(
            format!(
                "Failed to create destination directory '{}'",
                dest_dir.display()
            ),
            err,
        )
    })?;

    // Copy all content from source to destination
    println!("Copying content to site directory...");
    copy_dir_all(source_dir, dest_dir)?;

    println!("Build completed successfully!");
    Ok(())
}

/// Recursively copy a directory and all its contents
fn copy_dir_all(src: &Path, dst: &Path) -> Result<()> {
    if !src.is_dir() {
        return Err(RussError::build(format!(
            "Source '{}' is not a directory",
            src.display()
        )));
    }

    for entry_result in fs::read_dir(src).map_err(|err| {
        RussError::build_with_source(format!("Failed to read directory '{}'", src.display()), err)
    })? {
        let entry = entry_result.map_err(|err| {
            RussError::build_with_source(
                format!("Failed to read directory entry in '{}'", src.display()),
                err,
            )
        })?;

        let file_type = entry.file_type().map_err(|err| {
            RussError::build_with_source(
                format!(
                    "Failed to determine file type for '{}'",
                    entry.path().display()
                ),
                err,
            )
        })?;

        let src_path = entry.path();
        let dst_path = dst.join(entry.file_name());

        if file_type.is_dir() {
            // Create the destination directory
            fs::create_dir_all(&dst_path).map_err(|err| {
                RussError::build_with_source(
                    format!("Failed to create directory '{}'", dst_path.display()),
                    err,
                )
            })?;
            // Recursively copy the directory contents
            copy_dir_all(&src_path, &dst_path)?;
        } else if file_type.is_file() {
            // Copy the file
            fs::copy(&src_path, &dst_path).map_err(|err| {
                RussError::build_with_source(
                    format!(
                        "Failed to copy file from '{}' to '{}'",
                        src_path.display(),
                        dst_path.display()
                    ),
                    err,
                )
            })?;
        }
        // Skip other file types like symlinks
    }

    Ok(())
}