use quote::quote;
use syn::{Error, Type};
use crate::core::{ExportedFnData, InteropTypeDetail};
pub fn validate_parsed_data(data: &ExportedFnData) -> Result<(), Error> {
match &*data.runtime_arg_ty {
Type::Reference(type_ref) => {
if let Type::Path(type_path) = &*type_ref.elem {
let path_str = quote!(#type_path).to_string();
if !(path_str == "OCamlRuntime"
|| path_str == "ocaml_interop :: OCamlRuntime"
|| path_str == ":: ocaml_interop :: OCamlRuntime")
{
return Err(Error::new_spanned(
&data.runtime_arg_ty,
"Exported functions must take an OCamlRuntime reference (e.g., `rt: &OCamlRuntime` or `rt: &mut OCamlRuntime`) as their first argument.",
));
}
} else {
return Err(Error::new_spanned(
&data.runtime_arg_ty,
"Exported functions must take an OCamlRuntime reference (e.g., `rt: &OCamlRuntime` or `rt: &mut OCamlRuntime`) as their first argument.",
));
}
let is_mutable_runtime = type_ref.mutability.is_some();
if data.noalloc {
if is_mutable_runtime {
return Err(Error::new_spanned(
&data.runtime_arg_ty,
"When `noalloc` is used, OCaml runtime argument must be an immutable reference (e.g., &OCamlRuntime)",
));
}
} else {
if !is_mutable_runtime {
return Err(Error::new_spanned(
&data.runtime_arg_ty,
"OCaml runtime argument must be a mutable reference (e.g., &mut OCamlRuntime). Use `#[export(noalloc)]` for an immutable reference.",
));
}
}
}
_ => {
return Err(Error::new_spanned(
&data.runtime_arg_ty,
"Exported functions must take an OCamlRuntime reference (e.g., `rt: &OCamlRuntime` or `rt: &mut OCamlRuntime`) as their first argument.",
));
}
}
if data.processed_args.is_empty() {
return Err(syn::Error::new(
data._original_fn_ident_span, "OCaml functions must take at least one argument in addition to the OCamlRuntime.",
));
}
if let InteropTypeDetail::BoxRoot { .. } = &data.return_interop_detail {
return Err(Error::new_spanned(
&data.user_return_type_ast, "BoxRoot<T> cannot be used as a return type directly. Return OCaml<T> instead.",
));
}
for arg in &data.processed_args {
if let InteropTypeDetail::Unit = &arg.type_detail {
return Err(Error::new_spanned(
&arg.original_rust_type,
"Unit type `()` is not a supported argument type directly. Use OCaml<()> if needed for placeholder.",
));
}
}
if data.noalloc {
for arg in &data.processed_args {
if let InteropTypeDetail::BoxRoot { .. } = &arg.type_detail {
return Err(Error::new_spanned(
&arg.original_rust_type,
"`BoxRoot<T>` arguments are not allowed when `noalloc` is used because BoxRoot implies allocation for rooting.",
));
}
}
}
Ok(())
}