1use ra_ap_syntax::{ast::{self, AstNode, HasName, HasModuleItem}, match_ast, SourceFile, Parse};
2
3use crate::uml_entity::*;
4use super::StringParser;
5
6trait HasUMLEntity {
7 fn get_uml_entities(&self) -> Vec<UMLEntity>;
8}
9
10impl HasUMLEntity for ast::Struct {
11 fn get_uml_entities(&self) -> Vec<UMLEntity> {
12 let mut results = vec![];
13 let mut record_fields = vec![];
14 for node in self.syntax().descendants() {
15 match_ast! {
16 match node {
17 ast::RecordField(rf) => {
18 record_fields.push(rf.to_string());
20
21 let rf_str = rf.to_string();
23 if rf_str.contains(r"*mut") || rf_str.contains(r"*const") {
24 get_paths_str_from_ast_node(rf)
25 .iter()
26 .for_each(|p| results.push(
27 UMLEntity::UMLRelation(UMLRelation::new(&self.name().unwrap().text().to_string(), &p, UMLRelationKind::UMLAggregation)))
28 )
29 } else if !rf_str.contains(r"*mut") && !rf_str.contains(r"*const") {
30 get_paths_str_from_ast_node(rf)
31 .iter()
32 .for_each(|p| results.push(
33 UMLEntity::UMLRelation(UMLRelation::new(&self.name().unwrap().text().to_string(), &p, UMLRelationKind::UMLComposition)))
34 )
35 }
36 },
37 _ => ()
38 }
39 }
40 };
43 results.push(UMLEntity::UMLClass(UMLClass::new(&self.name().unwrap().text().to_string(), record_fields, vec![], UMLClassKind::UMLClass)));
44 results
45 }
46}
47
48impl HasUMLEntity for ast::Trait {
49 fn get_uml_entities(&self) -> Vec<UMLEntity> {
50 let mut results = vec![];
51 results.push(UMLEntity::UMLClass(UMLClass::new(&self.name().unwrap().text().to_string(), vec![], vec![], UMLClassKind::UMLTrait)));
53
54 for node in self.syntax().descendants() {
55 match_ast! {
56 match node {
57 ast::RecordField(rf) => {
58 let rf_str = rf.to_string();
60 if rf_str.contains(r"*mut") || rf_str.contains(r"*const") {
61 get_paths_str_from_ast_node(rf)
62 .iter()
63 .for_each(|p| results.push(
64 UMLEntity::UMLRelation(UMLRelation::new(&self.name().unwrap().text().to_string(), &p, UMLRelationKind::UMLAggregation)))
65 )
66 } else if !rf_str.contains(r"*mut") && !rf_str.contains(r"*const") {
67 get_paths_str_from_ast_node(rf)
68 .iter()
69 .for_each(|p| results.push(
70 UMLEntity::UMLRelation(UMLRelation::new(&self.name().unwrap().text().to_string(), &p, UMLRelationKind::UMLComposition)))
71 )
72 }
73 },
74 _ => ()
75 }
76 }
77 };
78
79 results
80 }
81}
82
83impl HasUMLEntity for ast::Impl {
84 fn get_uml_entities(&self) -> Vec<UMLEntity> {
85 let mut results = vec![];
86
87 let mut impl_fn_names = vec![];
89 let struct_name: String = strip_trait_bound(&self.self_ty().unwrap().to_string());
90
91 let mut dep_list: Vec<String> = vec![];
92 let mut asct_list: Vec<String> = vec![];
93 for node in self.syntax().descendants() {
94 match_ast! {
95 match node {
96 ast::Fn(f) => {
98 impl_fn_names.push(get_fn_full_name(&f));
99 },
100 ast::ParamList(pl) => {
102 dep_list.append(&mut get_paths_str_from_ast_node(pl));
103 },
104 ast::BlockExpr(ex) => {
105 dep_list.append(&mut get_paths_str_from_ast_node(ex));
106 },
107 ast::RetType(rt) => {
108 asct_list.append(&mut get_paths_str_from_ast_node(rt));
109 },
110 _ => ()
111 }
112 }
113 }
114
115 results.extend(
117 asct_list.iter().map(|p| UMLEntity::UMLRelation(UMLRelation::new(&p, &struct_name, UMLRelationKind::UMLAssociationUni)))
118 );
119 let mut dep_set: Vec<&String> = dep_list.iter().filter(|p| !asct_list.contains(p)).collect();
120 dep_set.sort();
121 dep_set.dedup();
122 results.extend(
123 dep_set.iter().map(|p| UMLEntity::UMLRelation(UMLRelation::new(&p, &struct_name, UMLRelationKind::UMLDependency)))
124 );
125
126
127 match self.trait_() {
129 Some(tt) => {
130 results.push(
131 UMLEntity::UMLRelation(UMLRelation::new(&struct_name, &strip_trait_bound(&tt.to_string()), UMLRelationKind::UMLRealization))
132 );
133 },
135 None => {
136 results.push(UMLEntity::UMLClass(UMLClass::new(&struct_name, vec![], impl_fn_names, UMLClassKind::UMLClass)));
137 }
138 }
139
140
141 results
142 }
143}
144
145fn strip_trait_bound(s: &str) -> String {
146 let class_name: Vec<&str> = s.split(r"<").collect();
147 String::from(class_name[0])
148}
149
150fn get_paths_str_from_ast_node(node: impl ast::AstNode) -> Vec<String> {
151 let mut results = vec![];
152 for node in node.syntax().descendants() {
153 match_ast! {
154 match node {
155 ast::Path(p) => {
156 results.push(p.to_string())
157 },
158 _ => ()
159 }
160 }
161 };
164 results
165}
166
167impl HasUMLEntity for ast::Fn {
168 fn get_uml_entities(&self) -> Vec<UMLEntity> {
169 let mut results: Vec<UMLEntity> = vec![];
170 let f_name = self.name().unwrap().text().to_string();
171 let full_name: String = get_fn_full_name(self);
172
173 for node in self.syntax().descendants() {
175 match_ast! {
176 match node {
177 ast::CallExpr(it) => {
178 let call_name = get_call_expr_fn_names(it);
179 results.push(UMLEntity::UMLRelation(UMLRelation::new(&f_name, &call_name, UMLRelationKind::UMLDependency)))
180 },
181 _ => {
182 },
185 }
186 }
187 }
188 results.push(UMLEntity::UMLFn(UMLFn::new(&f_name, &full_name)));
189 results
190 }
191}
192
193fn get_fn_full_name(f: &ast::Fn) -> String {
194 let f_name = f.name().unwrap().text().to_string();
195 let mut full_name: String = f_name.clone();
196
197 for node in f.syntax().descendants() {
199 match_ast! {
200 match node {
201 ast::ParamList(pl) => {
202 full_name.push_str(&pl.to_string());
203 },
204 ast::RetType(rt) => {
205 full_name.push_str(" ");
206 full_name.push_str(&rt.to_string());
207 },
208 _ => {
209 },
212 }
213 }
214 }
215 full_name
216}
217
218fn get_call_expr_fn_names(call_exp: ast::CallExpr) -> String {
219 let call_expr = call_exp.to_string();
220 let call_names: Vec<&str> = call_expr.split("(").collect();
221 String::from(call_names[0])
222}
223
224pub struct AstParser;
225
226impl StringParser for AstParser {
227 fn parse_string(input: &str) -> UMLGraph {
228 let parse: Parse<SourceFile> = SourceFile::parse(input);
229 let file: SourceFile = parse.tree();
230 let mut uml_graph = UMLGraph::new();
231 let mut uml_entities: Vec<UMLEntity> = vec![];
232
233 for item in file.items() {
235 match item {
236 ast::Item::Fn(f) => {
237 uml_entities.append(&mut f.get_uml_entities());
238 },
239 ast::Item::Impl(ip) => {
240 uml_entities.append(&mut ip.get_uml_entities());
241 },
242 ast::Item::Struct(st) => {
243 uml_entities.append(&mut st.get_uml_entities());
244 },
245 ast::Item::Trait(tt) => {
246 uml_entities.append(&mut tt.get_uml_entities());
247 },
248 _ => (),
249 }
250 }
251
252 let mut relations: Vec<UMLRelation> = vec![];
254 for e in uml_entities {
255 match e {
256 UMLEntity::UMLClass(c) => uml_graph.add_struct(c),
257 UMLEntity::UMLFn(f) => uml_graph.add_fn(f),
258 UMLEntity::UMLRelation(r) => {
259 relations.push(r);
261 },
262 }
263 }
264 for rel in relations {
265 uml_graph.add_relation(rel);
266 }
267
268 uml_graph
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275
276 #[test]
277 fn test_parse_fn() {
278 let code: &str = r#"
279 fn main() {
280 println!("Hello, world!");
281 }
282 "#;
283 let parsed_graph = AstParser::parse_string(code);
284 let mut target_graph: UMLGraph = UMLGraph::new();
285 target_graph.add_fn(UMLFn::new("main", "main()"));
286 assert_eq!(parsed_graph, target_graph);
287 }
288
289 #[test]
290 fn test_parse_struct() {
291 let code: &str = r#"
292 pub struct Mock;
293 impl Mock {
294 pub fn mock_fn() {}
295 }
296 "#;
297 let parsed_graph = AstParser::parse_string(code);
298 let mut target_graph: UMLGraph = UMLGraph::new();
299 target_graph.add_struct(UMLClass::new("Mock", vec![], vec![String::from("mock_fn()")], UMLClassKind::UMLClass));
300 assert_eq!(parsed_graph, target_graph);
301 }
302
303 #[test]
304 fn test_fn_dependency() {
305 let code: &str = r#"
306 fn main() {
307 hello();
308 }
309 fn hello() {}
310 "#;
311 let parsed_graph = AstParser::parse_string(code);
312 let mut target_graph: UMLGraph = UMLGraph::new();
313
314 target_graph.add_fn(UMLFn::new("main", "main()"));
315 target_graph.add_fn(UMLFn::new("hello", "hello()"));
316 target_graph.add_relation(UMLRelation::new("main", "hello", UMLRelationKind::UMLDependency));
317
318 assert_eq!(parsed_graph, target_graph);
319 }
320
321 #[test]
322 fn test_class_dependency() {
323 let code: &str = r#"
324 pub struct Mock;
325 impl Mock {
326 pub fn mock_fn() { f1(f2()) }
327 }
328 fn f1(i: usize) {}
329 fn f2() -> usize { 0 }
330 "#;
331 let parsed_graph = AstParser::parse_string(code);
332 let mut target_graph: UMLGraph = UMLGraph::new();
333
334 target_graph.add_struct(UMLClass::new("Mock", vec![], vec![String::from("mock_fn()")], UMLClassKind::UMLClass));
335 target_graph.add_fn(UMLFn::new("f1", "f1(i: usize)"));
336 target_graph.add_fn(UMLFn::new("f2", "f2() -> usize"));
337 target_graph.add_relation(UMLRelation::new("f1", "Mock", UMLRelationKind::UMLDependency));
338 target_graph.add_relation(UMLRelation::new("f2", "Mock", UMLRelationKind::UMLDependency));
339
340 assert_eq!(parsed_graph, target_graph);
341 }
342
343 #[test]
344 fn test_aggregation() {
345 let code: &str = r#"
346 struct Amut {
347 b: *mut B,
348 }
349
350 struct Aconst {
351 b: *const B,
352 }
353
354 struct B {
355 }
356 "#;
357 let parsed_graph = AstParser::parse_string(code);
358 let mut target_graph: UMLGraph = UMLGraph::new();
359
360 target_graph.add_struct(UMLClass::new("Amut", vec![String::from(r"b: *mut B")], vec![], UMLClassKind::UMLClass));
361 target_graph.add_struct(UMLClass::new("Aconst", vec![String::from(r"b: *const B")], vec![], UMLClassKind::UMLClass));
362 target_graph.add_struct(UMLClass::new("B", vec![], vec![], UMLClassKind::UMLClass));
363 target_graph.add_relation(UMLRelation::new("Amut", "B", UMLRelationKind::UMLAggregation));
364 target_graph.add_relation(UMLRelation::new("Aconst", "B", UMLRelationKind::UMLAggregation));
365
366 assert_eq!(parsed_graph, target_graph);
367 }
368
369 #[test]
370 fn test_composition() {
371 let code: &str = r#"
372 struct A {
373 b: B,
374 }
375
376 struct B {
377 }
378 "#;
379 let parsed_graph = AstParser::parse_string(code);
380 let mut target_graph: UMLGraph = UMLGraph::new();
381
382 target_graph.add_struct(UMLClass::new("A", vec![String::from(r"b: B")], vec![], UMLClassKind::UMLClass));
383 target_graph.add_struct(UMLClass::new("B", vec![], vec![], UMLClassKind::UMLClass));
384 target_graph.add_relation(UMLRelation::new("A", "B", UMLRelationKind::UMLComposition));
385
386 assert_eq!(parsed_graph, target_graph);
387 }
388
389
390 #[test]
391 fn test_realization() {
392 let code: &str = r#"
393 use std::fmt::Debug;
394
395 #[derive(Debug)]
396 struct A<T> where T: Debug {
397 a: T,
398 }
399
400 impl<T> A<T> where T: Debug {
401 fn a(a: T) -> Self {
402 A {
403 a: a,
404 }
405 }
406 }
407
408 impl <T>B<T> for A<T> where T: Debug {
409 fn a(&self) -> Option<T> {
410 None
411 }
412 }
413
414 trait B<T> : Debug where T: Debug {
415 fn a(&self) -> Option<T>;
416 }
417
418 impl <T>B<T> {
419 fn a(&self) -> Option<T> {
420 None
421 }
422 }
423 "#;
424 let parsed_graph = AstParser::parse_string(code);
425 let mut target_graph: UMLGraph = UMLGraph::new();
426
427 target_graph.add_struct(UMLClass::new("A", vec![String::from(r"a: T")], vec![String::from(r"a(a: T) -> Self")], UMLClassKind::UMLClass));
428 target_graph.add_struct(UMLClass::new("B", vec![], vec![String::from(r"a(&self) -> Option<T>")], UMLClassKind::UMLTrait));
429 target_graph.add_relation(UMLRelation::new("A", "B", UMLRelationKind::UMLRealization));
430
431 assert_eq!(parsed_graph, target_graph);
432 }
433
434 #[test]
435 fn test_association() {
436 let code: &str = r#"
437 struct A {
438 }
439
440 impl A {
441 fn b() -> B {
442 B {
443 }
444 }
445 }
446
447 struct Ab {
448 }
449
450 impl Ab {
451 fn b() -> B {
452 B {
453 }
454 }
455 }
456
457 struct B {
458 }
459
460 impl B {
461 fn a() -> Ab {
462 Ab {
463 }
464 }
465 }
466 "#;
467 let parsed_graph = AstParser::parse_string(code);
468 let mut target_graph: UMLGraph = UMLGraph::new();
469
470 target_graph.add_struct(UMLClass::new("A", vec![], vec![String::from(r"b() -> B")], UMLClassKind::UMLClass));
471 target_graph.add_struct(UMLClass::new("Ab", vec![], vec![String::from(r"b() -> B")], UMLClassKind::UMLClass));
472 target_graph.add_struct(UMLClass::new("B", vec![], vec![String::from(r"a() -> Ab")], UMLClassKind::UMLClass));
473 target_graph.add_relation(UMLRelation::new("B", "A", UMLRelationKind::UMLAssociationUni));
474 target_graph.add_relation(UMLRelation::new("B", "Ab", UMLRelationKind::UMLAssociationBi));
475
476 assert_eq!(parsed_graph, target_graph);
477 }
478
479}