use proc_macro2::TokenStream;
use syn::{Expr, Type, TypePtr};
use crate::{
conversion::analysis::fun::function_wrapper::{RustConversionType, TypeConversionPolicy},
types::make_ident,
};
use quote::quote;
use syn::parse_quote;
use super::MaybeUnsafeStmt;
pub(super) enum RustParamConversion {
Param {
ty: Type,
local_variables: Vec<MaybeUnsafeStmt>,
conversion: TokenStream,
conversion_requires_unsafe: bool,
},
ReturnValue {
ty: Type,
},
}
impl TypeConversionPolicy {
pub(super) fn rust_conversion(&self, var: Expr, counter: &mut usize) -> RustParamConversion {
match self.rust_conversion {
RustConversionType::None => RustParamConversion::Param {
ty: self.converted_rust_type(),
local_variables: Vec::new(),
conversion: quote! { #var },
conversion_requires_unsafe: false,
},
RustConversionType::FromStr => RustParamConversion::Param {
ty: parse_quote! { impl ToCppString },
local_variables: Vec::new(),
conversion: quote! ( #var .into_cpp() ),
conversion_requires_unsafe: false,
},
RustConversionType::ToBoxedUpHolder(ref sub) => {
let holder_type = sub.holder();
let id = sub.id();
let ty = parse_quote! { autocxx::subclass::CppSubclassRustPeerHolder<
super:: #id>
};
RustParamConversion::Param {
ty,
local_variables: Vec::new(),
conversion: quote! {
Box::new(#holder_type(#var))
},
conversion_requires_unsafe: false,
}
}
RustConversionType::FromPinMaybeUninitToPtr => {
let ty = match self.cxxbridge_type() {
Type::Ptr(TypePtr { elem, .. }) => elem,
_ => panic!("Not a ptr"),
};
let ty = parse_quote! {
::core::pin::Pin<&mut ::core::mem::MaybeUninit< #ty >>
};
RustParamConversion::Param {
ty,
local_variables: Vec::new(),
conversion: quote! {
#var.get_unchecked_mut().as_mut_ptr()
},
conversion_requires_unsafe: true,
}
}
RustConversionType::FromPinMoveRefToPtr => {
let ty = match self.cxxbridge_type() {
Type::Ptr(TypePtr { elem, .. }) => elem,
_ => panic!("Not a ptr"),
};
let ty = parse_quote! {
::core::pin::Pin<autocxx::moveit::MoveRef< '_, #ty >>
};
RustParamConversion::Param {
ty,
local_variables: Vec::new(),
conversion: quote! {
{ let r: &mut _ = ::core::pin::Pin::into_inner_unchecked(#var.as_mut());
r
}
},
conversion_requires_unsafe: true,
}
}
RustConversionType::FromTypeToPtr => {
let ty = match self.cxxbridge_type() {
Type::Ptr(TypePtr { elem, .. }) => elem,
_ => panic!("Not a ptr"),
};
let ty = parse_quote! { &mut #ty };
RustParamConversion::Param {
ty,
local_variables: Vec::new(),
conversion: quote! {
#var
},
conversion_requires_unsafe: false,
}
}
RustConversionType::FromValueParamToPtr | RustConversionType::FromRValueParamToPtr => {
let (handler_type, param_trait) = match self.rust_conversion {
RustConversionType::FromValueParamToPtr => ("ValueParamHandler", "ValueParam"),
RustConversionType::FromRValueParamToPtr => {
("RValueParamHandler", "RValueParam")
}
_ => unreachable!(),
};
let handler_type = make_ident(handler_type);
let param_trait = make_ident(param_trait);
let var_counter = *counter;
*counter += 1;
let space_var_name = format!("space{var_counter}");
let space_var_name = make_ident(space_var_name);
let ty = self.cxxbridge_type();
let ty = parse_quote! { impl autocxx::#param_trait<#ty> };
RustParamConversion::Param {
ty,
local_variables: vec![
MaybeUnsafeStmt::new(
quote! { let mut #space_var_name = autocxx::#handler_type::default(); },
),
MaybeUnsafeStmt::binary(
quote! { let mut #space_var_name =
unsafe { ::core::pin::Pin::new_unchecked(&mut #space_var_name) };
},
quote! { let mut #space_var_name =
::core::pin::Pin::new_unchecked(&mut #space_var_name);
},
),
MaybeUnsafeStmt::needs_unsafe(
quote! { #space_var_name.as_mut().populate(#var); },
),
],
conversion: quote! {
#space_var_name.get_ptr()
},
conversion_requires_unsafe: false,
}
}
RustConversionType::FromPlacementParamToNewReturn => {
let ty = match self.cxxbridge_type() {
Type::Ptr(TypePtr { elem, .. }) => *(*elem).clone(),
_ => panic!("Not a ptr"),
};
RustParamConversion::ReturnValue { ty }
}
RustConversionType::FromPointerToReferenceWrapper => {
let (is_mut, ty) = match self.cxxbridge_type() {
Type::Ptr(TypePtr {
mutability, elem, ..
}) => (mutability.is_some(), elem.as_ref()),
_ => panic!("Not a pointer"),
};
let (ty, wrapper_name) = if is_mut {
(
parse_quote! { autocxx::CppMutLtRef<'a, #ty> },
"CppMutLtRef",
)
} else {
(parse_quote! { autocxx::CppLtRef<'a, #ty> }, "CppLtRef")
};
let wrapper_name = make_ident(wrapper_name);
RustParamConversion::Param {
ty,
local_variables: Vec::new(),
conversion: quote! {
autocxx::#wrapper_name::from_ptr (#var)
},
conversion_requires_unsafe: false,
}
}
RustConversionType::FromReferenceWrapperToPointer => {
let (is_mut, ty) = match self.cxxbridge_type() {
Type::Ptr(TypePtr {
mutability, elem, ..
}) => (mutability.is_some(), elem.as_ref()),
_ => panic!("Not a pointer"),
};
let ty = if is_mut {
parse_quote! { autocxx::CppMutRef<#ty> }
} else {
parse_quote! { autocxx::CppRef<#ty> }
};
RustParamConversion::Param {
ty,
local_variables: Vec::new(),
conversion: if is_mut {
quote! {
#var .as_mut_ptr()
}
} else {
quote! {
#var .as_ptr()
}
},
conversion_requires_unsafe: false,
}
}
}
}
}