1use super::GoParser;
2use crate::{
3 DeclareKind, DeclareStatements, Error, FieldUnit, FileUnit, FunctionUnit, ImplUnit,
4 LanguageParser, ModuleUnit, Result, StructUnit, TraitUnit, Visibility,
5};
6use std::fs;
7use std::ops::{Deref, DerefMut};
8use std::path::Path;
9use tree_sitter::{Node, Parser};
10
11impl LanguageParser for GoParser {
12 fn parse_file(&mut self, file_path: &Path) -> Result<FileUnit> {
13 let source_code = fs::read_to_string(file_path).map_err(Error::Io)?;
15
16 let tree = self
18 .parse(source_code.as_bytes(), None)
19 .ok_or_else(|| Error::TreeSitter("Failed to parse source code".to_string()))?;
20 let root_node = tree.root_node();
21
22 let mut file_unit = FileUnit::new(file_path.to_path_buf());
24 file_unit.source = Some(source_code.clone());
25
26 let mut methods_by_type: std::collections::HashMap<String, Vec<FunctionUnit>> =
28 std::collections::HashMap::new();
29
30 let mut cursor = root_node.walk();
32 for child in root_node.children(&mut cursor) {
33 match child.kind() {
34 "package_clause" => {
35 let package_doc = extract_documentation(child, &source_code);
36 if let Some(package_name) =
37 get_child_node_text(child, "package_identifier", &source_code)
38 {
39 let module = ModuleUnit {
40 name: package_name,
41 visibility: Visibility::Public, doc: package_doc,
43 source: get_node_text(child, &source_code),
44 attributes: Vec::new(),
45 ..Default::default()
46 };
47 file_unit.modules.push(module);
48 }
49 }
50 "import_declaration" => {
51 let mut import_cursor = child.walk();
53 for import_spec in child.children(&mut import_cursor) {
54 if import_spec.kind() == "import_spec"
55 || import_spec.kind() == "interpreted_string_literal"
56 || import_spec.kind() == "raw_string_literal"
57 {
58 if let Some(import_text) = get_node_text(import_spec, &source_code) {
59 file_unit.declares.push(DeclareStatements {
60 source: import_text,
61 kind: DeclareKind::Use,
62 });
63 }
64 } else if import_spec.kind() == "import_spec_list" {
65 let mut list_cursor = import_spec.walk();
66 for inner_spec in import_spec.children(&mut list_cursor) {
67 if inner_spec.kind() == "import_spec" {
68 if let Some(import_text) =
69 get_node_text(inner_spec, &source_code)
70 {
71 file_unit.declares.push(DeclareStatements {
72 source: import_text,
73 kind: DeclareKind::Use,
74 });
75 }
76 }
77 }
78 }
79 }
80 }
81 "function_declaration" => {
82 if let Ok(func) = self.parse_function(child, &source_code) {
83 file_unit.functions.push(func);
84 }
85 }
86 "method_declaration" => {
87 if let Ok((receiver_type, method)) = self.parse_method(child, &source_code) {
88 methods_by_type
89 .entry(receiver_type)
90 .or_default()
91 .push(method);
92 }
93 }
94 "type_declaration" => {
95 let mut type_decl_cursor = child.walk();
96 for type_spec_node in child.children(&mut type_decl_cursor) {
97 if type_spec_node.kind() == "type_spec" {
98 let mut type_spec_cursor = type_spec_node.walk();
99 if let Some(type_def_node) = type_spec_node
100 .children(&mut type_spec_cursor)
101 .find(|n| n.kind() == "struct_type" || n.kind() == "interface_type")
102 {
103 if type_def_node.kind() == "struct_type" {
104 if let Ok(struct_item) =
105 self.parse_struct(type_spec_node, &source_code)
106 {
107 file_unit.structs.push(struct_item);
108 }
109 } else if type_def_node.kind() == "interface_type" {
110 if let Ok(interface_item) =
111 self.parse_interface(type_spec_node, &source_code)
112 {
113 file_unit.traits.push(interface_item);
114 }
115 }
116 }
117 }
118 }
119 }
120 "const_declaration" | "var_declaration" => {
121 let mut decl_cursor = child.walk();
122 for spec_node in child.children(&mut decl_cursor) {
123 if spec_node.kind() == "const_spec" || spec_node.kind() == "var_spec" {
124 if let Some(declare_text) = get_node_text(spec_node, &source_code) {
125 let kind_str = if child.kind() == "const_declaration" {
126 "const"
127 } else {
128 "var"
129 };
130 file_unit.declares.push(DeclareStatements {
131 source: declare_text,
132 kind: DeclareKind::Other(kind_str.to_string()),
133 });
134 }
135 } else if spec_node.kind() == "var_spec_list"
136 || spec_node.kind() == "const_spec_list"
137 {
138 let mut list_cursor = spec_node.walk();
139 for inner_spec_node in spec_node.children(&mut list_cursor) {
140 if inner_spec_node.kind() == "const_spec"
141 || inner_spec_node.kind() == "var_spec"
142 {
143 if let Some(declare_text) =
144 get_node_text(inner_spec_node, &source_code)
145 {
146 let kind_str = if child.kind() == "const_declaration" {
147 "const"
148 } else {
149 "var"
150 };
151 file_unit.declares.push(DeclareStatements {
152 source: declare_text,
153 kind: DeclareKind::Other(kind_str.to_string()),
154 });
155 }
156 }
157 }
158 }
159 }
160 }
161 "comment" => {
162 }
164 _ => {
165 }
167 }
168 }
169
170 for struct_item in &mut file_unit.structs {
172 if let Some(methods) = methods_by_type.remove(&struct_item.name) {
173 struct_item.methods.extend(methods.clone()); let impl_unit = ImplUnit {
177 doc: None, head: format!("methods for {}", struct_item.name),
179 source: None, attributes: Vec::new(),
181 methods, };
183 file_unit.impls.push(impl_unit);
184 }
185 }
186
187 for (receiver_type, methods) in methods_by_type {
190 let impl_unit = ImplUnit {
191 doc: None,
192 head: format!("methods for {}", receiver_type),
193 source: None,
194 attributes: Vec::new(),
195 methods,
196 };
197 file_unit.impls.push(impl_unit);
198 }
199
200 Ok(file_unit)
201 }
202}
203
204impl GoParser {
205 pub fn try_new() -> Result<Self> {
206 let mut parser = Parser::new();
207 let language = tree_sitter_go::LANGUAGE;
208 parser
209 .set_language(&language.into())
210 .map_err(|e| Error::TreeSitter(e.to_string()))?;
211 Ok(Self { parser })
212 }
213
214 fn determine_visibility(&self, name: &str) -> Visibility {
216 if !name.is_empty() && name.chars().next().unwrap().is_uppercase() {
217 Visibility::Public
218 } else {
219 Visibility::Private
220 }
221 }
222
223 fn parse_function(&self, node: Node, source_code: &str) -> Result<FunctionUnit> {
225 let documentation = extract_documentation(node, source_code);
226 let name = get_child_node_text(node, "identifier", source_code)
227 .unwrap_or_else(|| "unknown".to_string());
228
229 let visibility = self.determine_visibility(&name);
230 let source = get_node_text(node, source_code);
231 let mut signature = None;
232 let mut body = None;
233
234 if let Some(body_node) = node.child_by_field_name("body") {
236 let sig_end = body_node.start_byte();
237 let sig_start = node.start_byte();
238 if sig_end > sig_start {
239 signature = Some(source_code[sig_start..sig_end].trim().to_string());
240 }
241 body = get_node_text(body_node, source_code);
242 } else {
243 signature = source.clone();
245 }
246
247 Ok(FunctionUnit {
248 name,
249 visibility,
250 doc: documentation,
251 source,
252 signature,
253 body,
254 attributes: Vec::new(), })
256 }
257
258 fn parse_struct(&self, type_spec_node: Node, source_code: &str) -> Result<StructUnit> {
261 let documentation =
263 extract_documentation(type_spec_node, source_code).or_else(|| -> Option<String> {
264 type_spec_node
265 .parent()
266 .and_then(|p| extract_documentation(p, source_code))
267 });
268 let name = get_child_node_text(type_spec_node, "type_identifier", source_code)
269 .unwrap_or_else(|| "unknown".to_string());
270 let visibility = self.determine_visibility(&name);
271 let source = get_node_text(
272 type_spec_node.parent().unwrap_or(type_spec_node),
273 source_code,
274 );
275 let head = format!("type {} struct", name);
276
277 let mut fields = Vec::new();
278
279 if let Some(struct_type) = type_spec_node
280 .children(&mut type_spec_node.walk())
281 .find(|child| child.kind() == "struct_type")
282 {
283 if let Some(field_list) = struct_type
284 .children(&mut struct_type.walk())
285 .find(|child| child.kind() == "field_declaration_list")
286 {
287 let mut list_cursor = field_list.walk();
288 for field_decl in field_list.children(&mut list_cursor) {
289 if field_decl.kind() == "field_declaration" {
290 let field_documentation = extract_documentation(field_decl, source_code);
291 let field_source = get_node_text(field_decl, source_code);
292 let mut field_names = Vec::new();
293 let mut decl_cursor = field_decl.walk();
294 for child in field_decl.children(&mut decl_cursor) {
295 if child.kind() == "identifier" || child.kind() == "field_identifier" {
296 if let Some(field_name) = get_node_text(child, source_code) {
297 field_names.push(field_name);
298 }
299 } else if child.kind().ends_with("_type")
300 || child.kind() == "qualified_type"
301 {
302 break;
304 }
305 }
306 for field_name in field_names {
307 fields.push(FieldUnit {
308 name: field_name,
309 doc: field_documentation.clone(),
310 attributes: Vec::new(),
311 source: field_source.clone(),
312 });
313 }
314 }
315 }
316 }
317 }
318
319 Ok(StructUnit {
320 name,
321 head,
322 visibility,
323 doc: documentation,
324 source,
325 attributes: Vec::new(),
326 fields,
327 methods: Vec::new(),
328 })
329 }
330
331 fn parse_interface(&self, type_spec_node: Node, source_code: &str) -> Result<TraitUnit> {
334 let documentation =
336 extract_documentation(type_spec_node, source_code).or_else(|| -> Option<String> {
337 type_spec_node
338 .parent()
339 .and_then(|p| extract_documentation(p, source_code))
340 });
341 let name = get_child_node_text(type_spec_node, "type_identifier", source_code)
342 .unwrap_or_else(|| "unknown".to_string());
343 let visibility = self.determine_visibility(&name);
344 let source = get_node_text(
345 type_spec_node.parent().unwrap_or(type_spec_node),
346 source_code,
347 );
348
349 let mut methods = Vec::new();
350
351 if let Some(interface_type) = type_spec_node
352 .children(&mut type_spec_node.walk())
353 .find(|child| child.kind() == "interface_type")
354 {
355 let mut interface_cursor = interface_type.walk();
356 for child in interface_type.children(&mut interface_cursor) {
357 if child.kind() == "method_elem" {
358 let method_spec = child; let method_doc = extract_documentation(method_spec, source_code);
360 let method_source = get_node_text(method_spec, source_code);
361 let method_name = get_child_node_text(method_spec, "identifier", source_code)
363 .or_else(|| {
364 get_child_node_text(method_spec, "field_identifier", source_code)
365 })
366 .unwrap_or_else(|| "unknown_interface_method".to_string());
367 let visibility = self.determine_visibility(&method_name); let signature = method_source.clone();
370
371 methods.push(FunctionUnit {
372 name: method_name,
373 visibility, doc: method_doc,
375 source: method_source,
376 signature,
377 body: None, attributes: Vec::new(),
379 });
380 }
381 }
382 }
383
384 Ok(TraitUnit {
385 name,
386 visibility,
387 doc: documentation,
388 source,
389 attributes: Vec::new(),
390 methods,
391 })
392 }
393
394 fn parse_method(&self, node: Node, source_code: &str) -> Result<(String, FunctionUnit)> {
397 let documentation = extract_documentation(node, source_code);
398 let source = get_node_text(node, source_code);
399
400 let method_name = get_child_node_text(node, "field_identifier", source_code)
402 .unwrap_or_else(|| "unknown".to_string());
403
404 let receiver_type = if let Some(parameter_list) = node.child_by_field_name("receiver") {
406 if let Some(parameter) = parameter_list
408 .children(&mut parameter_list.walk())
409 .find(|child| child.kind() == "parameter_declaration")
410 {
411 if let Some(type_node) = parameter.child_by_field_name("type") {
413 get_node_text(type_node, source_code)
414 .map(|s| s.trim_start_matches('*').to_string()) .unwrap_or_else(|| "unknown".to_string())
416 } else {
417 "unknown".to_string()
418 }
419 } else {
420 "unknown".to_string()
421 }
422 } else {
423 "unknown".to_string()
424 };
425
426 let visibility = self.determine_visibility(&method_name);
427 let mut signature = None;
428 let mut body = None;
429
430 if let Some(body_node) = node.child_by_field_name("body") {
432 let sig_end = body_node.start_byte();
433 let sig_start = node.start_byte();
434 if sig_end > sig_start {
435 signature = Some(source_code[sig_start..sig_end].trim().to_string());
436 }
437 body = get_node_text(body_node, source_code);
438 } else {
439 signature = source.clone();
440 }
441
442 let function = FunctionUnit {
443 name: method_name,
444 visibility,
445 doc: documentation,
446 source,
447 signature,
448 body,
449 attributes: Vec::new(),
450 };
451
452 Ok((receiver_type, function))
453 }
454}
455
456fn get_node_text(node: Node, source_code: &str) -> Option<String> {
458 node.utf8_text(source_code.as_bytes())
459 .ok()
460 .map(String::from)
461}
462
463fn get_child_node_text<'a>(node: Node<'a>, kind: &str, source_code: &'a str) -> Option<String> {
465 if kind == "identifier" || kind == "package_identifier" || kind == "field_identifier" {
467 if let Some(name_node) = node.child_by_field_name("name") {
468 if name_node.kind() == kind {
470 return name_node
471 .utf8_text(source_code.as_bytes())
472 .ok()
473 .map(String::from);
474 }
475 }
476 }
477
478 if let Some(child) = node
480 .children(&mut node.walk())
481 .find(|child| child.kind() == kind)
482 {
483 return child
484 .utf8_text(source_code.as_bytes())
485 .ok()
486 .map(String::from);
487 }
488
489 if kind == "identifier" || kind == "package_identifier" || kind == "field_identifier" {
491 if let Some(ident_child) = node
492 .children(&mut node.walk())
493 .find(|child| child.kind() == kind)
494 {
495 return ident_child
496 .utf8_text(source_code.as_bytes())
497 .ok()
498 .map(String::from);
499 }
500 }
501 if let Some(ident_child) = node
503 .children(&mut node.walk())
504 .find(|child| child.kind() == "identifier")
505 {
506 return ident_child
507 .utf8_text(source_code.as_bytes())
508 .ok()
509 .map(String::from);
510 }
511
512 None
513}
514
515fn extract_documentation(node: Node, source_code: &str) -> Option<String> {
517 let mut prev_sibling = node.prev_sibling();
520 while let Some(sibling) = prev_sibling {
521 if sibling.kind() == "comment" {
522 if node.start_position().row == sibling.end_position().row + 1
524 || node.start_position().row == sibling.start_position().row + 1
525 {
526 let doc_text = get_node_text(sibling, source_code)?; let cleaned_doc = doc_text
530 .trim_start_matches("//")
531 .trim_start_matches("/*")
532 .trim_end_matches("*/")
533 .trim()
534 .to_string();
535 return Some(cleaned_doc);
539 } else {
540 break;
542 }
543 } else if !sibling.is_extra() {
544 break;
546 }
547 prev_sibling = sibling.prev_sibling();
548 }
549
550 None }
552
553impl Deref for GoParser {
554 type Target = Parser;
555
556 fn deref(&self) -> &Self::Target {
557 &self.parser
558 }
559}
560
561impl DerefMut for GoParser {
562 fn deref_mut(&mut self) -> &mut Self::Target {
563 &mut self.parser
564 }
565}
566
567#[cfg(test)]
568mod tests {
569 use super::*;
570 use std::path::PathBuf;
571
572 fn parse_fixture(file_name: &str) -> Result<FileUnit> {
573 let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")
574 .expect("CARGO_MANIFEST_DIR should be set during tests");
575 let path = PathBuf::from(manifest_dir).join("fixtures").join(file_name);
576 let mut parser = GoParser::try_new()?;
577 parser.parse_file(&path)
578 }
579
580 #[test]
581 fn test_parse_go_package() {
582 let file_unit = parse_fixture("sample.go").expect("Failed to parse Go file");
583 assert_eq!(
584 file_unit.modules.len(),
585 1,
586 "Should parse one package module"
587 );
588 assert_eq!(file_unit.modules[0].name, "example");
589 assert!(
590 file_unit.modules[0].doc.is_some(),
591 "Package doc comment missing"
592 );
593 assert!(
594 file_unit.modules[0]
595 .doc
596 .as_ref()
597 .unwrap()
598 .contains("sample Go file")
599 );
600 }
601
602 #[test]
603 fn test_parse_go_imports() {
604 let file_unit = parse_fixture("sample.go").expect("Failed to parse Go file");
605 let import_count = file_unit
607 .declares
608 .iter()
609 .filter(|d| d.kind == DeclareKind::Use)
610 .count();
611 assert_eq!(
612 import_count, 7,
613 "Expected exactly 7 imports, found {}",
614 import_count
615 ); assert!(
618 file_unit
619 .declares
620 .iter()
621 .any(|d| d.kind == DeclareKind::Use && d.source.contains("\"fmt\""))
622 );
623 assert!(
624 file_unit
625 .declares
626 .iter()
627 .any(|d| d.kind == DeclareKind::Use && d.source.contains("\"strings\""))
628 );
629 assert!(
630 file_unit
631 .declares
632 .iter()
633 .any(|d| d.kind == DeclareKind::Use && d.source.contains("\"os\""))
634 );
635 let const_count = file_unit
637 .declares
638 .iter()
639 .filter(|d| matches!(&d.kind, DeclareKind::Other(s) if s == "const"))
640 .count();
641 assert!(
642 const_count >= 3,
643 "Expected at least 3 const declarations, found {}",
644 const_count
645 );
646 let var_count = file_unit
647 .declares
648 .iter()
649 .filter(|d| matches!(&d.kind, DeclareKind::Other(s) if s == "var"))
650 .count();
651 assert!(
652 var_count >= 1,
653 "Expected at least 1 var declaration, found {}",
654 var_count
655 );
656 }
657
658 #[test]
659 fn test_parse_go_functions() {
660 let file_unit = parse_fixture("sample.go").expect("Failed to parse Go file");
661 let new_person_func = file_unit.functions.iter().find(|f| f.name == "NewPerson");
663 assert!(new_person_func.is_some(), "NewPerson function not found");
664 let new_person_func = new_person_func.unwrap();
665 assert_eq!(new_person_func.visibility, Visibility::Public);
666 assert!(new_person_func.doc.is_some(), "NewPerson doc missing");
667 assert!(
668 new_person_func
669 .doc
670 .as_ref()
671 .unwrap()
672 .contains("creates a new Person instance")
673 );
674 assert!(new_person_func.signature.is_some());
675 assert!(new_person_func.body.is_some());
676
677 let upper_case_func = file_unit.functions.iter().find(|f| f.name == "UpperCase");
678 assert!(upper_case_func.is_some(), "UpperCase function not found");
679 let upper_case_func = upper_case_func.unwrap();
680 assert_eq!(upper_case_func.visibility, Visibility::Public);
681 assert!(upper_case_func.doc.is_some(), "UpperCase doc missing");
682 assert!(
683 upper_case_func
684 .doc
685 .as_ref()
686 .unwrap()
687 .contains("converts a string to uppercase")
688 );
689 assert!(upper_case_func.signature.is_some());
690 assert!(upper_case_func.body.is_some());
691 }
692
693 #[test]
694 fn test_parse_go_structs() {
695 let file_unit = parse_fixture("sample.go").expect("Failed to parse Go file");
696
697 let person_struct = file_unit.structs.iter().find(|s| s.name == "Person");
698 assert!(person_struct.is_some(), "Person struct not found");
699 let person_struct = person_struct.unwrap();
700 assert_eq!(person_struct.visibility, Visibility::Public);
701 assert!(person_struct.doc.is_some(), "Person doc missing");
702 assert!(
703 person_struct
704 .doc
705 .as_ref()
706 .unwrap()
707 .contains("represents a person")
708 );
709 assert_eq!(person_struct.fields.len(), 3, "Person should have 3 fields");
710 assert!(person_struct.fields.iter().any(|f| f.name == "Name"));
712 assert!(person_struct.fields.iter().any(|f| f.name == "Age"));
713 assert!(person_struct.fields.iter().any(|f| f.name == "address"));
714 let name_field = person_struct
716 .fields
717 .iter()
718 .find(|f| f.name == "Name")
719 .unwrap();
720 assert!(name_field.doc.is_some(), "Name field doc missing");
721 assert!(name_field.doc.as_ref().unwrap().contains("person's name"));
722
723 let age_field = person_struct
724 .fields
725 .iter()
726 .find(|f| f.name == "Age")
727 .unwrap();
728 assert!(age_field.doc.is_some(), "Age field doc missing");
729 assert!(age_field.doc.as_ref().unwrap().contains("person's age"));
730
731 let address_field = person_struct
732 .fields
733 .iter()
734 .find(|f| f.name == "address")
735 .unwrap();
736 assert!(address_field.doc.is_some(), "address field doc missing");
737 assert!(
738 address_field
739 .doc
740 .as_ref()
741 .unwrap()
742 .contains("unexported field")
743 );
744
745 let greeter_impl_struct = file_unit.structs.iter().find(|s| s.name == "GreeterImpl");
746 assert!(
747 greeter_impl_struct.is_some(),
748 "GreeterImpl struct not found"
749 );
750 let greeter_impl_struct = greeter_impl_struct.unwrap();
751 assert_eq!(greeter_impl_struct.visibility, Visibility::Public);
752 assert!(greeter_impl_struct.doc.is_some(), "GreeterImpl doc missing");
753 assert!(
754 greeter_impl_struct
755 .doc
756 .as_ref()
757 .unwrap()
758 .contains("implements the Greeter interface")
759 );
760 assert_eq!(
761 greeter_impl_struct.fields.len(),
762 1,
763 "GreeterImpl should have 1 field"
764 );
765 assert_eq!(greeter_impl_struct.fields[0].name, "greeting");
766
767 let greeter_impl_methods = file_unit
769 .impls
770 .iter()
771 .find(|imp| imp.head == "methods for GreeterImpl");
772 assert!(
773 greeter_impl_methods.is_some(),
774 "Impl block for GreeterImpl not found"
775 );
776 assert_eq!(
777 greeter_impl_methods.unwrap().methods.len(),
778 1,
779 "GreeterImpl should have 1 method"
780 );
781 assert_eq!(greeter_impl_methods.unwrap().methods[0].name, "Greet");
782 }
783
784 #[test]
785 fn test_parse_go_interfaces() {
786 let file_unit = parse_fixture("sample.go").expect("Failed to parse Go file");
787
788 let greeter_interface = file_unit.traits.iter().find(|t| t.name == "Greeter");
789 assert!(greeter_interface.is_some(), "Greeter interface not found");
790 let greeter_interface = greeter_interface.unwrap();
791 assert_eq!(greeter_interface.visibility, Visibility::Public);
792 assert!(greeter_interface.doc.is_some(), "Greeter doc missing");
793 assert!(
794 greeter_interface
795 .doc
796 .as_ref()
797 .unwrap()
798 .contains("defines an interface")
799 );
800 assert_eq!(
801 greeter_interface.methods.len(),
802 1,
803 "Greeter interface should have 1 method"
804 );
805 assert_eq!(greeter_interface.methods[0].name, "Greet");
806 assert!(
807 greeter_interface.methods[0].doc.is_some(),
808 "Greet method doc missing"
809 );
810 assert!(
811 greeter_interface.methods[0]
812 .doc
813 .as_ref()
814 .unwrap()
815 .contains("returns a greeting message")
816 );
817 assert!(greeter_interface.methods[0].signature.is_some());
818 assert!(greeter_interface.methods[0].body.is_none());
819 }
820
821 #[test]
822 fn test_parse_go_methods() {
823 let file_unit = parse_fixture("sample.go").expect("Failed to parse Go file");
824
825 let person_impl = file_unit
827 .impls
828 .iter()
829 .find(|imp| imp.head == "methods for Person");
830 assert!(person_impl.is_some(), "Impl block for Person not found");
831 let person_impl = person_impl.unwrap();
832
833 assert_eq!(person_impl.methods.len(), 3, "Person should have 3 methods");
835
836 let set_address = person_impl.methods.iter().find(|m| m.name == "SetAddress");
838 assert!(set_address.is_some(), "SetAddress method not found");
839 let set_address = set_address.unwrap();
840 assert_eq!(set_address.visibility, Visibility::Public);
841 assert!(set_address.doc.is_some(), "SetAddress doc missing");
842 assert!(
843 set_address
844 .doc
845 .as_ref()
846 .unwrap()
847 .contains("sets the person's address")
848 );
849 assert!(set_address.signature.is_some());
850 assert!(set_address.body.is_some());
851
852 let get_address = person_impl.methods.iter().find(|m| m.name == "GetAddress");
854 assert!(get_address.is_some(), "GetAddress method not found");
855 let get_address = get_address.unwrap();
856 assert_eq!(get_address.visibility, Visibility::Public);
857 assert!(get_address.doc.is_some(), "GetAddress doc missing");
858 assert!(
859 get_address
860 .doc
861 .as_ref()
862 .unwrap()
863 .contains("returns the person's address")
864 );
865 assert!(get_address.signature.is_some());
866 assert!(get_address.body.is_some());
867
868 let string_method = person_impl.methods.iter().find(|m| m.name == "String");
870 assert!(string_method.is_some(), "String method not found");
871 let string_method = string_method.unwrap();
872 assert_eq!(string_method.visibility, Visibility::Public);
873 assert!(string_method.doc.is_some(), "String method doc missing");
874 assert!(
875 string_method
876 .doc
877 .as_ref()
878 .unwrap()
879 .contains("implements the Stringer interface")
880 );
881 assert!(string_method.signature.is_some());
882 assert!(string_method.body.is_some());
883 }
884}