arcature-cli 2026.2.0

Developer lifecycle CLI for Arcature applications.
Documentation
//! `arc make controller <name> --module <module>` — generate a controller file.
//!
//! Creates `src/<module>/<name>_controller.rs` with a `#[controller]` impl
//! block, and appends `pub mod <name>_controller;` to the module's `mod.rs`.
//! The controller name is PascalCase (e.g. `Links`); the file stem is
//! snake_case (e.g. `links_controller`).

use std::path::Path;

use super::naming::{
    append_mod_declaration, mod_declaration, module_file_path, snake_case, validate_module_name,
    validate_type_name,
};
use super::write::{GeneratedFile, module_mod_rs};

/// Generate a controller in `src/<module>/<name>_controller.rs` and return both
/// the generated file and the `mod.rs` update message (the caller writes the
/// file, then applies the `mod.rs` update).
pub(crate) fn generate(
    root: &Path,
    raw_name: &str,
    raw_module: &str,
) -> Result<(GeneratedFile, String), String> {
    let name = validate_type_name(raw_name)?;
    let module = validate_module_name(raw_module)?;
    let controller_name = if name.ends_with("Controller") {
        name.clone()
    } else {
        format!("{name}Controller")
    };
    let stem = format!("{}_controller", snake_case(&name));
    let path = module_file_path(root, &module, &format!("{stem}.rs"))?;
    let content = render(&controller_name, &module);
    let file = GeneratedFile {
        path: path.clone(),
        content,
    };
    let mod_rs = module_mod_rs(root, &module);
    let declaration = mod_declaration(&stem)?;
    let mod_msg = append_mod_declaration(&mod_rs, &declaration)?;
    Ok((file, mod_msg))
}

fn render(controller_name: &str, module: &str) -> String {
    format!(
        "//! `{controller_name}` — controller for the `{module}` module.\n\
         \n\
         use arcature::prelude::*;\n\
         \n\
         /// Controller for the `{module}` module. Add handler methods here; the\n\
         /// `#[controller]` macro generates the metadata const for `arc routes`.\n\
         #[arcature::arcature_dx::controller]\n\
         impl {controller_name} {{\n\
         \x20   /// Example handler. Replace with your route handlers.\n\
         \x20   pub async fn index() -> Result<Json<&'static str>, std::convert::Infallible> {{\n\
         \x20       Ok(Json(\"{controller_name}\"))\n\
         \x20   }}\n\
         }}\n"
    )
}