savvy_bindgen/ir/
savvy_struct.rs

1use syn::{parse_quote, spanned::Spanned};
2
3use crate::extract_docs;
4
5pub struct SavvyStruct {
6    /// Doc comments
7    pub docs: Vec<String>,
8    /// Attributes except for `#[savvy]`
9    pub attrs: Vec<syn::Attribute>,
10    /// Original struct name
11    pub ty: syn::Ident,
12}
13
14impl SavvyStruct {
15    pub fn new(orig: &syn::ItemStruct) -> syn::Result<Self> {
16        if let Some(lt) = orig.generics.lifetimes().next() {
17            return Err(syn::Error::new(
18                lt.span(),
19                "#[savvy] macro doesn't support lifetime",
20            ));
21        }
22
23        let mut attrs = orig.attrs.clone();
24        // Remove #[savvy]
25        attrs.retain(|attr| attr != &parse_quote!(#[savvy]));
26        // Extract doc comments
27        let docs = extract_docs(attrs.as_slice());
28        let ty = orig.ident.clone();
29
30        Ok(Self { docs, attrs, ty })
31    }
32
33    pub fn generate_try_from_impls(&self) -> Vec<syn::ItemImpl> {
34        let ty = &self.ty;
35
36        let impl_into_external_pointer: syn::ItemImpl =
37            parse_quote!(impl savvy::IntoExtPtrSexp for #ty {});
38
39        let impl_try_from_ty_to_sexp: syn::ItemImpl = parse_quote!(
40            impl TryFrom<#ty> for savvy::Sexp {
41                type Error = savvy::Error;
42
43                fn try_from(value: #ty) -> savvy::Result<Self> {
44                    use savvy::IntoExtPtrSexp;
45
46                    Ok(value.into_external_pointer())
47                }
48            }
49        );
50
51        let impl_try_from_sexp_to_ref_ty: syn::ItemImpl = parse_quote!(
52            impl TryFrom<savvy::Sexp> for &#ty {
53                type Error = savvy::Error;
54
55                fn try_from(value: savvy::Sexp) -> savvy::Result<Self> {
56                    // Return error if the SEXP is not an external pointer
57                    value.assert_external_pointer()?;
58
59                    let x = unsafe { savvy::get_external_pointer_addr(value.0)? as *mut #ty };
60                    let res = unsafe { x.as_ref() };
61                    res.ok_or(savvy::savvy_err!("Failed to convert the external pointer to the Rust object"))
62                }
63            }
64        );
65
66        let impl_try_from_sexp_to_ref_mut_ty: syn::ItemImpl = parse_quote!(
67            impl TryFrom<savvy::Sexp> for &mut #ty {
68                type Error = savvy::Error;
69
70                fn try_from(value: savvy::Sexp) -> savvy::Result<Self> {
71                    // Return error if the SEXP is not an external pointer
72                    value.assert_external_pointer()?;
73
74                    let x = unsafe { savvy::get_external_pointer_addr(value.0)? as *mut #ty };
75                    let res = unsafe { x.as_mut() };
76                    res.ok_or(savvy::savvy_err!("Failed to convert the external pointer to the Rust object"))
77                }
78            }
79        );
80
81        let impl_try_from_sexp_to_ty: syn::ItemImpl = parse_quote!(
82            impl TryFrom<savvy::Sexp> for #ty {
83                type Error = savvy::Error;
84
85                fn try_from(value: savvy::Sexp) -> savvy::Result<Self> {
86                    // Return error if the SEXP is not an external pointer
87                    value.assert_external_pointer()?;
88
89                    unsafe { savvy::take_external_pointer_value::<#ty>(value.0) }
90                }
91            }
92        );
93
94        vec![
95            impl_into_external_pointer,
96            impl_try_from_ty_to_sexp,
97            impl_try_from_sexp_to_ref_ty,
98            impl_try_from_sexp_to_ref_mut_ty,
99            impl_try_from_sexp_to_ty,
100        ]
101    }
102}