use crate::codegen::cfg::is_host_owned_rust_path;
use crate::codegen::conversions::helpers::is_tuple_variant;
use crate::codegen::generators::type_paths::resolve_type_path;
use crate::core::ir::{EnumDef, EnumVariant};
use std::collections::HashMap;
fn emit_cfg_gated_arm(
enum_def: &EnumDef,
variant: &EnumVariant,
is_host_enum: bool,
pattern: &str,
expression: &str,
direction: &str,
) -> Option<String> {
if variant.cfg.is_some() && !is_host_enum {
tracing::debug!(
enum_name = %enum_def.name,
enum_rust_path = %enum_def.rust_path,
variant_name = %variant.name,
cfg = variant.cfg.as_deref().unwrap_or_default(),
direction = direction,
"dropping extendr enum conversion match arm for a foreign-crate variant behind a \
#[cfg(...)] this generated crate cannot declare as a Cargo feature; the variant is \
unreachable from this conversion"
);
return None;
}
Some(crate::backends::extendr::template_env::render(
format!("enum_from_{direction}_arm.jinja").as_str(),
minijinja::context! {
pattern => pattern,
expression => expression,
cfg => variant.cfg.as_deref(),
},
))
}
pub(super) fn gen_from_binding_to_core(
enum_def: &EnumDef,
core_import: &str,
type_paths: &HashMap<String, String>,
configured_features: Option<&[String]>,
) -> String {
let core_path = resolve_type_path(&enum_def.name, core_import, type_paths);
let binding_name = enum_def.name.as_str();
let is_host_enum = is_host_owned_rust_path(core_import, &enum_def.rust_path);
let arms: Vec<String> = enum_def
.variants
.iter()
.filter_map(|variant| {
let pattern = binding_pattern(binding_name, variant);
let expression = core_expression(variant);
emit_cfg_gated_arm(
enum_def,
variant,
is_host_enum,
&pattern,
&expression,
"binding_to_core",
)
})
.collect();
let catch_all = catch_all(enum_def, is_host_enum, configured_features, true).then(|| {
crate::backends::extendr::template_env::render(
"enum_from_binding_to_core_catch_all.jinja",
minijinja::context! {},
)
});
crate::backends::extendr::template_env::render(
"enum_from_binding_to_core_impl.jinja",
minijinja::context! {
binding_name => binding_name,
core_path => core_path,
arms => arms,
catch_all => catch_all,
},
)
}
pub(super) fn gen_from_core_to_binding(
enum_def: &EnumDef,
core_import: &str,
type_paths: &HashMap<String, String>,
configured_features: Option<&[String]>,
) -> String {
let core_path = resolve_type_path(&enum_def.name, core_import, type_paths);
let binding_name = enum_def.name.as_str();
let is_host_enum = is_host_owned_rust_path(core_import, &enum_def.rust_path);
let arms: Vec<String> = enum_def
.variants
.iter()
.filter_map(|variant| {
let pattern = core_pattern(&core_path, variant);
let expression = binding_expression(variant);
emit_cfg_gated_arm(
enum_def,
variant,
is_host_enum,
&pattern,
&expression,
"core_to_binding",
)
})
.collect();
let catch_all = catch_all(enum_def, is_host_enum, configured_features, true).then(|| {
crate::backends::extendr::template_env::render(
"enum_from_core_to_binding_catch_all.jinja",
minijinja::context! {},
)
});
crate::backends::extendr::template_env::render(
"enum_from_core_to_binding_impl.jinja",
minijinja::context! {
binding_name => binding_name,
core_path => core_path,
arms => arms,
catch_all => catch_all,
},
)
}
fn catch_all(
enum_def: &EnumDef,
is_host_enum: bool,
configured_features: Option<&[String]>,
declaration_may_drop_variant: bool,
) -> bool {
let has_excluded_variants = !enum_def.excluded_variants.is_empty();
let core_has_struct_variants = enum_def
.variants
.iter()
.any(|variant| !variant.fields.is_empty() && !variant.is_tuple);
let has_any_data_variants = enum_def.variants.iter().any(|v| !v.fields.is_empty());
crate::codegen::conversions::enum_conversion_needs_catch_all_for_features(
enum_def,
is_host_enum,
has_excluded_variants,
configured_features,
declaration_may_drop_variant,
) || core_has_struct_variants
|| has_any_data_variants
}
fn binding_pattern(binding_name: &str, variant: &EnumVariant) -> String {
format!("{binding_name}::{}", variant.name)
}
fn core_pattern(core_path: &str, variant: &EnumVariant) -> String {
if variant.fields.is_empty() {
format!("{core_path}::{}", variant.name)
} else if is_tuple_variant(&variant.fields) {
format!("{core_path}::{}(..)", variant.name)
} else {
format!("{core_path}::{} {{ .. }}", variant.name)
}
}
fn core_expression(variant: &EnumVariant) -> String {
if variant.fields.is_empty() {
format!("Self::{}", variant.name)
} else if is_tuple_variant(&variant.fields) {
let defaults = variant
.fields
.iter()
.map(|_| "Default::default()")
.collect::<Vec<_>>()
.join(", ");
format!("Self::{}({defaults})", variant.name)
} else {
let defaults = variant
.fields
.iter()
.map(|field| format!("{}: Default::default()", field.name))
.collect::<Vec<_>>()
.join(", ");
format!("Self::{} {{ {defaults} }}", variant.name)
}
}
fn binding_expression(variant: &EnumVariant) -> String {
format!("Self::{}", variant.name)
}