gather_all_code_from_crates/
function_info.rs1crate::ix!();
2
3#[derive(Builder,Setters,Getters,Debug, Clone)]
5#[builder(setter(into))]
6#[getset(get="pub",set="pub")]
7pub struct FunctionInfo {
8 name: String,
10
11 is_public: bool,
13
14 is_test: bool,
16
17 attributes: Vec<String>,
19
20 signature: String,
22
23 body: Option<String>,
25}
26
27pub fn extract_functions_from_ast(syntax: &SyntaxNode, remove_doc_comments: bool) -> Vec<FunctionInfo> {
36 let mut results = Vec::new();
37
38 for node in syntax.descendants() {
39 if let Some(fn_def) = ast::Fn::cast(node.clone()) {
40 let name_node = match fn_def.name() {
42 Some(n) => n.text().to_string(),
43 None => continue, };
45
46 let mut attributes = Vec::new();
47 let mut is_test = false;
48
49 for attr in fn_def.attrs() {
50 let txt = attr.syntax().text().to_string();
51 attributes.push(txt.trim().to_string());
52 if txt.contains("#[test]") {
53 is_test = true;
54 }
55 }
56
57 let is_public = fn_def.visibility().map_or(false, |v| v.syntax().text().to_string().contains("pub"));
58
59 let signature = {
61 let start = fn_def.syntax().text_range().start();
62 let mut end = start;
63 let mut found_end = false;
64 for token in fn_def.syntax().descendants_with_tokens() {
65 if let Some(t) = token.as_token() {
66 if t.kind() == ra_ap_syntax::SyntaxKind::L_CURLY
67 || t.kind() == ra_ap_syntax::SyntaxKind::SEMICOLON {
68 end = t.text_range().start();
69 found_end = true;
70 break;
71 }
72 }
73 }
74 if !found_end {
75 fn_def.syntax().text().to_string()
77 } else {
78 let text = fn_def.syntax().text().to_string();
80 let start_idx = 0;
81 let end_idx = (end - start).into();
82 if end_idx <= text.len() {
83 text[..end_idx].trim_end().to_string()
84 } else {
85 text }
87 }
88 };
89
90 let body = fn_def.body().map(|b| b.syntax().text().to_string());
92
93 if remove_doc_comments {
94
95 let mut signature = signature.trim_end().to_string();
97
98 let mut filtered_lines = Vec::new();
100 for line in signature.lines() {
101 let trimmed = line.trim_start();
102 if trimmed.starts_with("///") || trimmed.starts_with("//!") {
104 continue;
105 }
106 filtered_lines.push(line);
107 }
108
109 signature = filtered_lines.join("\n");
111
112 results.push(FunctionInfo {
114 name: name_node,
115 is_public,
116 is_test,
117 attributes,
118 signature,
119 body,
120 });
121
122 } else {
123
124 results.push(FunctionInfo {
125 name: name_node,
126 is_public,
127 is_test,
128 attributes,
129 signature: signature.trim_end().to_string(),
130 body,
131 });
132
133 }
134 }
135 }
136
137 results
138}
139
140#[cfg(test)]
141mod function_info_tests {
142 use super::*;
143
144 fn parse_source_code(code: &str) -> SyntaxNode {
145 let parsed = SourceFile::parse(code, Edition::CURRENT);
146 assert!(parsed.errors().is_empty(), "Parsing errors: {:?}", parsed.errors());
147 parsed.tree().syntax().clone()
148 }
149
150 #[test]
151 fn test_extract_functions_from_ast_basic() {
152 let code = r#"
153 fn foo() { println!("hello"); }
154 pub fn bar(x: i32) -> i32 { x + 1 }
155 #[test]
156 fn test_me() {}
157 fn unnamed() {}
158 "#;
159 let syntax = parse_source_code(code);
160 let funcs = extract_functions_from_ast(&syntax, false);
161
162 assert_eq!(funcs.len(), 4);
174
175 let foo = funcs.iter().find(|f| f.name == "foo").unwrap();
176 assert!(!foo.is_public);
177 assert!(!foo.is_test);
178 assert!(foo.signature.contains("fn foo("));
179 assert!(foo.body.as_ref().unwrap().contains("println"));
180
181 let bar = funcs.iter().find(|f| f.name == "bar").unwrap();
182 assert!(bar.is_public);
183 assert!(!bar.is_test);
184 assert!(bar.signature.contains("pub fn bar(x: i32) -> i32"));
185 assert!(bar.body.as_ref().unwrap().contains("x + 1"));
186
187 let test_me = funcs.iter().find(|f| f.name == "test_me").unwrap();
188 assert!(!test_me.is_public);
189 assert!(test_me.is_test);
190 assert!(test_me.attributes.iter().any(|a| a.contains("#[test]")));
191
192 let unnamed = funcs.iter().find(|f| f.name == "unnamed").unwrap();
193 assert!(!unnamed.is_public);
197 assert!(!unnamed.is_test);
198 }
199
200 #[test]
201 fn test_extract_functions_from_ast_no_body() {
202 let code = r#"
203 extern "C" {
204 pub fn interface();
205 }
206 "#;
207 let syntax = parse_source_code(code);
208 let funcs = extract_functions_from_ast(&syntax, false);
209
210 assert_eq!(funcs.len(), 1);
213 let interface = &funcs[0];
214 assert!(interface.is_public);
215 assert!(interface.body.is_none());
216 println!("interface.signature: {:#?}", interface.signature);
217 assert!(interface.signature.contains("pub fn interface()"));
218 }
219}