auric 0.1.6

CLI for the Ember-inspired Auric SPA framework
use clap::Parser;
use std::path::PathBuf;
use std::{fs, path::Path};

#[derive(Debug, Parser)]
pub struct Opts {
    /// Route Name
    #[arg(value_name = "name")]
    pub name: String,
}

pub async fn run(name: String) -> anyhow::Result<()> {
    // Delete route module file
    let routes_dir = PathBuf::new().join("src").join("routes");
    let (modulename, filename, dir) = {
        let s = change_case::snake_case(&name).replace("_", "/");
        let filename = format!("{s}.rs");
        let modulename = match s.rfind("/") {
            None => s,
            Some(n) => s[n + 1..].to_owned(),
        };
        let dir = routes_dir
            .join(&filename)
            .parent()
            .expect("Expected to get parent dir")
            .to_owned();
        (modulename, filename, dir)
    };
    let relative_path = routes_dir.join(filename);
    let root_dir = Path::new(".");
    let path = root_dir.join(&relative_path);
    if path.exists() {
        fs::remove_file(&path)?;
        eprintln!("deleted {}", relative_path.display());
    }

    // Remove reference to it from the parent module
    let relative_path = dir.join("mod.rs");
    let path = root_dir.join(&relative_path);
    let code = fs::read_to_string(&path)?;
    let new_code = code.replace(&format!("pub mod {modulename};\n"), "");
    if code != new_code {
        fs::write(&path, new_code)?;
        eprintln!("updated {}", relative_path.display());
    }

    // When the parent module no longer references any routes, collapse the directory
    // (except leave the root-level src/routes/mod.rs)
    let num_routes = {
        let mut n = 0;
        for entry in fs::read_dir(&dir)? {
            let entry = entry?;
            if !entry.file_type()?.is_file() {
                continue;
            }
            if entry.file_name() == "mod.rs" {
                continue;
            }
            n += 1;
        }
        n
    };
    if num_routes == 0 && name.contains(".") {
        // Delete directory
        fs::remove_dir_all(&dir)?;
        eprintln!("deleted {}/", dir.display());

        // Remove reference to it from the parent module
        let modulename = dir
            .file_name()
            .expect("Expected to get filename")
            .to_str()
            .expect("Expected to get filename as a string");
        let relative_path = dir.parent().expect("Expected to get parent dir").join("mod.rs");
        let path = root_dir.join(&relative_path);
        let code = fs::read_to_string(&path)?;
        let new_code = code.replace(&format!("pub mod {modulename};\n"), "");
        if code != new_code {
            fs::write(&path, new_code)?;
            eprintln!("updated {}", relative_path.display());
        }
    }

    // Delete template
    super::template::run(name).await?;

    Ok(())
}