arcature-cli 2026.2.0

Developer lifecycle CLI for Arcature applications.
Documentation
//! `arc make job <name> --module <module>` — generate a job struct + handler.
//!
//! Creates `src/<module>/<name>_job.rs` with a `#[derive(Job)]` struct and a
//! `#[job_handler]` async function, and appends `pub mod <name>_job;` to the
//! module's `mod.rs`. The job name is PascalCase (e.g. `SendEmail`).

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};

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 job_name = name.clone();
    let stem = format!("{}_job", snake_case(&name));
    let path = module_file_path(root, &module, &format!("{stem}.rs"))?;
    let content = render(&job_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(job_name: &str, module: &str) -> String {
    let handler_name = format!("handle_{}", snake_case(job_name));
    format!(
        "//! `{job_name}` — job for the `{module}` module.\n\
         \n\
         /// A typed job. `#[derive(Job)]` generates `DxComponent`, `Job`, and a\n\
         /// `JobModel` const. Add the payload fields your handler needs.\n\
         #[derive(Job, serde::Serialize, serde::Deserialize, Debug, Clone)]\n\
         pub struct {job_name} {{\n\
         \x20   // Add job payload fields here.\n\
         }}\n\
         \n\
         /// The job handler. The `#[job_handler]` macro validates the signature\n\
         /// and generates a `JobBinding` const for `arc check`.\n\
         #[arcature::job_handler]\n\
         pub async fn {handler_name}(job: {job_name}) -> Result<(), ()> {{\n\
         \x20   let _ = job;\n\
         \x20   Ok(())\n\
         }}\n"
    )
}