savvy_bindgen/ir/
mod.rs

1use std::collections::HashMap;
2
3use syn::Ident;
4
5use crate::{SavvyEnum, SavvyFn, SavvyImpl, SavvyStruct};
6
7pub mod savvy_enum;
8pub mod savvy_fn;
9pub mod savvy_impl;
10pub mod savvy_struct;
11
12pub struct ParsedTestCase {
13    pub label: String,
14    pub orig_code: String,
15    pub location: String,
16    pub code: String,
17}
18
19// For main.rs
20pub struct ParsedResult {
21    pub base_path: std::path::PathBuf,
22    pub bare_fns: Vec<SavvyFn>,
23    pub impls: Vec<SavvyImpl>,
24    pub structs: Vec<SavvyStruct>,
25    pub enums: Vec<SavvyEnum>,
26    pub mod_path: Vec<String>,
27    pub child_mods: Vec<String>,
28    pub tests: Vec<ParsedTestCase>,
29}
30
31pub struct SavvyMergedImpl {
32    /// Doc comments
33    pub docs: Vec<String>,
34    /// Original type name
35    pub ty: syn::Ident,
36    /// Methods and accociated functions
37    pub fns: Vec<SavvyFn>,
38}
39
40pub struct MergedResult {
41    pub bare_fns: Vec<SavvyFn>,
42    pub impls: Vec<(Ident, SavvyMergedImpl)>,
43    pub enums: Vec<SavvyEnum>,
44}
45
46#[derive(Debug, Clone)]
47pub enum SavvyParseError {
48    ConflictingDefinitions(syn::Ident),
49}
50
51impl std::fmt::Display for SavvyParseError {
52    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53        match self {
54            SavvyParseError::ConflictingDefinitions(ident) => {
55                write!(f, "Different definitions are found for fn `{ident}`")
56            }
57        }
58    }
59}
60
61pub fn merge_parsed_results(
62    results: Vec<ParsedResult>,
63) -> Result<MergedResult, Vec<SavvyParseError>> {
64    let mut bare_fns: Vec<SavvyFn> = Vec::new();
65    let mut impl_map: HashMap<Ident, SavvyMergedImpl> = HashMap::new();
66    let mut enums: Vec<SavvyEnum> = Vec::new();
67
68    for result in results {
69        let mut fns = result.bare_fns;
70        bare_fns.append(&mut fns);
71
72        for i in result.impls {
73            let key = i.ty.clone();
74            match impl_map.get_mut(&key) {
75                Some(merged) => {
76                    let mut fns = i.fns;
77                    merged.fns.append(&mut fns);
78                }
79                None => {
80                    impl_map.insert(
81                        key,
82                        SavvyMergedImpl {
83                            docs: Vec::new(),
84                            ty: i.ty,
85                            fns: i.fns,
86                        },
87                    );
88                }
89            }
90        }
91
92        // get documents from struct
93        for s in result.structs {
94            let key = s.ty.clone();
95            match impl_map.get_mut(&key) {
96                Some(merged) => {
97                    merged.docs = s.docs;
98                }
99                None => {
100                    impl_map.insert(
101                        key,
102                        SavvyMergedImpl {
103                            docs: Vec::new(),
104                            ty: s.ty,
105                            fns: Vec::new(),
106                        },
107                    );
108                }
109            }
110        }
111
112        for e in result.enums {
113            let key = e.ty.clone();
114            match impl_map.get_mut(&key) {
115                Some(merged) => {
116                    merged.docs.clone_from(&e.docs);
117                }
118                None => {
119                    impl_map.insert(
120                        key,
121                        SavvyMergedImpl {
122                            docs: e.docs.clone(),
123                            ty: e.ty.clone(),
124                            fns: Vec::new(),
125                        },
126                    );
127                }
128            }
129            enums.push(e);
130        }
131    }
132
133    let mut impls = impl_map.into_iter().collect::<Vec<_>>();
134
135    // in order to make the wrapper generation deterministic, sort by the type
136    impls.sort_by_key(|(k, _)| k.clone());
137
138    let mut parse_errors: Vec<SavvyParseError> = Vec::new();
139
140    // validate bare functions
141    let bare_fns = match remove_duplicated_fns(&bare_fns) {
142        Ok(fns) => fns,
143        Err(mut e) => {
144            parse_errors.append(&mut e);
145            Vec::new()
146        }
147    };
148
149    // validate impl functions
150    for (_, merged) in impls.iter_mut() {
151        merged.fns = match remove_duplicated_fns(&merged.fns) {
152            Ok(fns) => fns,
153            Err(mut e) => {
154                parse_errors.append(&mut e);
155                Vec::new()
156            }
157        };
158    }
159
160    if parse_errors.is_empty() {
161        Ok(MergedResult {
162            bare_fns,
163            impls,
164            enums,
165        })
166    } else {
167        Err(parse_errors)
168    }
169}
170
171fn remove_duplicated_fns(fns: &[SavvyFn]) -> Result<Vec<SavvyFn>, Vec<SavvyParseError>> {
172    let mut fn_map: HashMap<syn::Ident, SavvyFn> = HashMap::new();
173    let mut parse_errors: Vec<SavvyParseError> = Vec::new();
174
175    for f in fns {
176        match fn_map.get(&f.fn_name) {
177            // If there's already the same name of the function, check if the signatures match.
178            Some(f_entry) => {
179                let args1 = &f.args;
180                let args2 = &f_entry.args;
181                if args1.len() == args2.len()
182                    && args1.iter().zip(args2.iter()).all(|(a1, a2)| a1 == a2)
183                {
184                    // do nothing if these share the same signature
185                } else {
186                    // raise an error if the signatures differ
187                    parse_errors.push(SavvyParseError::ConflictingDefinitions(f.fn_name.clone()));
188                }
189            }
190            None => {
191                fn_map.insert(f.fn_name.clone(), f.clone());
192            }
193        }
194    }
195
196    if parse_errors.is_empty() {
197        let mut fns: Vec<SavvyFn> = fn_map.into_values().collect();
198        fns.sort_by_key(|f| f.fn_name.clone()); // make the order deterministic
199        Ok(fns)
200    } else {
201        Err(parse_errors)
202    }
203}