use crate::core::ir::{DefaultValue, FieldDef, TypeDef, TypeRef};
use super::shared::{constructor_fields, default_value_for_field, use_unwrap_or_default};
const MAGNUS_MAX_ARITY: usize = 15;
pub fn gen_magnus_kwargs_constructor(typ: &TypeDef, type_mapper: &dyn Fn(&TypeRef) -> String) -> String {
let _ = MAGNUS_MAX_ARITY;
gen_magnus_hash_constructor(typ, type_mapper)
}
fn as_type_path_prefix(type_str: &str) -> String {
if type_str.contains('<') {
format!("<{type_str}>")
} else {
type_str.to_string()
}
}
fn gen_magnus_hash_constructor(typ: &TypeDef, type_mapper: &dyn Fn(&TypeRef) -> String) -> String {
let fields: Vec<_> = constructor_fields(typ)
.map(|field| {
let is_optional = field_is_optional_in_rust(field);
let effective_inner_ty = match &field.ty {
TypeRef::Optional(inner) if is_optional => inner.as_ref(),
ty => ty,
};
let inner_type = type_mapper(effective_inner_ty);
let type_prefix = as_type_path_prefix(&inner_type);
let assignment = if is_optional {
format!(
"kwargs.get(ruby.to_symbol(\"{}\")).and_then(|v| {}::try_convert(v).ok()),",
field.name, type_prefix
)
} else if use_unwrap_or_default(field) {
format!(
"kwargs.get(ruby.to_symbol(\"{}\")).and_then(|v| {}::try_convert(v).ok()).unwrap_or_default(),",
field.name, type_prefix
)
} else if matches!(effective_inner_ty, TypeRef::Named(_))
&& !matches!(&field.typed_default, Some(DefaultValue::EnumVariant(_)))
{
format!(
"kwargs.get(ruby.to_symbol(\"{}\")).and_then(|v| {}::try_convert(v).ok()).ok_or_else(|| magnus::Error::new(unsafe {{ magnus::Ruby::get_unchecked() }}.exception_arg_error(), \"missing required field: {}\"))?,",
field.name, type_prefix, field.name
)
} else {
let default_str = if inner_type == "String" {
if let Some(DefaultValue::EnumVariant(variant)) = &field.typed_default {
use heck::ToSnakeCase;
format!("\"{}\".to_string()", variant.to_snake_case())
} else {
default_value_for_field(field, "rust")
}
} else {
default_value_for_field(field, "rust")
};
format!(
"kwargs.get(ruby.to_symbol(\"{}\")).and_then(|v| {}::try_convert(v).ok()).unwrap_or({}),",
field.name, type_prefix, default_str
)
};
minijinja::context! {
name => field.name.clone(),
assignment => assignment,
}
})
.collect();
crate::codegen::template_env::render(
"config_gen/magnus_hash_constructor.jinja",
minijinja::context! {
fields => fields,
},
)
}
fn field_is_optional_in_rust(field: &FieldDef) -> bool {
field.optional || matches!(&field.ty, TypeRef::Optional(_))
}