use crate::core::config::Language;
use crate::core::ir::{FieldDef, MethodDef, ParamDef, ReceiverKind, TypeRef};
use crate::docs::type_mapping::doc_type;
pub(crate) fn rust_param_type(param: &ParamDef, ffi_prefix: &str) -> String {
let element_borrow = param.vec_inner_is_ref;
let inner = rust_borrowed_type(¶m.ty, param.is_ref, param.is_mut, element_borrow, ffi_prefix);
if param.optional {
format!("Option<{inner}>")
} else {
inner
}
}
pub(crate) fn rust_field_type(field: &FieldDef, ffi_prefix: &str) -> String {
let inner = match field.original_type.as_deref() {
Some(original) => original.to_string(),
None => doc_type(&field.ty, Language::Rust, ffi_prefix),
};
if field.optional && !inner.starts_with("Option<") {
format!("Option<{inner}>")
} else {
inner
}
}
pub(crate) fn rust_receiver(method: &MethodDef) -> Option<&'static str> {
if method.is_static {
return None;
}
Some(match method.receiver {
Some(ReceiverKind::RefMut) => "&mut self",
Some(ReceiverKind::Owned) => "self",
Some(ReceiverKind::Ref) | None => "&self",
})
}
fn rust_borrowed_type(ty: &TypeRef, is_ref: bool, is_mut: bool, element_borrow: bool, ffi_prefix: &str) -> String {
if !is_ref {
return doc_type(ty, Language::Rust, ffi_prefix);
}
let borrow = if is_mut { "&mut " } else { "&" };
let borrowed = match ty {
TypeRef::String | TypeRef::Char => "str".to_string(),
TypeRef::Bytes => "[u8]".to_string(),
TypeRef::Vec(inner) => {
let element = rust_borrowed_type(inner, element_borrow, false, false, ffi_prefix);
format!("[{element}]")
}
_ => doc_type(ty, Language::Rust, ffi_prefix),
};
format!("{borrow}{borrowed}")
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::ir::PrimitiveType;
use crate::docs::test_helpers::{TEST_PREFIX, make_param, make_ref_param};
#[test]
fn borrowed_str_and_slice_params_use_the_unsized_forms() {
assert_eq!(
rust_param_type(&make_ref_param("name", TypeRef::String, false), TEST_PREFIX),
"&str"
);
assert_eq!(
rust_param_type(&make_ref_param("data", TypeRef::Bytes, false), TEST_PREFIX),
"&[u8]"
);
assert_eq!(
rust_param_type(
&make_ref_param(
"ids",
TypeRef::Vec(Box::new(TypeRef::Primitive(PrimitiveType::U32))),
false
),
TEST_PREFIX
),
"&[u32]"
);
}
#[test]
fn a_slice_of_borrowed_elements_borrows_the_element_too() {
let param = ParamDef {
vec_inner_is_ref: true,
..make_ref_param("names", TypeRef::Vec(Box::new(TypeRef::String)), false)
};
assert_eq!(rust_param_type(¶m, TEST_PREFIX), "&[&str]");
}
#[test]
fn an_optional_borrowed_param_borrows_inside_the_option() {
let param = make_ref_param("options", TypeRef::Named("TextOptions".to_string()), true);
assert_eq!(rust_param_type(¶m, TEST_PREFIX), "Option<&TextOptions>");
}
#[test]
fn an_owned_param_keeps_its_owned_type() {
assert_eq!(
rust_param_type(&make_param("name", TypeRef::String, false), TEST_PREFIX),
"String"
);
assert_eq!(
rust_param_type(&make_param("data", TypeRef::Bytes, false), TEST_PREFIX),
"Vec<u8>"
);
}
#[test]
fn rust_field_type_prefers_original_type_for_a_lossless_array_lowering() {
let field = FieldDef {
ty: TypeRef::Vec(Box::new(TypeRef::Named("Point".to_string()))),
original_type: Some("[Point ; 4]".to_string()),
sanitized: false,
..crate::docs::test_helpers::make_field(
"points",
TypeRef::Vec(Box::new(TypeRef::Named("Point".to_string()))),
false,
None,
)
};
assert_eq!(rust_field_type(&field, TEST_PREFIX), "[Point ; 4]");
}
#[test]
fn rust_field_type_falls_back_to_doc_type_when_original_type_is_absent() {
let field = crate::docs::test_helpers::make_field("name", TypeRef::String, false, None);
assert_eq!(rust_field_type(&field, TEST_PREFIX), "String");
}
}