arcature-cli 2026.1.1

Developer lifecycle CLI for Arcature applications.
Documentation
//! `arc make policy <name> --module <module>` — generate a policy struct.
//!
//! Creates `src/<module>/<name>_policy.rs` with a `#[policy(<Model>)]` struct
//! and a stub `Policy<...>` impl signature, and appends `pub mod
//! <name>_policy;` to the module's `mod.rs`. The policy name is PascalCase
//! (e.g. `Link`); the file stem is snake_case (e.g. `link_policy`).
//!
//! Per AGENTS.md §"No fake implementations": the generator does NOT fill in
//! the `check` body — it leaves a `todo!()`-free stub that the developer
//! completes. Authorization logic is business behavior the macro must not
//! guess (A9 §"authorization logic is business behavior").

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 policy_name = if name.ends_with("Policy") {
        name.clone()
    } else {
        format!("{name}Policy")
    };
    let model = name.trim_end_matches("Policy").to_owned();
    let stem = format!("{}_policy", snake_case(&model));
    let path = module_file_path(root, &module, &format!("{stem}.rs"))?;
    let content = render(&policy_name, &model, &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(policy_name: &str, model: &str, module: &str) -> String {
    format!(
        "//! `{policy_name}` — authorization policy for `{model}` in `{module}`.\n\
         \n\
         use arcature::Policy;\n\
         \n\
         /// Policy for `{model}`. The `#[policy]` macro generates `impl DxComponent`.\n\
         /// Implement `Policy<{model}>` by hand — the authorization logic is\n\
         /// business behavior the macro must not generate.\n\
         #[arcature::policy({model})]\n\
         pub struct {policy_name};\n\
         \n\
         /// Define the user type for this policy, then implement `check`.\n\
         /// Replace `YourUser` with the application's authenticated user type.\n\
         impl Policy<{model}> for {policy_name} {{\n\
         \x20   type User = YourUser;\n\
         \x20   fn check(_user: &Self::User, _action: &str, _model: &{model}) -> bool {{\n\
         \x20       false // deny by default until the developer authorizes\n\
         \x20   }}\n\
         }}\n"
    )
}