use crate::codegen::cfg::is_host_owned_rust_path;
use crate::codegen::conversions::{VariantDeclaration, enum_variant_declaration};
use crate::core::ir::{EnumDef, EnumVariant, FieldDef, TypeRef};
use std::collections::HashSet;
pub fn gen_enum(enum_def: &EnumDef, core_import: &str, configured_features: Option<&[String]>) -> String {
let is_host_enum = is_host_owned_rust_path(core_import, &enum_def.rust_path);
let configured_features_set: Option<HashSet<&str>> =
configured_features.map(|features| features.iter().map(String::as_str).collect());
let declared_variants: Vec<&EnumVariant> = enum_def
.variants
.iter()
.filter(|v| {
!matches!(
enum_variant_declaration(v, is_host_enum, configured_features_set.as_ref()),
VariantDeclaration::Drop
)
})
.collect();
let has_data = declared_variants.iter().any(|v| !v.fields.is_empty());
let first_variant = declared_variants.first().map(|v| v.name.as_str()).unwrap_or("Default");
let default = declared_variants
.iter()
.find(|v| v.is_default)
.or(declared_variants.first());
let default_variant = default.map(|v| v.name.as_str()).unwrap_or(first_variant);
let first_variant_default = if has_data {
match default {
Some(default) if !default.fields.is_empty() => {
if emits_tuple_variant(enum_def, default) {
let field_defaults: Vec<&str> = default.fields.iter().map(|_| "Default::default()").collect();
format!("({})", field_defaults.join(", "))
} else {
let field_defaults: Vec<String> = default
.fields
.iter()
.map(|f| format!("{}: Default::default()", f.name))
.collect();
format!(" {{ {} }}", field_defaults.join(", "))
}
}
_ => String::new(),
}
} else {
String::new()
};
let variants: Vec<minijinja::Value> = declared_variants
.iter()
.map(|variant| {
let fields: Vec<minijinja::Value> = variant
.fields
.iter()
.map(|f| {
minijinja::context! {
name => &f.name,
field_type => field_type_for_serde(f),
}
})
.collect();
let snake_name = crate::codegen::naming::pascal_to_snake(&variant.name);
let wire_name = crate::codegen::naming::wire_variant_value(
&variant.name,
variant.serde_rename.as_deref(),
enum_def.serde_rename_all.as_deref(),
);
minijinja::context! {
name => &variant.name,
serde_rename => &variant.serde_rename,
fields => &fields,
is_tuple => variant.is_tuple,
emits_as_tuple => emits_tuple_variant(enum_def, variant),
snake_name => &snake_name,
wire_name => &wire_name,
accepted_input_values => accepted_unit_variant_input_spellings(&variant.name, &snake_name, &wire_name),
}
})
.collect();
crate::backends::magnus::template_env::render(
"enum_magnus.rs.jinja",
minijinja::context! {
enum_name => &enum_def.name,
has_data => has_data,
serde_tag => &enum_def.serde_tag,
serde_content => &enum_def.serde_content,
serde_untagged => enum_def.serde_untagged,
serde_rename_all => &enum_def.serde_rename_all,
variants => &variants,
first_variant => first_variant,
default_variant => default_variant,
first_variant_default => &first_variant_default,
},
)
}
fn accepted_unit_variant_input_spellings(variant_name: &str, snake_name: &str, wire_name: &str) -> Vec<String> {
let mut spellings = vec![wire_name.to_string()];
for candidate in [snake_name, variant_name] {
if !spellings.iter().any(|existing| existing == candidate) {
spellings.push(candidate.to_string());
}
}
spellings
}
fn emits_tuple_variant(enum_def: &EnumDef, variant: &crate::core::ir::EnumVariant) -> bool {
crate::codegen::conversions::helpers::variant_emits_tuple_form(enum_def, variant)
}
fn field_type_for_serde_inner(ty: &TypeRef) -> String {
use crate::core::ir::PrimitiveType;
match ty {
TypeRef::String | TypeRef::Char | TypeRef::Path => "String".to_string(),
TypeRef::Primitive(PrimitiveType::Bool) => "bool".to_string(),
TypeRef::Primitive(PrimitiveType::U8) => "u8".to_string(),
TypeRef::Primitive(PrimitiveType::U16) => "u16".to_string(),
TypeRef::Primitive(PrimitiveType::U32) => "u32".to_string(),
TypeRef::Primitive(PrimitiveType::U64) => "u64".to_string(),
TypeRef::Primitive(PrimitiveType::Usize) => "usize".to_string(),
TypeRef::Primitive(PrimitiveType::I8) => "i8".to_string(),
TypeRef::Primitive(PrimitiveType::I16) => "i16".to_string(),
TypeRef::Primitive(PrimitiveType::I32) => "i32".to_string(),
TypeRef::Primitive(PrimitiveType::I64) => "i64".to_string(),
TypeRef::Primitive(PrimitiveType::Isize) => "isize".to_string(),
TypeRef::Primitive(PrimitiveType::F32) => "f32".to_string(),
TypeRef::Primitive(PrimitiveType::F64) => "f64".to_string(),
TypeRef::Duration => "u64".to_string(),
TypeRef::Bytes => "Vec<u8>".to_string(),
TypeRef::Named(n) => n.clone(),
TypeRef::Vec(inner) => format!("Vec<{}>", field_type_for_serde_inner(inner)),
TypeRef::Map(_, _) => "String".to_string(),
TypeRef::Optional(inner) => format!("Option<{}>", field_type_for_serde_inner(inner)),
_ => "String".to_string(),
}
}
pub(super) fn field_type_for_serde(field: &FieldDef) -> String {
serde_field_type(&field.ty, field.optional)
}
pub(super) fn serde_field_type(ty: &TypeRef, optional: bool) -> String {
let base = field_type_for_serde_inner(ty);
if optional { format!("Option<{base}>") } else { base }
}
pub fn gen_data_enum_variant_constructors(enum_def: &EnumDef) -> String {
let constructors = crate::codegen::generators::collect_all_variant_constructors(enum_def);
if constructors.is_empty() {
return String::new();
}
let rendered: Vec<minijinja::Value> = constructors
.iter()
.map(|ctor| {
let params = ctor
.params
.iter()
.map(|p| format!("{}: {}", p.name, serde_field_type(&p.ty, p.optional)))
.collect::<Vec<_>>()
.join(", ");
let field_inits = ctor
.params
.iter()
.map(|p| p.name.as_str())
.collect::<Vec<_>>()
.join(", ");
minijinja::context! {
rust_fn_name => format!("_factory_{}", ctor.snake_name),
variant_name => ctor.variant_name,
params => params,
field_inits => field_inits,
}
})
.collect();
crate::backends::magnus::template_env::render(
"enum_variant_constructor.rs.jinja",
minijinja::context! {
enum_name => &enum_def.name,
constructors => rendered,
},
)
}
pub fn data_enum_variant_constructor_registrations(enum_def: &EnumDef) -> Vec<(String, String, i32)> {
crate::codegen::generators::collect_all_variant_constructors(enum_def)
.into_iter()
.map(|ctor| {
let arity = ctor.params.len() as i32;
(ctor.snake_name.clone(), format!("_factory_{}", ctor.snake_name), arity)
})
.collect()
}