workspacer_syntax/
generate_function_signature.rs1crate::ix!();
3
4pub fn post_process_spacing(signature: &str) -> String {
6 signature
7 .replace(")->", ") ->")
8 .replace(">where", "> where")
9}
10
11#[derive(Debug, Clone)]
12pub struct FnSignatureGenerator(ast::Fn);
13
14impl GenerateSignature for ast::Fn {
15
16 fn generate_signature_with_opts(&self, opts: &SignatureOptions) -> String {
17 use tracing::{debug, trace};
18
19 trace!("Generating signature for ast::Fn with opts: {:?}", opts);
20
21 let doc_text = if *opts.include_docs() {
23 extract_docs(&self.syntax())
24 .map(|d| format!("{}\n", d))
25 .unwrap_or_default()
26 } else {
27 "".to_string()
28 };
29
30 let vis_str = self
32 .visibility()
33 .map(|v| format!("{} ", v.syntax().text()))
34 .unwrap_or_default();
35
36 let async_str = if let Some(token) = self.async_token() {
37 format!("{} ", token.text())
38 } else {
39 "".to_string()
40 };
41
42 let fn_keyword = "fn";
43 let name_str = self
44 .name()
45 .map(|n| n.text().to_string())
46 .unwrap_or_else(|| "<anon>".to_string());
47
48 let generic_params = self
50 .generic_param_list()
51 .map(|gp| gp.syntax().text().to_string())
52 .unwrap_or_default();
53
54 let mut param_entries = Vec::new();
56 if let Some(plist) = self.param_list() {
57 debug!(?plist, "Found param_list for fn");
58
59 if let Some(sp) = plist.self_param() {
61 let has_amp = sp.amp_token().is_some();
62 let has_mut = sp.mut_token().is_some();
63 let lifetime_str = sp
64 .lifetime()
65 .map(|lt| lt.syntax().text().to_string())
66 .unwrap_or_default();
67
68 let mut name_part = String::new();
69 if has_amp {
70 name_part.push('&');
71 if !lifetime_str.is_empty() {
72 name_part.push_str(&lifetime_str);
73 name_part.push(' ');
74 }
75 }
76 if has_mut {
77 name_part.push_str("mut ");
78 }
79 name_part.push_str("self");
80
81 param_entries.push((name_part.trim_end().to_string(), "".to_string()));
83 }
84
85 for param in plist.params() {
87 if let Some(normal) = ast::Param::cast(param.syntax().clone()) {
88 let pat_str = normal
89 .pat()
90 .map(|p| p.syntax().text().to_string())
91 .unwrap_or_default();
92 let ty_str = normal
93 .ty()
94 .map(|t| t.syntax().text().to_string())
95 .unwrap_or_default();
96
97 if !pat_str.is_empty() && !ty_str.is_empty() {
98 param_entries.push((pat_str, ty_str));
99 } else if !ty_str.is_empty() {
100 param_entries.push((ty_str, "".to_string()));
101 } else if !pat_str.is_empty() {
102 param_entries.push((pat_str, "".to_string()));
103 } else {
104 param_entries.push(("<unknown_param>".to_string(), "".to_string()));
105 }
106 } else {
107 param_entries.push(("<unrecognized_param>".to_string(), "".to_string()));
108 }
109 }
110 }
111
112 let param_count = param_entries.len();
113
114 let ret_str = if let Some(ret_type) = self.ret_type() {
116 if let Some(ty_node) = ret_type.ty() {
117 format!(" -> {}", ty_node.syntax().text())
118 } else {
119 "".to_string()
120 }
121 } else {
122 "".to_string()
123 };
124
125 let where_str = full_clean_where_clause(&self.where_clause());
127
128 let prefix_line = format!("{vis_str}{async_str}{fn_keyword} {name_str}{generic_params}");
137
138 let multiline: bool = param_count > 3;
140
141 let mut param_str = String::new();
142 if param_count == 0 {
143 param_str.push_str("()");
145 } else if !multiline {
146 let joined: Vec<String> = param_entries
148 .iter()
149 .map(|(n, t)| {
150 if t.is_empty() {
151 n.to_string()
152 } else {
153 format!("{}: {}", n, t)
154 }
155 })
156 .collect();
157 param_str.push('(');
158 param_str.push_str(&joined.join(", "));
159 param_str.push(')');
160 } else {
161 let max_name_len = param_entries
164 .iter()
165 .map(|(n, _)| n.len())
166 .max()
167 .unwrap_or(0);
168
169 param_str.push('(');
171
172 for (i, (name_part, ty_part)) in param_entries.iter().enumerate() {
174 param_str.push('\n');
175 param_str.push_str(" ");
177 let spacing_needed = max_name_len.saturating_sub(name_part.len());
178 if ty_part.is_empty() {
179 param_str.push_str(name_part);
181 if i + 1 < param_count {
183 param_str.push(',');
184 }
185 } else {
186 param_str.push_str(name_part);
188 param_str.push_str(": ");
189 param_str.push_str(&" ".repeat(spacing_needed));
190 param_str.push_str(ty_part);
191 if i + 1 < param_count {
193 param_str.push(',');
194 }
195 }
196 }
197 param_str.push('\n');
198 param_str.push(')');
199 }
200
201 let suffix = if !where_str.is_empty() {
204 format!("{ret_str} {where_str}")
205 } else {
206 ret_str
207 };
208
209 let final_func_line = if multiline {
213 if suffix.trim().is_empty() {
216 param_str
217 } else {
218 format!("{param_str}{suffix}")
219 }
220 } else {
221 format!("{}{}", param_str, suffix)
223 };
224
225 let raw_sig = format!("{prefix_line}{final_func_line}");
227
228 let combined = match opts.add_semicolon() {
229 true => format!("{doc_text}{raw_sig};"),
230 false => format!("{doc_text}{raw_sig}"),
231 };
232
233 post_process_spacing(&combined)
234 }
235}