use crate::codegen::cfg::is_host_owned_rust_path;
use crate::codegen::conversions::{VariantDeclaration, enum_variant_declaration};
use crate::codegen::shared::binding_fields;
use crate::core::ir::{CoreWrapper, EnumDef, ErrorDef, FieldDef, TypeDef, TypeRef};
use super::conversions::{frb_rust_type, frb_rust_type_inner, primitive_name};
fn emit_rust_doc(doc: &str, indent: &str, out: &mut String) {
if doc.is_empty() {
return;
}
for line in doc.lines() {
out.push_str(indent);
if line.is_empty() {
out.push_str("///\n");
} else {
out.push_str("/// ");
out.push_str(line);
out.push('\n');
}
}
}
pub(crate) fn emit_mirror_struct(out: &mut String, ty: &TypeDef, source_crate_name: &str) {
use crate::backends::dart::template_env;
if ty.is_opaque {
let source_module = source_crate_name.replace('-', "_");
let inner_path = if ty.rust_path.is_empty() {
format!("{source_module}::{}", ty.name)
} else {
ty.rust_path.replace('-', "_")
};
emit_rust_doc(&ty.doc, "", out);
let wrapper_cfg = super::helpers::widen_opaque_wrapper_cfg(ty.cfg.as_deref().unwrap_or(""));
out.push_str(&template_env::render(
"rust_opaque_wrapper_struct.jinja",
crate::alef_context! {
name => ty.name.as_str(),
inner_path => inner_path.as_str(),
source_cfg => wrapper_cfg.as_str(),
},
));
return;
}
emit_rust_doc(&ty.doc, "", out);
out.push_str(&template_env::render(
"rust_mirror_struct_attribute.jinja",
crate::alef_context! {
name => ty.name.as_str(),
source_cfg => ty.cfg.as_deref().unwrap_or(""),
},
));
out.push_str(&template_env::render(
"rust_mirror_struct_open.jinja",
crate::alef_context! {
name => ty.name.as_str(),
},
));
for field in binding_fields(&ty.fields) {
let rust_ty = frb_rust_type(&field.ty, field.optional);
emit_rust_doc(&field.doc, " ", out);
out.push_str(&template_env::render(
"rust_mirror_struct_field.jinja",
crate::alef_context! {
field_name => field.name.as_str(),
rust_ty => rust_ty,
constructor_default => ty.constructor_extension_value(field).and_then(|_| super::super::constructor_defaults::frb_literal(field)),
constructor_metadata => ty.constructor_extension_value(field).and_then(|_| super::super::constructor_defaults::literal(field)).map(|value| {
serde_json::json!([ty.name, crate::codegen::naming::public_field_name(crate::core::config::Language::Dart, &field.name, None), value]).to_string()
}),
},
));
}
out.push_str(&template_env::render(
"rust_mirror_struct_close.jinja",
crate::alef_context! {},
));
}
pub(crate) fn emit_mirror_enum(
out: &mut String,
en: &EnumDef,
source_crate_name: &str,
configured_features: Option<&[String]>,
) {
use crate::backends::dart::template_env;
let is_host_enum = is_host_owned_rust_path(source_crate_name, &en.rust_path);
let configured_features_set: Option<std::collections::HashSet<&str>> =
configured_features.map(|features| features.iter().map(String::as_str).collect());
let declared_variants: Vec<&crate::core::ir::EnumVariant> = en
.variants
.iter()
.filter(|variant| {
!matches!(
enum_variant_declaration(variant, is_host_enum, configured_features_set.as_ref()),
VariantDeclaration::Drop
)
})
.collect();
let all_unit = declared_variants
.iter()
.all(|v| v.fields.iter().all(|f| f.binding_excluded));
emit_rust_doc(&en.doc, "", out);
out.push_str(&template_env::render(
"rust_mirror_enum_attribute.jinja",
crate::alef_context! {
name => en.name.as_str(),
source_cfg => en.cfg.as_deref().unwrap_or(""),
},
));
out.push_str(&template_env::render(
"rust_mirror_enum_open.jinja",
crate::alef_context! {
name => en.name.as_str(),
},
));
if all_unit {
for variant in &declared_variants {
emit_rust_doc(&variant.doc, " ", out);
out.push_str(&template_env::render(
"rust_mirror_enum_unit_variant.jinja",
crate::alef_context! {
variant_name => variant.name.as_str(),
},
));
}
} else {
for variant in &declared_variants {
let visible_fields: Vec<&_> = variant.fields.iter().filter(|f| !f.binding_excluded).collect();
if visible_fields.is_empty() {
emit_rust_doc(&variant.doc, " ", out);
out.push_str(&template_env::render(
"rust_mirror_enum_unit_variant.jinja",
crate::alef_context! {
variant_name => variant.name.as_str(),
},
));
} else {
emit_rust_doc(&variant.doc, " ", out);
out.push_str(&template_env::render(
"rust_mirror_enum_data_variant_open.jinja",
crate::alef_context! {
variant_name => variant.name.as_str(),
},
));
for (idx, f) in visible_fields.iter().enumerate() {
let fname = if f.name.is_empty() || f.name.starts_with('_') {
format!("field{idx}")
} else {
f.name.clone()
};
let rust_ty = frb_rust_type(&f.ty, f.optional);
emit_rust_doc(&f.doc, " ", out);
out.push_str(&template_env::render(
"rust_mirror_enum_data_variant_field.jinja",
crate::alef_context! {
field_name => fname,
rust_ty => rust_ty,
},
));
}
out.push_str(&template_env::render(
"rust_mirror_enum_data_close.jinja",
crate::alef_context! {},
));
}
}
}
out.push_str("}\n");
}
#[path = "mirror_field_reconstruction.rs"]
mod field_reconstruction;
use field_reconstruction::field_from_expr;
pub(super) fn borrows_static_text(error: &ErrorDef) -> bool {
error
.variants
.iter()
.flat_map(|variant| &variant.fields)
.any(|field| !field.binding_excluded && !field.sanitized && field.core_wrapper == CoreWrapper::StaticStr)
}
fn variant_is_reconstructible(fields: &[&FieldDef]) -> bool {
fields.iter().all(|f| !f.sanitized)
}
fn emit_from_impl(out: &mut String, error: &ErrorDef, core_path: &str, error_cfg: &str) {
use crate::backends::dart::template_env;
let any_skipped = error.variants.iter().any(|v| {
let visible_fields: Vec<&FieldDef> = v.fields.iter().filter(|f| !f.binding_excluded).collect();
!v.is_unit && !visible_fields.is_empty() && !variant_is_reconstructible(&visible_fields)
});
let borrows_static_str = borrows_static_text(error);
out.push_str(&template_env::render(
"rust_mirror_error_from_impl_open.rs.jinja",
crate::alef_context! {
name => error.name.as_str(),
core_path => core_path,
source_cfg => error_cfg,
borrows_static_str => borrows_static_str,
static_text_module => static_text_module_name(error),
},
));
for variant in &error.variants {
let vname = &variant.name;
if variant.is_unit {
out.push_str(&template_env::render(
"rust_mirror_error_unit_from_arm.rs.jinja",
crate::alef_context! {
name => error.name.as_str(),
vname => vname.as_str(),
},
));
} else if !variant.is_unit && variant.is_tuple && variant.fields.iter().all(|f| f.binding_excluded) {
out.push_str(&template_env::render(
"rust_mirror_error_excluded_from_arm.rs.jinja",
crate::alef_context! {
name => error.name.as_str(),
vname => vname.as_str(),
},
));
} else if !variant.is_unit && variant.fields.is_empty() {
out.push_str(&template_env::render(
"rust_mirror_error_unit_from_arm.rs.jinja",
crate::alef_context! {
name => error.name.as_str(),
vname => vname.as_str(),
},
));
} else if variant.fields.iter().all(|f| f.binding_excluded) {
out.push_str(&template_env::render(
"rust_mirror_error_excluded_from_arm.rs.jinja",
crate::alef_context! {
name => error.name.as_str(),
vname => vname.as_str(),
},
));
} else {
let visible_fields: Vec<&FieldDef> = variant.fields.iter().filter(|f| !f.binding_excluded).collect();
if !variant_is_reconstructible(&visible_fields) {
continue;
}
let is_tuple_variant = visible_fields
.iter()
.all(|f| f.name.is_empty() || f.name.starts_with('_'));
let field_names: Vec<String> = visible_fields
.iter()
.enumerate()
.map(|(idx, f)| {
if f.name.is_empty() || f.name.starts_with('_') {
format!("field{idx}")
} else {
f.name.clone()
}
})
.collect();
let pat_fields: String = field_names
.iter()
.map(|fname| format!("{fname}: f_{fname}"))
.collect::<Vec<_>>()
.join(", ");
out.push_str(&template_env::render(
"rust_mirror_error_struct_pattern_arm.rs.jinja",
crate::alef_context! {
name => error.name.as_str(),
vname => vname.as_str(),
pat_fields => pat_fields.as_str(),
},
));
if is_tuple_variant {
let mut args: Vec<String> = visible_fields
.iter()
.enumerate()
.map(|(i, f)| {
let fname = &field_names[i];
field_from_expr(f, &format!("f_{fname}"))
})
.collect();
let excluded_count = variant.fields.iter().filter(|f| f.binding_excluded).count();
for _ in 0..excluded_count {
args.push("Default::default()".to_string());
}
out.push_str(&template_env::render(
"rust_mirror_error_tuple_return.rs.jinja",
crate::alef_context! {
vname => vname.as_str(),
args => args.join(", "),
},
));
} else {
let mut real_fields: Vec<String> = Vec::new();
for (i, f) in visible_fields.iter().enumerate() {
let fname = &field_names[i];
let expr = field_from_expr(f, &format!("f_{fname}"));
real_fields.push(format!(" {fname}: {expr}"));
}
for f in variant.fields.iter().filter(|f| f.binding_excluded) {
real_fields.push(format!(" {}: Default::default()", f.name));
}
out.push_str(&template_env::render(
"rust_mirror_error_struct_return.rs.jinja",
crate::alef_context! {
vname => vname.as_str(),
real_fields => real_fields.join(",\n"),
},
));
}
out.push_str(" }\n");
}
}
if any_skipped {
out.push_str(&template_env::render(
"rust_mirror_error_sanitized_wildcard_arm.rs.jinja",
crate::alef_context! { borrows_static_str => borrows_static_str },
));
}
out.push_str(&template_env::render(
"rust_mirror_error_from_impl_close.rs.jinja",
crate::alef_context! { borrows_static_str => borrows_static_str },
));
}
pub(crate) fn error_method_dart_name(error: &ErrorDef, method_name: &str) -> Option<String> {
use crate::codegen::naming::{PublicIdentifierKind, public_host_identifier};
use crate::core::config::Language;
let methods: Vec<&crate::core::ir::MethodDef> = error.methods.iter().filter(|m| !m.sanitized).collect();
let position = methods.iter().position(|m| m.name == method_name)?;
let rust_name = error_bridge_method_names(error, &methods).swap_remove(position);
Some(public_host_identifier(
Language::Dart,
PublicIdentifierKind::Method,
&rust_name,
))
}
fn error_bridge_method_names(error: &ErrorDef, methods: &[&crate::core::ir::MethodDef]) -> Vec<String> {
use crate::codegen::naming::{PublicIdentifierKind, internal_rust_identifier, public_host_identifier};
use crate::core::config::Language;
let dart_name = |name: &str| public_host_identifier(Language::Dart, PublicIdentifierKind::Method, name);
let mut occupied: std::collections::HashSet<String> = error
.variants
.iter()
.flat_map(|variant| {
if !variant.is_unit && variant.is_tuple && variant.fields.iter().all(|field| field.binding_excluded) {
return vec!["field0".to_string()];
}
binding_fields(&variant.fields)
.enumerate()
.map(|(index, field)| {
if field.name.is_empty() || field.name.starts_with('_') {
format!("field{index}")
} else {
field.name.clone()
}
})
.collect()
})
.map(|name| dart_name(&name))
.collect();
let mut reserved = occupied.clone();
reserved.extend(methods.iter().map(|method| dart_name(&method.name)));
methods
.iter()
.map(|method| {
let mut name = method.name.clone();
while occupied.contains(&dart_name(&name)) || (name != method.name && reserved.contains(&dart_name(&name)))
{
name = format!("get_{name}");
}
let host_name = dart_name(&name);
occupied.insert(host_name.clone());
reserved.insert(host_name);
internal_rust_identifier(&name)
})
.collect()
}
pub(super) fn static_text_module_name(error: &ErrorDef) -> String {
crate::codegen::naming::internal_rust_identifier(&format!(
"alef_static_text_{}",
crate::codegen::naming::pascal_to_snake(&error.name)
))
}
fn introspection_return_type(ty: &TypeRef, fallible: bool) -> String {
let value = frb_rust_type_inner(ty);
if fallible {
format!("Result<{value}, String>")
} else {
value
}
}
pub(crate) fn emit_mirror_error(out: &mut String, error: &ErrorDef, source_crate_name: &str) {
use crate::backends::dart::template_env;
emit_rust_doc(&error.doc, "", out);
out.push_str(&template_env::render(
"rust_mirror_enum_attribute.jinja",
crate::alef_context! {
name => error.name.as_str(),
source_cfg => "",
},
));
out.push_str(&template_env::render(
"rust_mirror_enum_open.jinja",
crate::alef_context! {
name => error.name.as_str(),
},
));
for variant in &error.variants {
emit_rust_doc(&variant.doc, " ", out);
if variant.is_unit {
out.push_str(&template_env::render(
"rust_mirror_enum_unit_variant.jinja",
crate::alef_context! {
variant_name => variant.name.as_str(),
},
));
} else if !variant.is_unit && variant.is_tuple && variant.fields.iter().all(|f| f.binding_excluded) {
out.push_str(&template_env::render(
"rust_mirror_enum_data_variant_open.jinja",
crate::alef_context! {
variant_name => variant.name.as_str(),
},
));
out.push_str(&template_env::render(
"rust_mirror_enum_data_variant_field.jinja",
crate::alef_context! {
field_name => "field0",
rust_ty => "String",
},
));
out.push_str(&template_env::render(
"rust_mirror_enum_data_close.jinja",
crate::alef_context! {},
));
} else {
let visible_fields: Vec<_> = super::error_fields::visible(variant).map(|(_, f)| f).collect();
if visible_fields.is_empty() {
out.push_str(&template_env::render(
"rust_mirror_enum_unit_variant.jinja",
crate::alef_context! {
variant_name => variant.name.as_str(),
},
));
} else {
out.push_str(&template_env::render(
"rust_mirror_enum_data_variant_open.jinja",
crate::alef_context! {
variant_name => variant.name.as_str(),
},
));
for (idx, f) in visible_fields.iter().enumerate() {
let fname = if f.name.is_empty() || f.name.starts_with('_') {
format!("field{idx}")
} else {
f.name.clone()
};
let rust_ty = frb_rust_type(&f.ty, f.optional);
out.push_str(&template_env::render(
"rust_mirror_enum_data_variant_field.jinja",
crate::alef_context! {
field_name => fname,
rust_ty => rust_ty,
},
));
}
out.push_str(&template_env::render(
"rust_mirror_enum_data_close.jinja",
crate::alef_context! {},
));
}
}
}
out.push_str("}\n");
if borrows_static_text(error) {
out.push_str(&template_env::render(
"rust_error_static_text_registry.rs.jinja",
crate::alef_context! { module_name => static_text_module_name(error) },
));
}
let bridge_methods: Vec<&crate::core::ir::MethodDef> = error.methods.iter().filter(|m| !m.sanitized).collect();
if bridge_methods.is_empty() {
return;
}
let core_path = if error.rust_path.is_empty() {
format!("{source_crate_name}::{}", error.name)
} else {
error.rust_path.replace('-', "_")
};
emit_from_impl(out, error, &core_path, "");
out.push_str(&crate::backends::dart::template_env::render(
"rust_error_impl_open.rs.jinja",
crate::alef_context! {
error_name => error.name.as_str(),
source_cfg => "",
},
));
let fallible = borrows_static_text(error);
let bridge_names = error_bridge_method_names(error, &bridge_methods);
for (method, bridge_name) in bridge_methods.into_iter().zip(bridge_names) {
emit_rust_doc(&method.doc, " ", out);
let ret_ty = introspection_return_type(&method.return_type, fallible);
out.push_str(&crate::backends::dart::template_env::render(
"rust_error_method_open.rs.jinja",
crate::alef_context! {
method_name => bridge_name,
ret_ty => ret_ty.as_str(),
},
));
let call_suffix: String = if method.returns_ref
&& matches!(method.return_type, crate::core::ir::TypeRef::String)
{
".to_string()".to_string()
} else if matches!(method.return_type, crate::core::ir::TypeRef::Duration) {
".as_millis() as i64".to_string()
} else if matches!(&method.return_type, crate::core::ir::TypeRef::Optional(inner) if matches!(inner.as_ref(), crate::core::ir::TypeRef::Duration))
{
".map(|d| d.as_millis() as i64)".to_string()
} else if let crate::core::ir::TypeRef::Primitive(ref prim) = method.return_type {
let native = primitive_name(prim);
let frb_ty = frb_rust_type_inner(&method.return_type);
if native != frb_ty.as_str() {
format!(" as {frb_ty}")
} else {
String::new()
}
} else {
String::new()
};
out.push_str(&crate::backends::dart::template_env::render(
"rust_error_method_body.rs.jinja",
crate::alef_context! {
core_path => core_path.as_str(),
method_name => method.name.as_str(),
call_suffix => call_suffix.as_str(),
fallible => fallible,
},
));
out.push_str(" }\n");
}
out.push_str("}\n");
}
#[cfg(test)]
mod tests {
use super::{emit_mirror_error, emit_mirror_struct};
use crate::core::ir::{ErrorDef, MethodDef, ReceiverKind, TypeDef, TypeRef};
fn duration_error(return_type: TypeRef) -> ErrorDef {
ErrorDef {
exception_base: None,
name: "ApiError".to_string(),
rust_path: "demo::ApiError".to_string(),
original_rust_path: String::new(),
variants: vec![],
doc: String::new(),
methods: vec![MethodDef {
name: "retry_after".to_string(),
params: vec![],
return_type,
is_async: false,
is_static: false,
error_type: None,
doc: String::new(),
receiver: Some(ReceiverKind::Ref),
cfg: None,
sanitized: false,
trait_source: None,
returns_ref: false,
returns_cow: false,
return_newtype_wrapper: None,
has_default_impl: false,
binding_excluded: false,
binding_exclusion_reason: None,
version: Default::default(),
}],
binding_excluded: false,
binding_exclusion_reason: None,
version: Default::default(),
}
}
#[test]
fn duration_error_methods_convert_to_milliseconds() {
let mut out = String::new();
emit_mirror_error(
&mut out,
&duration_error(TypeRef::Optional(Box::new(TypeRef::Duration))),
"demo",
);
assert!(out.contains("-> Option<i64>"), "{out}");
assert!(
out.contains("real.retry_after().map(|d| d.as_millis() as i64)"),
"{out}"
);
let mut bare = String::new();
emit_mirror_error(&mut bare, &duration_error(TypeRef::Duration), "demo");
assert!(bare.contains("-> i64"), "{bare}");
assert!(bare.contains("real.retry_after().as_millis() as i64"), "{bare}");
}
#[test]
fn error_accessors_do_not_collide_with_freezed_variant_properties() {
use crate::core::ir::{ErrorVariant, FieldDef};
for (fields, expected) in [
(vec!["retry_after"], "get_retry_after"),
(vec!["retryAfter"], "get_retry_after"),
(vec!["retry_after", "get_retry_after"], "get_get_retry_after"),
(vec!["message"], "retry_after"),
] {
let mut error = duration_error(TypeRef::Optional(Box::new(TypeRef::Duration)));
error.variants.push(ErrorVariant {
name: "RateLimited".into(),
fields: fields
.iter()
.map(|name| FieldDef {
name: name.to_string(),
ty: TypeRef::Duration,
..Default::default()
})
.collect(),
..Default::default()
});
let mut generated = String::new();
emit_mirror_error(&mut generated, &error, "demo");
assert!(
generated.contains(&format!("pub fn {expected}(&self)")),
"{fields:?}: {generated}"
);
assert!(generated.contains("real.retry_after().map(|d| d.as_millis() as i64)"));
for field in fields {
assert!(
generated.contains(&format!("{field}: i64")),
"property renamed: {generated}"
);
}
}
}
#[test]
fn optional_error_variant_fields_stay_optional_in_the_mirror() {
use crate::core::ir::{ErrorVariant, FieldDef};
let field = |name: &str, ty: TypeRef, optional: bool| FieldDef {
name: name.to_string(),
ty,
optional,
..Default::default()
};
let mut error = duration_error(TypeRef::Optional(Box::new(TypeRef::Duration)));
error.variants.push(ErrorVariant {
name: "RateLimited".into(),
fields: vec![
field("message", TypeRef::String, false),
field("retry_after", TypeRef::Duration, true),
field("model", TypeRef::String, true),
field("status", TypeRef::Primitive(crate::core::ir::PrimitiveType::U16), false),
],
..Default::default()
});
let mut generated = String::new();
emit_mirror_error(&mut generated, &error, "demo");
for expected in [
"retry_after: Option<i64>,",
"model: Option<String>,",
"message: String,",
"status: i64,",
"retry_after: f_retry_after.as_ref().map(|v| std::time::Duration::from_millis(*v as u64))",
"model: f_model.clone()",
"message: f_message.clone()",
"status: *f_status as u16",
] {
assert!(generated.contains(expected), "missing `{expected}`: {generated}");
}
assert!(!generated.contains("Some(std::time::Duration"), "{generated}");
assert!(!generated.contains("Some(f_model"), "{generated}");
}
#[test]
fn renamed_error_accessors_do_not_take_an_existing_method_name() {
use crate::core::ir::{ErrorVariant, FieldDef};
let mut error = duration_error(TypeRef::Duration);
let mut other = error.methods[0].clone();
other.name = "get_retry_after".into();
error.methods.push(other);
error.variants.push(ErrorVariant {
name: "RateLimited".into(),
fields: vec![FieldDef {
name: "retry_after".into(),
ty: TypeRef::Duration,
..Default::default()
}],
..Default::default()
});
let mut generated = String::new();
emit_mirror_error(&mut generated, &error, "demo");
assert_eq!(
generated.matches("pub fn get_retry_after(&self)").count(),
1,
"{generated}"
);
assert_eq!(
generated.matches("pub fn get_get_retry_after(&self)").count(),
1,
"{generated}"
);
assert!(generated.contains("real.retry_after().as_millis() as i64"));
assert!(generated.contains("real.get_retry_after().as_millis() as i64"));
}
#[test]
fn gated_opaque_wrapper_carries_cfg_on_struct_and_both_impls() {
let ty = TypeDef {
name: "OcrEngine".to_string(),
rust_path: "mylib::thumbnails::OcrEngine".to_string(),
cfg: Some(r#"feature = "thumbnails""#.to_string()),
is_opaque: true,
..Default::default()
};
let mut out = String::new();
emit_mirror_struct(&mut out, &ty, "mylib");
assert!(
out.contains("mylib::thumbnails::OcrEngine"),
"expected the gated host path to still be referenced, got:\n{out}"
);
assert_eq!(
out.matches("#[cfg(feature = \"thumbnails\")]").count(),
3,
"the whole-type gate must land on the struct declaration and both From impls, got:\n{out}"
);
}
#[test]
fn ungated_opaque_wrapper_emits_no_cfg() {
let ty = TypeDef {
name: "PlainEngine".to_string(),
rust_path: "mylib::PlainEngine".to_string(),
is_opaque: true,
..Default::default()
};
let mut out = String::new();
emit_mirror_struct(&mut out, &ty, "mylib");
assert!(
!out.contains("#[cfg("),
"ungated opaque type must not emit #[cfg(...)], got:\n{out}"
);
}
pub(super) fn error_with_text_field(wrapper: crate::core::ir::CoreWrapper, optional: bool) -> ErrorDef {
use crate::core::ir::{ErrorVariant, FieldDef};
let mut error = duration_error(TypeRef::Primitive(crate::core::ir::PrimitiveType::Bool));
error.variants.push(ErrorVariant {
name: "Denied".into(),
fields: vec![FieldDef {
name: "reason".into(),
ty: TypeRef::String,
optional,
core_wrapper: wrapper,
..Default::default()
}],
..Default::default()
});
error
}
#[test]
fn borrowed_error_fields_lookup_original_native_static_str() {
use crate::core::ir::CoreWrapper;
for optional in [false, true] {
let mut generated = String::new();
let error = error_with_text_field(CoreWrapper::StaticStr, optional);
emit_mirror_error(&mut generated, &error, "demo");
super::super::error_bridge::emit(&mut generated, &[error], "demo");
let registration = if optional {
"reason.map(alef_static_text_api_error::register)"
} else {
"::register(reason)"
};
assert!(generated.contains(registration), "{generated}");
assert!(!generated.contains("Box::leak"), "{generated}");
let expected = if optional {
"reason: f_reason.as_ref().map(|v| lookup_static_str(v)).transpose()?"
} else {
"reason: lookup_static_str(f_reason)?"
};
assert!(generated.contains(expected), "optional={optional}: {generated}");
assert_eq!(
generated.matches("fn lookup_static_str").count(),
1,
"optional={optional}: {generated}"
);
let mut owned = String::new();
emit_mirror_error(&mut owned, &error_with_text_field(CoreWrapper::None, optional), "demo");
assert!(!owned.contains("lookup_static_str"), "optional={optional}: {owned}");
assert!(
owned.contains("reason: f_reason.clone()"),
"optional={optional}: {owned}"
);
let mut cow = String::new();
emit_mirror_error(&mut cow, &error_with_text_field(CoreWrapper::Cow, optional), "demo");
let expected = if optional {
"reason: f_reason.as_ref().map(|v| std::borrow::Cow::Owned(v.clone()))"
} else {
"reason: std::borrow::Cow::Owned(f_reason.clone())"
};
assert!(cow.contains(expected), "optional={optional}: {cow}");
assert!(!cow.contains("lookup_static_str"), "optional={optional}: {cow}");
}
}
}
#[cfg(test)]
#[path = "mirror_static_text_tests.rs"]
mod static_text_tests;