workspacer_syntax/
generate_function_signature.rs

1// ---------------- [ File: workspacer-syntax/src/generate_function_signature.rs ]
2crate::ix!();
3
4/// Minimal post-processing to ensure correct spacing around arrows, where clauses, etc.
5pub 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    fn generate_signature_with_opts(&self, opts: &SignatureOptions) -> String {
16        use tracing::{debug, trace};
17
18        trace!("Generating signature for ast::Fn with opts: {:?}", opts);
19
20        // 1) Possibly gather doc lines
21        let doc_text = if *opts.include_docs() {
22            extract_docs(&self.syntax())
23                .map(|d| format!("{}\n", d))
24                .unwrap_or_default()
25        } else {
26            "".to_string()
27        };
28
29        // 2) Gather visibility, async, etc.
30        let vis_str = self
31            .visibility()
32            .map(|v| format!("{} ", v.syntax().text()))
33            .unwrap_or_default();
34
35        let async_str = if let Some(token) = self.async_token() {
36            format!("{} ", token.text())
37        } else {
38            "".to_string()
39        };
40
41        let fn_keyword = "fn";
42        let name_str = self
43            .name()
44            .map(|n| n.text().to_string())
45            .unwrap_or_else(|| "<anon>".to_string());
46
47        // 3) Generic params
48        let generic_params = self
49            .generic_param_list()
50            .map(|gp| gp.syntax().text().to_string())
51            .unwrap_or_default();
52
53        // 4) Parameter list
54        let mut param_texts = Vec::new();
55        if let Some(plist) = self.param_list() {
56            debug!(?plist, "Found param_list for fn");
57            if let Some(sp) = plist.self_param() {
58                let has_amp = sp.amp_token().is_some();  
59                let has_mut = sp.mut_token().is_some();
60                let lifetime_str = sp
61                    .lifetime()
62                    .map(|lt| lt.syntax().text().to_string())
63                    .unwrap_or_default();
64
65                let mut pieces = String::new();
66                if has_amp {
67                    pieces.push('&');
68                    if !lifetime_str.is_empty() {
69                        pieces.push_str(&lifetime_str);
70                        pieces.push(' ');
71                    }
72                }
73                if has_mut {
74                    pieces.push_str("mut ");
75                }
76                pieces.push_str("self");
77                param_texts.push(pieces.trim_end().to_string());
78            }
79
80            for param in plist.params() {
81                if let Some(normal) = ast::Param::cast(param.syntax().clone()) {
82                    let pat_str = normal
83                        .pat()
84                        .map(|p| p.syntax().text().to_string())
85                        .unwrap_or_default();
86                    let ty_str = normal
87                        .ty()
88                        .map(|t| t.syntax().text().to_string())
89                        .unwrap_or_default();
90                    if !pat_str.is_empty() && !ty_str.is_empty() {
91                        param_texts.push(format!("{}: {}", pat_str, ty_str));
92                    } else if !ty_str.is_empty() {
93                        param_texts.push(ty_str);
94                    } else if !pat_str.is_empty() {
95                        param_texts.push(pat_str);
96                    } else {
97                        param_texts.push("<unknown_param>".to_string());
98                    }
99                } else {
100                    param_texts.push("<unrecognized_param>".to_string());
101                }
102            }
103        }
104        let params_str = param_texts.join(", ");
105
106        // 5) Return type
107        let ret_str = if let Some(ret_type) = self.ret_type() {
108            if let Some(ty_node) = ret_type.ty() {
109                format!(" -> {}", ty_node.syntax().text())
110            } else {
111                "".to_string()
112            }
113        } else {
114            "".to_string()
115        };
116
117        // 6) Where clause
118        let where_str = if let Some(wc) = self.where_clause() {
119            format!(" {}", wc.syntax().text())
120        } else {
121            "".to_string()
122        };
123
124        // 7) Build signature line ***WITHOUT*** appending braces.
125        //    (We let CrateInterfaceItem<T> decide how to handle the body.)
126        let raw_sig = format!(
127            "{vis_str}{async_str}{fn_keyword} {name_str}{generic_params}({params_str}){ret_str}{where_str}"
128        );
129
130        let combined = match opts.add_semicolon() {
131            true  => format!("{doc_text}{raw_sig};"),
132            false => format!("{doc_text}{raw_sig}"),
133        };
134
135        post_process_spacing(&combined)
136    }
137}