1use proc_macro2::Ident;
27use quote::{format_ident, quote};
28use std::{collections::HashMap, sync::Mutex};
29use syn::{parse_quote, punctuated::Punctuated, Expr, ExprLit, FnArg, ItemFn, Token, Type};
30
31use crate::extendr_options::ExtendrOptions;
32
33pub const META_PREFIX: &str = "meta__";
34pub const WRAP_PREFIX: &str = "wrap__";
35
36lazy_static::lazy_static! {
37 static ref STRUCT_DOCS: Mutex<HashMap<String, String>> = Mutex::new(HashMap::new());
38}
39
40pub fn register_struct_doc(name: &str, doc: &str) {
45 STRUCT_DOCS
46 .lock()
47 .unwrap()
48 .insert(name.to_string(), doc.to_string());
49}
50
51pub fn get_struct_doc(name: &str) -> String {
53 STRUCT_DOCS
54 .lock()
55 .unwrap()
56 .get(name)
57 .cloned()
58 .unwrap_or_default()
59}
60
61pub(crate) fn make_function_wrappers(
63 opts: &ExtendrOptions,
64 wrappers: &mut Vec<ItemFn>,
65 prefix: &str,
66 attrs: &[syn::Attribute],
67 sig: &mut syn::Signature,
68 self_ty: Option<&syn::Type>,
69) -> syn::Result<()> {
70 let rust_name = sig.ident.clone();
71
72 let r_name_str = if let Some(r_name) = opts.r_name.as_ref() {
73 r_name.clone()
74 } else {
75 sig.ident.to_string()
76 };
77
78 let mod_name = if let Some(mod_name) = opts.mod_name.as_ref() {
79 format_ident!("{}", mod_name)
80 } else {
81 sig.ident.clone()
82 };
83
84 let mod_name = sanitize_identifier(mod_name);
85 let wrap_name = format_ident!("{}{}{}", WRAP_PREFIX, prefix, mod_name);
86 let meta_name = format_ident!("{}{}{}", META_PREFIX, prefix, mod_name);
87
88 let rust_name_str = format!("{}", rust_name);
89 let c_name_str = format!("{}", mod_name);
90 let wrap_name_str = format!("{}", wrap_name);
91 let doc_string = get_doc_string(attrs);
92 let return_type_string = get_return_type(sig);
93 let opts_invisible = match opts.invisible {
94 Some(true) => quote!(Some(true)),
95 Some(false) => quote!(Some(false)),
96 None => quote!(None),
97 };
98
99 let inputs = &mut sig.inputs;
100 let has_self = matches!(inputs.iter().next(), Some(FnArg::Receiver(_)));
101
102 let call_name = if has_self {
103 let is_mut = match inputs.iter().next() {
104 Some(FnArg::Receiver(ref receiver)) => receiver.mutability.is_some(),
105 _ => false,
106 };
107 if is_mut {
108 quote! { extendr_api::unwrap_or_throw_error(
110 <&mut #self_ty>::try_from(&mut _self_robj)
111 ).#rust_name }
112 } else {
113 quote! { extendr_api::unwrap_or_throw_error(
115 <&#self_ty>::try_from(&_self_robj)
116 ).#rust_name }
117 }
118 } else if let Some(ref self_ty) = &self_ty {
119 quote! { <#self_ty>::#rust_name }
121 } else {
122 quote! { #rust_name }
124 };
125
126 let formal_args = inputs
128 .iter()
129 .map(|input| translate_formal(input, self_ty))
130 .collect::<syn::Result<Punctuated<FnArg, Token![,]>>>()?;
131
132 let sexp_args = formal_args
134 .clone()
135 .into_iter()
136 .map(|x| match x {
137 FnArg::Receiver(_) => unreachable!(),
139 FnArg::Typed(ref typed) => match typed.pat.as_ref() {
140 syn::Pat::Ident(ref pat_ident) => pat_ident.ident.clone(),
141 _ => unreachable!(),
142 },
143 })
144 .collect::<Vec<Ident>>();
145
146 let convert_args: Vec<syn::Stmt> = inputs
148 .iter()
149 .map(translate_to_robj)
150 .collect::<syn::Result<Vec<syn::Stmt>>>()?;
151
152 let actual_args: Punctuated<Expr, Token![,]> =
153 inputs.iter().filter_map(translate_actual).collect();
154
155 let meta_args: Vec<Expr> = inputs
156 .iter_mut()
157 .map(|input| translate_meta_arg(input, self_ty))
158 .collect::<syn::Result<Vec<Expr>>>()?;
159
160 let rng_start = if opts.use_rng {
173 {
174 quote!(single_threaded(|| unsafe {
175 extendr_api::GetRNGstate();
176 });)
177 }
178 } else {
179 Default::default()
180 };
181 let rng_end = if opts.use_rng {
182 {
183 quote!(single_threaded(|| unsafe {
184 extendr_api::PutRNGstate();
185 });)
186 }
187 } else {
188 Default::default()
189 };
190
191 let return_is_ref_self = {
197 match sig.output {
198 syn::ReturnType::Default => false,
200 syn::ReturnType::Type(_, ref return_type) => match return_type.as_ref() {
203 Type::Reference(ref reference_type) => {
204 if let Type::Path(path) = reference_type.elem.as_ref() {
206 let is_typename_impl_type = self_ty
207 .map(|x| x == reference_type.elem.as_ref())
208 .unwrap_or(false);
209 path.path.is_ident("Self") || is_typename_impl_type
210 } else {
211 false
212 }
213 }
214 _ => false,
215 },
216 }
217 };
218
219 let return_type_conversion = if return_is_ref_self {
220 quote!(
223 let return_ref_to_self = #call_name(#actual_args);
224
225 #(
226 let arg_ref = extendr_api::R_ExternalPtrAddr(#sexp_args)
227 .cast::<Box<dyn std::any::Any>>()
228 .as_ref()
229 .unwrap()
230 .downcast_ref::<#self_ty>()
231 .unwrap();
232 if std::ptr::addr_eq(
233 arg_ref,
234 std::ptr::from_ref(return_ref_to_self)) {
235 return Ok(extendr_api::Robj::from_sexp(#sexp_args))
236 }
237 )*
238 Err(Error::ExpectedExternalPtrReference.into())
239 )
240 } else {
241 quote!(Ok(extendr_api::Robj::from(#call_name(#actual_args))))
242 };
243
244 wrappers.push(parse_quote!(
246 #[no_mangle]
247 #[allow(non_snake_case, clippy::not_unsafe_ptr_arg_deref)]
248 pub extern "C" fn #wrap_name(#formal_args) -> extendr_api::SEXP {
249 use extendr_api::robj::*;
250
251 #rng_start
253
254 let wrap_result_state: std::result::Result<
255 std::result::Result<extendr_api::Robj, Box<dyn std::error::Error>>,
256 Box<dyn std::any::Any + Send>
257 > = unsafe {
258 std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || -> std::result::Result<extendr_api::Robj, Box<dyn std::error::Error>> {
259 #(#convert_args)*
260 #return_type_conversion
261 }))
262 };
263
264 #rng_end
266
267 match wrap_result_state {
269 Ok(Ok(zz)) => {
270 return unsafe { zz.get() };
271 }
272 Ok(Err(conversion_err)) => {
274 let err_string = conversion_err.to_string();
275 drop(conversion_err); extendr_api::throw_r_error(&err_string);
277 }
278 Err(unwind_err) => {
280 let panic_msg = if let Some(s) = unwind_err.downcast_ref::<&str>() {
281 (*s).to_string()
282 } else if let Some(s) = unwind_err.downcast_ref::<String>() {
283 s.clone()
284 } else {
285 format!("User function panicked: {}", #r_name_str)
286 };
287
288 extendr_api::throw_r_error(&panic_msg);
289 }
290 }
291 }
292 ));
293
294 wrappers.push(parse_quote!(
296 #[allow(non_snake_case)]
297 fn #meta_name(metadata: &mut Vec<extendr_api::metadata::Func>) {
298 let mut args = vec![#(#meta_args,)*];
299
300 metadata.push(extendr_api::metadata::Func {
301 doc: #doc_string,
302 rust_name: #rust_name_str,
303 r_name: #r_name_str,
304 c_name: #wrap_name_str,
305 mod_name: #c_name_str,
306 args: args,
307 return_type: #return_type_string,
308 func_ptr: #wrap_name as * const u8,
309 hidden: false,
310 invisible: #opts_invisible,
311 })
312 }
313 ));
314
315 Ok(())
316}
317
318pub fn get_doc_string(attrs: &[syn::Attribute]) -> String {
320 let mut res = String::new();
321 for attr in attrs {
322 if !attr.path().is_ident("doc") {
323 continue;
324 }
325
326 if let syn::Meta::NameValue(ref nv) = attr.meta {
327 if let Expr::Lit(ExprLit {
328 lit: syn::Lit::Str(ref litstr),
329 ..
330 }) = nv.value
331 {
332 if !res.is_empty() {
333 res.push('\n');
334 }
335 res.push_str(&litstr.value());
336 }
337 }
338 }
339 res
340}
341
342pub fn get_return_type(sig: &syn::Signature) -> String {
343 match &sig.output {
344 syn::ReturnType::Default => "()".into(),
345 syn::ReturnType::Type(_, ref rettype) => type_name(rettype),
346 }
347}
348
349pub fn mangled_type_name(type_: &Type) -> String {
350 let src = quote!( #type_ ).to_string();
351 let mut res = String::new();
352 for c in src.chars() {
353 if c != ' ' {
354 if c.is_alphanumeric() {
355 res.push(c)
356 } else {
357 let f = format!("_{:02x}", c as u32);
358 res.push_str(&f);
359 }
360 }
361 }
362 res
363}
364
365pub fn type_name(type_: &Type) -> String {
374 match type_ {
375 Type::Path(syn::TypePath { path, .. }) => {
376 if let Some(ident) = path.get_ident() {
377 ident.to_string()
378 } else if path.segments.len() == 1 {
379 let seg = path.segments.clone().into_iter().next().unwrap();
380 seg.ident.to_string()
381 } else {
382 mangled_type_name(type_)
383 }
384 }
385 Type::Group(syn::TypeGroup { elem, .. }) => type_name(elem),
386 Type::Reference(syn::TypeReference { elem, .. }) => type_name(elem),
387 Type::Paren(syn::TypeParen { elem, .. }) => type_name(elem),
388 Type::Ptr(syn::TypePtr { elem, .. }) => type_name(elem),
389 _ => mangled_type_name(type_),
390 }
391}
392
393pub fn translate_formal(input: &FnArg, self_ty: Option<&syn::Type>) -> syn::Result<FnArg> {
395 match input {
396 FnArg::Typed(ref pattype) => {
398 let pat = pattype.pat.as_ref();
399 let pat_ident = translate_only_alias(pat)?;
401 Ok(parse_quote! { #pat_ident: extendr_api::SEXP })
402 }
403 FnArg::Receiver(ref receiver) => {
405 if !receiver.attrs.is_empty() || receiver.reference.is_none() {
406 return Err(syn::Error::new_spanned(
407 input,
408 "expected &self or &mut self",
409 ));
410 }
411 if self_ty.is_none() {
412 return Err(syn::Error::new_spanned(
413 input,"found &self in non-impl function - have you missed the #[extendr] before the impl?"
414 ));
415 }
416 Ok(parse_quote! { _self : extendr_api::SEXP })
417 }
418 }
419}
420
421fn translate_only_alias(pat: &syn::Pat) -> Result<&Ident, syn::Error> {
426 Ok(match pat {
427 syn::Pat::Ident(ref pat_ident) => &pat_ident.ident,
428 _ => {
429 return Err(syn::Error::new_spanned(
430 pat,
431 "failed to translate name of argument",
432 ));
433 }
434 })
435}
436
437fn translate_meta_arg(input: &mut FnArg, self_ty: Option<&syn::Type>) -> syn::Result<Expr> {
439 match input {
440 FnArg::Typed(ref mut pattype) => {
442 let pat = pattype.pat.as_ref();
443 let ty = pattype.ty.as_ref();
444 let pat_ident = translate_only_alias(pat)?;
447 let name_string = quote! { #pat_ident }.to_string();
448 let type_string = type_name(ty);
449 let is_option = matches!(ty, Type::Path(p) if p.path.segments.last().is_some_and(|seg| seg.ident == "Option"));
450 let default = if let Some(default) = get_defaults(&mut pattype.attrs) {
451 quote!(Some(#default))
452 } else if let Some(default) = get_named_lit(&mut pattype.attrs, "default") {
453 quote!(Some(#default))
454 } else if is_option {
455 quote!(Some("NULL"))
456 } else {
457 quote!(None)
458 };
459 Ok(parse_quote! {
460 extendr_api::metadata::Arg {
461 name: #name_string,
462 arg_type: #type_string,
463 default: #default
464 }
465 })
466 }
467 FnArg::Receiver(ref receiver) => {
469 if !receiver.attrs.is_empty() || receiver.reference.is_none() {
470 return Err(syn::Error::new_spanned(
471 input,
472 "expected &self or &mut self",
473 ));
474 }
475 if self_ty.is_none() {
476 return Err(syn::Error::new_spanned(
477 input,
478 "found &self in non-impl function - have you missed the #[extendr] before the impl?"
479 )
480 );
481 }
482 let type_string = type_name(self_ty.unwrap());
483 Ok(parse_quote! {
484 extendr_api::metadata::Arg {
485 name: "self",
486 arg_type: #type_string,
487 default: None
488 }
489 })
490 }
491 }
492}
493
494fn get_defaults(attrs: &mut Vec<syn::Attribute>) -> Option<String> {
496 use syn::Lit;
497
498 let mut new_attrs = Vec::new();
499 let mut res = None;
500
501 for i in attrs.drain(0..) {
502 if let syn::Meta::List(ref meta_list) = i.meta {
503 if meta_list.path.is_ident("extendr") {
504 let mut default_value = None;
505 let mut theres_default = false;
506
507 let parse_result = meta_list.parse_nested_meta(|meta| {
508 if meta.path.is_ident("default") {
509 theres_default = true;
510 let value = meta.value()?;
511 if let Ok(Lit::Str(litstr)) = value.parse() {
512 default_value = Some(litstr.value());
513 }
514 }
515 Ok(())
516 });
517
518 if parse_result.is_ok() && theres_default {
519 res = default_value;
520 continue;
521 }
522 }
523 }
524
525 new_attrs.push(i);
526 }
527 *attrs = new_attrs;
528 res
529}
530
531fn type_needs_mut_robj(ty: &Type) -> bool {
532 match ty {
533 Type::Reference(reference) => {
534 reference.mutability.is_some() || type_needs_mut_robj(&reference.elem)
535 }
536 Type::Path(path) => path.path.segments.iter().any(|seg| match &seg.arguments {
537 syn::PathArguments::AngleBracketed(args) => args.args.iter().any(|arg| {
538 if let syn::GenericArgument::Type(inner_ty) = arg {
539 type_needs_mut_robj(inner_ty)
540 } else {
541 false
542 }
543 }),
544 _ => false,
545 }),
546 Type::Tuple(tuple) => tuple.elems.iter().any(type_needs_mut_robj),
547 _ => false,
548 }
549}
550
551fn translate_to_robj(input: &FnArg) -> syn::Result<syn::Stmt> {
556 match input {
557 FnArg::Typed(ref pattype) => {
558 let pat = &pattype.pat.as_ref();
559 if let syn::Pat::Ident(ref ident) = pat {
560 let varname = format_ident!("_{}_robj", ident.ident);
561 let ident = &ident.ident;
562 let mut_token = type_needs_mut_robj(&pattype.ty);
563 if mut_token {
565 Ok(
566 parse_quote! { let mut #varname = extendr_api::robj::Robj::from_sexp(#ident); },
567 )
568 } else {
569 Ok(parse_quote! { let #varname = extendr_api::robj::Robj::from_sexp(#ident); })
570 }
571 } else {
572 Err(syn::Error::new_spanned(
573 input,
574 "expect identifier as arg name",
575 ))
576 }
577 }
578 FnArg::Receiver(_) => {
579 Ok(parse_quote! { let mut _self_robj = extendr_api::robj::Robj::from_sexp(_self); })
581 }
582 }
583}
584
585fn translate_actual(input: &FnArg) -> Option<Expr> {
587 match input {
588 FnArg::Typed(ref pattype) => {
589 let pat = &pattype.pat.as_ref();
590 if let syn::Pat::Ident(ref ident) = pat {
591 let varname = format_ident!("_{}_robj", ident.ident);
592 let arg_name = ident.ident.to_string();
593 let robj_to_try: Expr = if type_needs_mut_robj(&pattype.ty) {
594 parse_quote! { &mut #varname }
595 } else {
596 parse_quote! { &#varname }
597 };
598 Some(parse_quote! {
599 (#robj_to_try)
600 .try_into()
601 .map_err(|error| extendr_api::error::Error::Other(format!(
602 "failed to convert argument `{}` from R: {}",
603 #arg_name,
604 error
605 )))?
606 })
607 } else {
608 None
609 }
610 }
611 FnArg::Receiver(_) => {
612 None
614 }
615 }
616}
617
618fn get_named_lit(attrs: &mut Vec<syn::Attribute>, name: &str) -> Option<String> {
622 let mut new_attrs = Vec::new();
623 let mut res = None;
624 for a in attrs.drain(0..) {
625 if let syn::Meta::NameValue(ref nv) = a.meta {
626 if nv.path.is_ident(name) {
627 if let Expr::Lit(ExprLit {
628 lit: syn::Lit::Str(ref litstr),
629 ..
630 }) = nv.value
631 {
632 eprintln!("#[default = \"arg\"] is deprecated. Use #[extendr(default = \"arg\")] instead.");
633 res = Some(litstr.value());
634 continue;
635 }
636 }
637 }
638
639 new_attrs.push(a);
640 }
641 *attrs = new_attrs;
642 res
643}
644
645fn sanitize_identifier(ident: Ident) -> Ident {
648 static PREFIX: &str = "r#";
649 let (ident, span) = (ident.to_string(), ident.span());
650 let ident = match ident.strip_prefix(PREFIX) {
651 Some(ident) => ident.into(),
652 None => ident,
653 };
654
655 Ident::new(&ident, span)
656}