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
19pub 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 pub docs: Vec<String>,
34 pub ty: syn::Ident,
36 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 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 impls.sort_by_key(|(k, _)| k.clone());
137
138 let mut parse_errors: Vec<SavvyParseError> = Vec::new();
139
140 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 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 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 } else {
186 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()); Ok(fns)
200 } else {
201 Err(parse_errors)
202 }
203}