use super::php::{classes, error_info};
use super::shared::{error_variant_wildcard_pattern, python_exception_name, to_snake_case};
use super::{ExceptionNaming, exception_lineage, shared_exception_classes};
use crate::core::ir::{ApiSurface, ErrorDef};
fn rust_path_of(error: &ErrorDef, core_import: &str) -> String {
if error.rust_path.is_empty() {
format!("{core_import}::{}", error.name)
} else {
error.rust_path.replace('-', "_")
}
}
pub fn gen_magnus_error_class_registrations(api: &ApiSurface) -> anyhow::Result<Vec<String>> {
let mut lines = Vec::new();
for class in classes(api, "", None)? {
let var = format!("{}_class", to_snake_case(&class.name));
let parent = match &class.parent {
Some(parent) => format!("{}_class", to_snake_case(parent)),
None => "ruby.exception_standard_error()".to_string(),
};
lines.push(format!(
" let {var} = module.define_error(\"{}\", {parent})?;",
class.name
));
for field in &class.info {
lines.push(format!(
" {var}.define_attr(\"{}\", magnus::Attr::Read)?;",
field.name
));
if field.name == "is_transient" {
lines.push(format!(" {var}.define_alias(\"transient?\", \"is_transient\")?;"));
}
}
}
let shared = shared_exception_classes(&api.errors, ExceptionNaming::Python);
let mut families: Vec<Vec<String>> = api
.errors
.iter()
.map(|error| exception_lineage(&api.errors, &error.name))
.collect();
families.sort_by_key(Vec::len);
for lineage in &families {
lines.push(format!(
" module.funcall::<_, _, magnus::Value>(\"module_eval\", ({:?},))?;",
ruby_family_module(&lineage[0], lineage.get(1).map(String::as_str))
));
}
for (class, members) in &shared {
for family in members.families.iter().skip(1) {
lines.push(format!(
" module.funcall::<_, _, magnus::Value>(\"module_eval\", ({:?},))?;",
format!("{class}.include({family}::Family)")
));
}
}
Ok(lines)
}
fn ruby_family_module(family: &str, parent: Option<&str>) -> String {
let module = parent.map_or_else(
|| "module Family; end".to_string(),
|parent| format!("module Family; include {parent}::Family; end"),
);
format!(
"class {family}\n {module}\n include Family\n def self.===(other)\n \
super || (equal?({family}) && Family === other)\n end\nend"
)
}
pub fn gen_magnus_error_converter(error: &ErrorDef, core_import: &str, module_name: &str) -> String {
let rust_path = rust_path_of(error, core_import);
let variants = error
.variants
.iter()
.map(|variant| {
crate::alef_context! {
pattern => error_variant_wildcard_pattern(&rust_path, variant),
class_name => python_exception_name(&variant.name, &error.name),
}
})
.collect::<Vec<_>>();
crate::codegen::template_env::render(
"error_gen/magnus_error_converter.jinja",
crate::alef_context! {
rust_path => rust_path,
fn_name => format!("{}_to_magnus_err", to_snake_case(&error.name)),
module_name => module_name,
base_class => error.name.as_str(),
info => error_info(error),
variants => variants,
},
)
}
pub fn gen_magnus_error_dispatch(api: &ApiSurface, core_import: &str) -> String {
let arms = api
.errors
.iter()
.map(|error| {
crate::alef_context! {
rust_path => rust_path_of(error, core_import),
fn_name => format!("{}_to_magnus_err", to_snake_case(&error.name)),
}
})
.collect::<Vec<_>>();
crate::codegen::template_env::render(
"error_gen/magnus_error_dispatch.jinja",
crate::alef_context! { arms => arms },
)
}