sway_lsp/capabilities/code_actions/common/
generate_impl.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use sway_core::{
    transform::{AttributeKind, AttributesMap},
    TypeParameter,
};
use sway_types::Spanned;

use crate::capabilities::code_actions::CodeAction;

pub(crate) const CONTRACT: &str = "Contract";
pub(crate) const TAB: &str = "    ";

pub(crate) trait GenerateImplCodeAction<'a, T: Spanned>: CodeAction<'a, T> {
    /// Returns a [String] holding the name of the declaration.
    fn decl_name(&self) -> String;

    /// Returns an optional [String] of the type parameters for the given [TypeParameter] vector.
    fn type_param_string(&self, type_params: &[TypeParameter]) -> Option<String> {
        if type_params.is_empty() {
            None
        } else {
            Some(
                type_params
                    .iter()
                    .map(|param| param.name.to_string())
                    .collect::<Vec<_>>()
                    .join(", "),
            )
        }
    }

    /// Returns a [String] of a generated impl with the optional `for <for_name>` signature.
    /// Can be used for both ABI and Struct impls.
    fn impl_string(
        &self,
        type_params: Option<String>,
        body: String,
        for_name: Option<String>,
    ) -> String {
        let for_string = match for_name {
            Some(name) => format!(" for {name}"),
            None => String::new(),
        };
        let type_param_string = match type_params {
            Some(params) => format!("<{params}>"),
            None => String::new(),
        };
        format!(
            "\nimpl{} {}{}{} {{{}}}\n",
            type_param_string,
            self.decl_name(),
            type_param_string,
            for_string,
            body
        )
    }

    /// Returns a [String] of a an attribute map, optionally excluding comments.
    fn attribute_string(&self, attr_map: &AttributesMap, include_comments: bool) -> String {
        let attr_string = attr_map
            .iter()
            .map(|(kind, attrs)| {
                attrs
                    .iter()
                    .filter_map(|attr| match kind {
                        AttributeKind::DocComment { .. } => {
                            if include_comments {
                                return Some(format!("{}{}", TAB, attr.span.as_str()));
                            }
                            None
                        }
                        _ => Some(format!("{}{}", TAB, attr.span.as_str())),
                    })
                    .collect::<Vec<String>>()
                    .join("\n")
            })
            .collect::<Vec<String>>()
            .join("\n");
        let attribute_padding = if attr_string.len() > 1 { "\n" } else { "" };
        format!("{attr_string}{attribute_padding}")
    }

    /// Returns a [String] of a generated function signature.
    fn fn_signature_string(
        &self,
        fn_name: String,
        params_string: String,
        attr_map: &AttributesMap,
        return_type_string: String,
        body: Option<String>,
    ) -> String {
        let attribute_string = self.attribute_string(attr_map, false);
        let body_string = match body {
            Some(body) => format!(" {body} "),
            None => String::new(),
        };
        format!(
            "{attribute_string}{TAB}fn {fn_name}({params_string}){return_type_string} {{{body_string}}}",
        )
    }
}