savvy_bindgen/ir/
savvy_impl.rs1use syn::parse_quote;
2
3use super::savvy_fn::{SavvyFn, SavvyFnType};
4use crate::utils::extract_docs;
5
6pub struct SavvyImpl {
7 pub docs: Vec<String>,
9 pub attrs: Vec<syn::Attribute>,
11 pub ty: syn::Ident,
13 pub fns: Vec<SavvyFn>,
15}
16
17impl SavvyImpl {
18 pub fn new(orig: &syn::ItemImpl) -> syn::Result<Self> {
19 let mut attrs = orig.attrs.clone();
20 attrs.retain(|attr| attr != &parse_quote!(#[savvy]));
22 let docs = extract_docs(attrs.as_slice());
24 let self_ty = orig.self_ty.as_ref();
25
26 let ty = match self_ty {
27 syn::Type::Path(type_path) => type_path.path.segments.last().unwrap().ident.clone(),
28 _ => {
29 return Err(syn::Error::new_spanned(self_ty, "Unexpected type"));
30 }
31 };
32
33 let fns = orig
34 .items
35 .clone()
36 .iter()
37 .filter_map(|f| match f {
38 syn::ImplItem::Fn(impl_item_fn) => {
39 let ty = self_ty.clone();
40 let fn_type = match impl_item_fn.sig.inputs.first() {
41 Some(syn::FnArg::Receiver(syn::Receiver {
42 reference,
43 mutability,
44 ..
45 })) => SavvyFnType::Method {
46 ty,
47 reference: reference.is_some(),
48 mutability: mutability.is_some(),
49 },
50 _ => SavvyFnType::AssociatedFunction(ty),
51 };
52
53 Some(SavvyFn::from_impl_fn(impl_item_fn, fn_type, self_ty))
54 }
55 _ => None,
56 })
57 .collect::<syn::Result<Vec<SavvyFn>>>()?;
58
59 Ok(Self {
60 docs,
61 attrs,
62 ty,
63 fns,
64 })
65 }
66
67 #[allow(dead_code)]
68 pub fn generate_inner_fns(&self) -> Vec<syn::ItemFn> {
69 self.fns.iter().map(|f| f.generate_inner_fn()).collect()
70 }
71
72 #[allow(dead_code)]
73 pub fn generate_ffi_fns(&self) -> Vec<syn::ItemFn> {
74 self.fns.iter().map(|f| f.generate_ffi_fn()).collect()
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::SavvyFnType::*;
81 use super::*;
82 use syn::parse_quote;
83
84 #[test]
85 fn test_impl() {
86 let item_impl: syn::ItemImpl = parse_quote!(
87 #[savvy]
88 impl Person {
89 fn new() -> Self {
90 Self {
91 name: "".to_string(),
92 }
93 }
94
95 fn set_name(&mut self, name: StringSexp) -> savvy::Result<()> {
96 self.name = name.iter().next().unwrap().to_string();
97 Ok(())
98 }
99
100 fn name(&self) -> savvy::Result<savvy::Sexp> {
101 let mut out = OwnedStringSexp::new(1);
102 out.set_elt(0, self.name.as_str());
103 Ok(out.into())
104 }
105
106 fn do_nothing() -> savvy::Result<()> {}
107 }
108 );
109
110 let parsed = SavvyImpl::new(&item_impl).expect("Failed to parse");
111 assert_eq!(parsed.ty.to_string().as_str(), "Person");
112
113 assert_eq!(parsed.fns.len(), 4);
114
115 assert_eq!(parsed.fns[0].fn_name.to_string().as_str(), "new");
116 assert!(matches!(parsed.fns[0].fn_type, AssociatedFunction(_)));
117
118 assert_eq!(parsed.fns[1].fn_name.to_string().as_str(), "set_name");
119 assert!(matches!(parsed.fns[1].fn_type, Method { .. }));
120
121 assert_eq!(parsed.fns[2].fn_name.to_string().as_str(), "name");
122 assert!(matches!(parsed.fns[2].fn_type, Method { .. }));
123
124 assert_eq!(parsed.fns[3].fn_name.to_string().as_str(), "do_nothing");
125 assert!(matches!(parsed.fns[3].fn_type, AssociatedFunction(_)));
126 }
127}