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"
)
}