use crate::codegen::naming::{PublicIdentifierKind, internal_rust_identifier, public_host_identifier};
use crate::core::config::Language;
use crate::core::ir::{ErrorDef, ErrorVariant, FieldDef, TypeRef};
pub(super) fn typed_error_definition<'a>(error_type: Option<&str>, errors: &'a [ErrorDef]) -> Option<&'a ErrorDef> {
let name = error_type?;
errors.iter().find(|error| {
!error.binding_excluded
&& [&error.rust_path, &error.original_rust_path, &error.name]
.iter()
.any(|path| path.as_str() == name || path.replace('-', "_") == name)
})
}
pub(super) fn uses_typed_error(error_type: Option<&str>, errors: &[ErrorDef]) -> bool {
typed_error_definition(error_type, errors).is_some()
}
pub(super) fn core_error_path(error: &ErrorDef, source_crate: &str) -> String {
if error.rust_path.is_empty() {
format!("{source_crate}::{}", error.name)
} else {
error.rust_path.replace('-', "_")
}
}
pub(super) fn result_error_type(error_type: Option<&str>, errors: &[ErrorDef]) -> &'static str {
if uses_typed_error(error_type, errors) {
"AlefBridgeError"
} else {
"String"
}
}
pub(super) fn error_mapper(error_type: Option<&str>, errors: &[ErrorDef]) -> &'static str {
if uses_typed_error(error_type, errors) {
"AlefBridgeError::from"
} else {
"|e| e.to_string()"
}
}
pub(super) fn emit(out: &mut String, errors: &[ErrorDef], source_crate: &str) {
let errors = errors
.iter()
.filter(|error| !error.binding_excluded)
.collect::<Vec<_>>();
if errors.is_empty() {
return;
}
let invalid_input = carrier_name("InvalidInput", &errors);
let native_failure = carrier_name("NativeFailure", &errors);
let mut decode_arms = Vec::new();
let mut encode_arms = Vec::new();
let entries = errors.iter().map(|error| {
let dart_name = public_host_identifier(Language::Dart, PublicIdentifierKind::Type, &error.name);
let dart_factory = public_host_identifier(Language::Dart, PublicIdentifierKind::Method, &error.name);
decode_arms.push(format!("AlefBridgeErrorWire_{dart_name}(:final error) => error"));
encode_arms.push(format!("{dart_name} error => AlefBridgeErrorWire.{dart_factory}(error: error)"));
crate::alef_context! {
name => &error.name,
core_path => core_error_path(error, source_crate),
needs_message => error.variants.iter().any(|variant| variant.is_tuple && variant.fields.iter().all(|field| field.binding_excluded)),
arms => error.variants.iter().map(|variant| variant_arm(error, variant, source_crate)).collect::<Vec<_>>(),
}
}).collect::<Vec<_>>();
for (carrier, exception, message) in [
(&invalid_input, "ArgumentError", "error.message.toString()"),
(&native_failure, "StateError", "error.message"),
] {
let dart_name = public_host_identifier(Language::Dart, PublicIdentifierKind::Type, carrier);
let dart_factory = public_host_identifier(Language::Dart, PublicIdentifierKind::Method, carrier);
decode_arms.push(format!(
"AlefBridgeErrorWire_{dart_name}(:final message) => {exception}(message)"
));
encode_arms.push(format!(
"{exception} error => AlefBridgeErrorWire.{dart_factory}(message: {message})"
));
}
encode_arms.push("_ => throw ArgumentError.value({}, 'error', 'Unsupported bridge error')".into());
out.push_str(&crate::backends::dart::error_display::metadata_comments(&errors));
out.push_str(&crate::backends::dart::template_env::render(
"rust_typed_error_bridge.jinja",
crate::alef_context! {
errors => entries,
invalid_input => invalid_input,
native_failure => native_failure,
decode_code => format!("{:?}", format!("switch ({{}}) {{ {} }}", decode_arms.join(", "))),
encode_code => format!("{:?}", format!("switch ({{}}) {{ {} }}", encode_arms.join(", "))),
},
));
}
fn carrier_name(preferred: &str, errors: &[&ErrorDef]) -> String {
let mut name = preferred.to_string();
let mut suffix = 0;
while errors.iter().any(|error| {
public_host_identifier(Language::Dart, PublicIdentifierKind::Type, &error.name)
== public_host_identifier(Language::Dart, PublicIdentifierKind::Type, &name)
}) {
suffix += 1;
name = format!("{preferred}{suffix}");
}
name
}
fn variant_arm(error: &ErrorDef, variant: &ErrorVariant, source_crate: &str) -> minijinja::Value {
let core_path = core_error_path(error, source_crate);
crate::alef_context! {
pattern => variant_pattern(&core_path, variant),
variant => &variant.name,
fields => variant_fields(error, variant, source_crate),
}
}
fn variant_pattern(core_path: &str, variant: &ErrorVariant) -> String {
if variant.is_unit {
return format!("{core_path}::{}", variant.name);
}
if variant.is_tuple {
let bindings = variant
.fields
.iter()
.enumerate()
.map(|(index, field)| {
if field.binding_excluded {
"_".to_string()
} else {
format!("field{index}")
}
})
.collect::<Vec<_>>();
return format!("{core_path}::{}({})", variant.name, bindings.join(", "));
}
let bindings = variant
.fields
.iter()
.filter(|field| !field.binding_excluded)
.map(|field| internal_rust_identifier(&field.name))
.collect::<Vec<_>>();
let bindings = if bindings.is_empty() {
String::new()
} else {
format!("{},", bindings.join(", "))
};
format!("{core_path}::{} {{ {bindings} .. }}", variant.name)
}
fn variant_fields(error: &ErrorDef, variant: &ErrorVariant, source_crate: &str) -> Vec<minijinja::Value> {
let mut fields = super::error_fields::visible(variant)
.enumerate().map(|(visible_index, (index, field))| {
let binding = if variant.is_tuple { format!("field{index}") } else { internal_rust_identifier(&field.name) };
let mut field = field.clone();
if variant.is_tuple { field.optional = false; }
crate::alef_context! {
name => if variant.is_tuple { format!("field{visible_index}") } else { internal_rust_identifier(&field.name) },
value => field_expression(&binding, &field, source_crate, &super::mirror::static_text_module_name(error)),
}
}).collect::<Vec<_>>();
if variant.is_tuple && fields.is_empty() {
fields.push(crate::alef_context! { name => "field0", value => "message" });
}
fields
}
fn optional_field_expression(
binding: &str,
flatten: &str,
field: &FieldDef,
source_crate: &str,
static_text_module: &str,
) -> String {
let mapper = if !field.sanitized
&& field.ty == TypeRef::String
&& field.core_wrapper == crate::core::ir::CoreWrapper::StaticStr
{
format!("{static_text_module}::register")
} else {
format!(
"|value| {}",
field_expression("value", field, source_crate, static_text_module)
)
};
format!("{binding}{flatten}.map({mapper})")
}
fn field_expression(binding: &str, field: &FieldDef, source_crate: &str, static_text_module: &str) -> String {
if let TypeRef::Optional(inner_ty) = &field.ty {
let inner = FieldDef {
ty: *inner_ty.clone(),
optional: false,
..field.clone()
};
let flatten = if field.optional { ".flatten()" } else { "" };
return optional_field_expression(binding, flatten, &inner, source_crate, static_text_module);
}
if field.optional {
let inner = FieldDef {
optional: false,
..field.clone()
};
return optional_field_expression(binding, "", &inner, source_crate, static_text_module);
}
if let TypeRef::Duration = field.ty {
return format!("{binding}.as_millis() as i64");
}
if field.sanitized {
return format!("{binding}.to_string()");
}
if field.ty == TypeRef::String && field.core_wrapper == crate::core::ir::CoreWrapper::StaticStr {
return format!("{static_text_module}::register({binding})");
}
if field.ty == TypeRef::String {
return format!("{binding}.into()");
}
super::enum_fields::enum_variant_field_conv(binding, field, source_crate)
}