extendr_macros/
extendr_conversion.rs
1use crate::{extendr_options::ExtendrOptions, wrappers};
2use proc_macro::TokenStream;
3use quote::quote;
4use syn::Attribute;
5use syn::Generics;
6use syn::Ident;
7use syn::Item;
8
9struct TypeFields {
10 ident: Ident,
11 generics: Generics,
12 attrs: Vec<Attribute>,
13}
14
15fn pull_fields(item: &Item) -> syn::Result<TypeFields> {
16 let fields = match item {
17 Item::Struct(str) => TypeFields {
18 ident: str.ident.clone(),
19 generics: str.generics.clone(),
20 attrs: str.attrs.clone(),
21 },
22 Item::Enum(str) => TypeFields {
23 ident: str.ident.clone(),
24 generics: str.generics.clone(),
25 attrs: str.attrs.clone(),
26 },
27 _ => {
28 return Err(syn::Error::new_spanned(
29 item,
30 "#[extendr] conversions can only be built for `struct` and `enum`",
31 ))
32 }
33 };
34 Ok(fields)
35}
36
37pub(crate) fn extendr_type_conversion(item: Item, opts: &ExtendrOptions) -> TokenStream {
38 match do_extendr_type_conversion(item, opts) {
39 Ok(result) => result,
40 Err(e) => e.to_compile_error().into(),
41 }
42}
43
44fn do_extendr_type_conversion(item: Item, _opts: &ExtendrOptions) -> syn::Result<TokenStream> {
45 let TypeFields {
46 ident: self_ty,
47 generics,
48 attrs,
49 } = pull_fields(&item)?;
50 if generics.const_params().count() != 0 {
51 return Err(syn::Error::new_spanned(
52 item,
53 "const params not allowed in #[extendr] impl",
54 ));
55 }
56 let mut self_ty_name = self_ty.to_string();
57 for gen in generics.type_params() {
58 self_ty_name.push('_');
59 self_ty_name.push_str(gen.ident.to_string().as_str());
60 }
61
62 let _doc_string = wrappers::get_doc_string(&attrs);
66
67 let conversion_impls = quote! {
68 impl TryFrom<Robj> for &#self_ty {
71 type Error = extendr_api::Error;
72
73 fn try_from(robj: Robj) -> extendr_api::Result<Self> {
74 Self::try_from(&robj)
75 }
76 }
77
78 impl TryFrom<Robj> for &mut #self_ty {
79 type Error = extendr_api::Error;
80
81 fn try_from(mut robj: Robj) -> extendr_api::Result<Self> {
82 Self::try_from(&mut robj)
83 }
84 }
85
86 impl TryFrom<&Robj> for &#self_ty {
88 type Error = extendr_api::Error;
89 fn try_from(robj: &Robj) -> extendr_api::Result<Self> {
90 use extendr_api::ExternalPtr;
91 unsafe {
92 let external_ptr: &ExternalPtr<#self_ty> = robj.try_into()?;
93 external_ptr.try_addr()
94 }
95 }
96 }
97
98 impl TryFrom<&mut Robj> for &mut #self_ty {
100 type Error = extendr_api::Error;
101 fn try_from(robj: &mut Robj) -> extendr_api::Result<Self> {
102 use extendr_api::ExternalPtr;
103 unsafe {
104 let external_ptr: &mut ExternalPtr<#self_ty> = robj.try_into()?;
105 external_ptr.try_addr_mut()
106 }
107 }
108 }
109 };
110
111 let output = TokenStream::from(quote! {
112 #item
113
114 #conversion_impls
115
116 impl From<#self_ty> for Robj {
118 fn from(value: #self_ty) -> Self {
119 use extendr_api::ExternalPtr;
120 unsafe {
121 let mut res: ExternalPtr<#self_ty> = ExternalPtr::new(value);
122 res.set_attrib(class_symbol(), #self_ty_name).unwrap();
123 res.into()
124 }
125 }
126 }
127
128 });
129 Ok(output)
130}