use crate::core::ir::{ParamDef, PrimitiveType, TypeRef};
pub(crate) fn frb_rust_type(ty: &TypeRef, optional: bool) -> String {
let effective_ty = unwrap_nested_optional(ty, optional);
let inner = frb_rust_type_inner(effective_ty);
if optional { format!("Option<{inner}>") } else { inner }
}
pub(crate) fn frb_rust_type_excluded_aware(
ty: &TypeRef,
optional: bool,
excluded_type_paths: &std::collections::HashMap<String, String>,
) -> String {
let effective_ty = unwrap_nested_optional(ty, optional);
let inner = frb_rust_type_inner_excluded_aware(effective_ty, excluded_type_paths);
if optional { format!("Option<{inner}>") } else { inner }
}
fn unwrap_nested_optional(ty: &TypeRef, optional: bool) -> &TypeRef {
if optional && let TypeRef::Optional(inner) = ty {
inner.as_ref()
} else {
ty
}
}
pub(crate) fn frb_rust_type_inner(ty: &TypeRef) -> String {
frb_rust_type_inner_excluded_aware(ty, &std::collections::HashMap::new())
}
pub(crate) fn frb_rust_type_inner_excluded_aware(
ty: &TypeRef,
excluded_type_paths: &std::collections::HashMap<String, String>,
) -> String {
match ty {
TypeRef::Primitive(p) => match p {
PrimitiveType::Bool => "bool".to_string(),
PrimitiveType::F32 | PrimitiveType::F64 => "f64".to_string(),
_ => "i64".to_string(),
},
TypeRef::String | TypeRef::Char => "String".to_string(),
TypeRef::Bytes => "Vec<u8>".to_string(),
TypeRef::Optional(inner) => format!(
"Option<{}>",
frb_rust_type_inner_excluded_aware(inner, excluded_type_paths)
),
TypeRef::Vec(inner) => format!(
"Vec<{}>",
frb_rust_type_inner_excluded_aware(inner, excluded_type_paths)
),
TypeRef::Map(k, v) => {
format!(
"std::collections::HashMap<{}, {}>",
frb_rust_type_inner_excluded_aware(k, excluded_type_paths),
frb_rust_type_inner_excluded_aware(v, excluded_type_paths)
)
}
TypeRef::Named(name) => match excluded_type_paths.get(name) {
Some(path) if !path.is_empty() => path.replace('-', "_"),
_ => name.clone(),
},
TypeRef::Path => "String".to_string(),
TypeRef::Unit => "()".to_string(),
TypeRef::Json => "String".to_string(),
TypeRef::Duration => "i64".to_string(),
}
}
pub(crate) fn primitive_name(prim: &PrimitiveType) -> &'static str {
match prim {
PrimitiveType::Bool => "bool",
PrimitiveType::U8 => "u8",
PrimitiveType::I8 => "i8",
PrimitiveType::U16 => "u16",
PrimitiveType::I16 => "i16",
PrimitiveType::U32 => "u32",
PrimitiveType::I32 => "i32",
PrimitiveType::U64 => "u64",
PrimitiveType::I64 => "i64",
PrimitiveType::Usize => "usize",
PrimitiveType::Isize => "isize",
PrimitiveType::F32 => "f32",
PrimitiveType::F64 => "f64",
}
}
#[allow(dead_code)]
pub(crate) fn frb_rust_type_with_source(
ty: &TypeRef,
optional: bool,
source_crate: &str,
type_paths: &std::collections::HashMap<String, String>,
) -> String {
let effective_ty = unwrap_nested_optional(ty, optional);
let inner = frb_rust_type_inner_with_source(effective_ty, source_crate, type_paths);
if optional { format!("Option<{inner}>") } else { inner }
}
pub(crate) fn frb_rust_type_inner_with_source(
ty: &TypeRef,
source_crate: &str,
type_paths: &std::collections::HashMap<String, String>,
) -> String {
match ty {
TypeRef::Named(name) => match type_paths.get(name) {
Some(path) => path.clone(),
None => format!("{source_crate}::{name}"),
},
TypeRef::Optional(inner) => {
format!(
"Option<{}>",
frb_rust_type_inner_with_source(inner, source_crate, type_paths)
)
}
TypeRef::Vec(inner) => {
format!(
"Vec<{}>",
frb_rust_type_inner_with_source(inner, source_crate, type_paths)
)
}
TypeRef::Map(k, v) => format!(
"std::collections::HashMap<{}, {}>",
frb_rust_type_inner_with_source(k, source_crate, type_paths),
frb_rust_type_inner_with_source(v, source_crate, type_paths)
),
_ => frb_rust_type_inner(ty),
}
}
pub(crate) fn dart_call_arg(p: &ParamDef) -> String {
let name = &p.name;
let original = p.original_type.as_deref().unwrap_or("");
let stripped_orig = original
.trim()
.trim_start_matches('&')
.trim_start_matches("mut ")
.trim();
if !stripped_orig.is_empty() && stripped_orig.starts_with("Vec(") && stripped_orig.contains("Named(\"(") {
let tuple_inner = stripped_orig
.find("Named(\"(")
.and_then(|start| {
let rest = &stripped_orig[start + 8..]; rest.find(")\")")
.map(|end| rest[..end].trim_end_matches(')').to_string())
})
.unwrap_or_default();
if tuple_inner.starts_with("PathBuf,") || tuple_inner.starts_with("PathBuf ,") {
return format!("{name}.into_iter().map(|p| (std::path::PathBuf::from(p), None)).collect::<Vec<_>>()");
}
if tuple_inner.starts_with("Vec<u8>,") || tuple_inner.starts_with("Vec<u8> ,") {
return format!(
"{{ let _ = {name}; compile_error!(\"alef cannot bridge Vec<(Vec<u8>, ...)> through Dart FRB; configure dart.exclude_functions for this item\") }}"
);
}
}
if matches!(p.ty, TypeRef::Path) {
if p.optional {
if p.is_ref {
return format!("{name}.as_ref().map(std::path::Path::new)");
}
return format!("{name}.map(std::path::PathBuf::from)");
}
if p.is_ref {
return format!("std::path::Path::new(&{name})");
}
return format!("std::path::PathBuf::from({name})");
}
if let TypeRef::Primitive(prim) = &p.ty {
let target = primitive_name(prim);
if target != "i64" && target != "f64" && target != "bool" {
if p.optional {
return format!("{name}.map(|v| v as {target})");
}
return if p.is_ref {
format!("&({name} as {target})")
} else {
format!("{name} as {target}")
};
}
}
if let TypeRef::Vec(inner) = &p.ty {
if let TypeRef::Primitive(prim) = inner.as_ref() {
let target = primitive_name(prim);
if target != "i64" && target != "f64" && target != "bool" {
if p.optional {
if p.is_ref {
return format!(
"{name}.as_ref().map(|v| v.iter().map(|x| *x as {target}).collect::<Vec<_>>()).as_deref()"
);
}
return format!("{name}.map(|v| v.into_iter().map(|x| x as {target}).collect::<Vec<_>>())");
}
if p.is_ref {
return format!("{name}.iter().map(|x| *x as {target}).collect::<Vec<_>>().as_slice()");
}
return format!("{name}.into_iter().map(|x| x as {target}).collect::<Vec<_>>()");
}
}
}
if p.is_ref
&& p.vec_inner_is_ref
&& matches!(p.ty, TypeRef::Vec(ref inner) if matches!(inner.as_ref(), TypeRef::String))
{
return format!("&{name}.iter().map(|s| s.as_str()).collect::<Vec<_>>()");
}
if !p.is_ref {
return name.clone();
}
match (&p.ty, p.optional) {
(TypeRef::Bytes, false) => format!("&{name}"),
(TypeRef::String | TypeRef::Char, false) => format!("&{name}"),
(TypeRef::String | TypeRef::Char, true) => format!("{name}.as_deref()"),
(TypeRef::Vec(_), true) => format!("{name}.as_deref()"),
_ => format!("&{name}"),
}
}