kotlin_poet_rs/spec/
function.rs

1use crate::io::RenderKotlin;
2use crate::spec::{VisibilityModifier, CodeBlock, GenericParameter, MemberInheritanceModifier, Name, Type, Parameter, Annotation};
3use crate::spec::annotation::{mixin_annotation_mutators, AnnotationSlot};
4use crate::spec::kdoc::{KdocSlot, mixin_kdoc_mutators};
5use crate::tokens;
6
7#[derive(Debug, Clone)]
8pub struct Function {
9    name: Name,
10    visibility_modifier: VisibilityModifier,
11    parameters: Vec<Parameter>,
12    body: Option<CodeBlock>,
13    returns: Type,
14    receiver: Option<Type>,
15    inheritance_modifier: MemberInheritanceModifier,
16    is_suspended: bool,
17    is_inline: bool,
18    is_operator: bool,
19    is_override: bool,
20    generic_parameters: Vec<GenericParameter>,
21    annotation_slot: AnnotationSlot,
22    kdoc: KdocSlot,
23}
24
25impl Function {
26    pub fn new<NameLike: Into<Name>>(name: NameLike) -> Function {
27        Function {
28            name: name.into(),
29            visibility_modifier: VisibilityModifier::default(),
30            parameters: Vec::new(),
31            body: None,
32            returns: Type::unit(),
33            receiver: None,
34            inheritance_modifier: MemberInheritanceModifier::Final,
35            is_suspended: false,
36            is_inline: false,
37            is_operator: false,
38            generic_parameters: Vec::new(),
39            is_override: false,
40            annotation_slot: AnnotationSlot::vertical(),
41            kdoc: KdocSlot::default(),
42        }
43    }
44
45    pub fn visibility_modifier(mut self, visibility_modifier: VisibilityModifier) -> Function {
46        self.visibility_modifier = visibility_modifier;
47        self
48    }
49
50    pub fn operator(mut self, flag: bool) -> Function {
51        self.is_operator = flag;
52        self
53    }
54
55    pub fn parameter(mut self, parameter: Parameter) -> Function {
56        self.parameters.push(parameter);
57        self
58    }
59
60    pub fn body<CodeBlockLike: Into<CodeBlock>>(mut self, body: CodeBlockLike) -> Function {
61        self.body = Some(body.into());
62        self
63    }
64
65    pub fn returns<TypeLike: Into<Type>>(mut self, returns: TypeLike) -> Function {
66        self.returns = returns.into();
67        self
68    }
69
70    pub fn receiver<TypeLike: Into<Type>>(mut self, receiver: TypeLike) -> Function {
71        self.receiver = Some(receiver.into());
72        self
73    }
74
75    pub fn inheritance_modifier(mut self, inheritance_modifier: MemberInheritanceModifier) -> Function {
76        self.inheritance_modifier = inheritance_modifier;
77        self
78    }
79
80    pub fn generic_parameter(mut self, parameter: GenericParameter) -> Function {
81        self.generic_parameters.push(parameter);
82        self
83    }
84
85    pub fn suspended(mut self, flag: bool) -> Function {
86        self.is_suspended = flag;
87        self
88    }
89
90    pub fn inline(mut self, flag: bool) -> Function {
91        self.is_inline = flag;
92        self
93    }
94
95    pub fn overrides(mut self, flag: bool) -> Function {
96        self.is_override = flag;
97        self
98    }
99
100    mixin_annotation_mutators!();
101    mixin_kdoc_mutators!();
102}
103
104impl RenderKotlin for Function {
105    fn render_into(&self, block: &mut CodeBlock) {
106        block.push_renderable(&self.kdoc);
107        block.push_renderable(&self.annotation_slot);
108
109        block.push_renderable(&self.visibility_modifier);
110        block.push_space();
111
112        if self.is_suspended {
113            block.push_static_atom(tokens::keyword::SUSPEND);
114            block.push_space();
115        }
116
117        if self.is_inline {
118            block.push_static_atom(tokens::keyword::INLINE);
119            block.push_space();
120        }
121
122        if self.is_operator {
123            block.push_static_atom(tokens::keyword::OPERATOR);
124            block.push_space();
125        }
126
127        if self.is_override {
128            block.push_static_atom(tokens::keyword::OVERRIDE);
129            block.push_space();
130        }
131
132        block.push_static_atom(tokens::keyword::FUN);
133        block.push_space();
134
135        if !self.generic_parameters.is_empty() {
136            block.push_angle_brackets(|code| {
137                code.push_comma_separated(
138                    &self.generic_parameters.iter().map(|it| it.render_definition())
139                        .collect::<Vec<CodeBlock>>()
140                );
141            });
142            block.push_space();
143        }
144
145        if let Some(receiver) = &self.receiver {
146            block.push_renderable(receiver);
147            block.push_static_atom(tokens::DOT);
148        }
149        block.push_renderable(&self.name);
150
151        block.push_round_brackets(|parameters_code| {
152            let total_parameters = self.parameters.len();
153            for (index, parameter) in self.parameters.iter().enumerate() {
154                parameters_code.push_renderable(parameter);
155                if index != total_parameters - 1 {
156                    parameters_code.push_static_atom(tokens::COMMA);
157                    parameters_code.push_space()
158                }
159            }
160        });
161
162        block.push_static_atom(tokens::COLON);
163        block.push_space();
164        block.push_renderable(&self.returns);
165
166        block.push_space();
167        block.push_renderable(
168            &GenericParameter::render_type_boundaries_vec_if_required(
169                &self.generic_parameters
170            )
171        );
172
173        if let Some(body) = &self.body {
174            block.push_space();
175            block.push_curly_brackets(|inner| {
176                inner.push_renderable(body);
177            });
178        }
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use crate::io::RenderKotlin;
185    use crate::spec::{Annotation, ClassLikeTypeName, CodeBlock, Function, GenericParameter, KDoc, Name, Package, Type, VisibilityModifier};
186    use crate::spec::function::Parameter;
187
188    #[test]
189    fn test_function_with_multiple_parameters() {
190        let block = Function::new(Name::from("main"))
191            .receiver(Type::short())
192            .visibility_modifier(VisibilityModifier::default())
193            .parameter(Parameter::new(Name::from("args"), Type::array(Type::string())))
194            .parameter(Parameter::new(Name::from("args2"), Type::array(Type::int())))
195            .body(CodeBlock::statement("return 23"))
196            .operator(true)
197            .suspended(true)
198            .inline(true);
199
200
201        assert_eq!(
202            "public suspend inline operator fun kotlin.Short.main(args: kotlin.Array<kotlin.String>, args2: kotlin.Array<kotlin.Int>): kotlin.Unit {\n    return 23\n}",
203            block.render_string()
204        )
205    }
206
207    #[test]
208    fn test_function_with_parameter_default_value() {
209        let block = Function::new(Name::from("main"))
210            .receiver(Type::short())
211            .visibility_modifier(VisibilityModifier::Public)
212            .parameter(
213                Parameter::new(Name::from("args"), Type::array(Type::string()))
214                    .default_value(CodeBlock::atom("\"hello world\""))
215            )
216            .body(CodeBlock::statement("return 23"))
217            .operator(true)
218            .suspended(true)
219            .inline(true);
220
221
222        assert_eq!(
223            "public suspend inline operator fun kotlin.Short.main(args: kotlin.Array<kotlin.String> = \"hello world\"): kotlin.Unit {\n    return 23\n}",
224            block.render_string()
225        )
226    }
227
228    #[test]
229    fn test_function_with_generic_arguments() {
230        let block = Function::new(Name::from("box"))
231            .generic_parameter(
232                GenericParameter::new(Name::from("A"))
233                    .type_boundary(Type::string())
234                    .type_boundary(Type::int()),
235            )
236            .generic_parameter(
237                GenericParameter::new(Name::from("B"))
238            );
239
240
241        assert_eq!(
242            "public fun <A, B> box(): kotlin.Unit where A: kotlin.String, A: kotlin.Int",
243            block.render_string()
244        )
245    }
246
247    #[test]
248    fn test_override() {
249        let block = Function::new(Name::from("box"))
250            .overrides(true)
251            .returns(Type::int())
252            .body(CodeBlock::statement("return 23"));
253
254        assert_eq!(
255            "public override fun box(): kotlin.Int {\n    return 23\n}",
256            block.render_string()
257        )
258    }
259
260    #[test]
261    fn test_kdoc() {
262        let block = Function::new(Name::from("box"))
263            .kdoc(KDoc::from("Hello\nWorld"));
264
265        assert_eq!(
266            "/**\n * Hello\n * World\n */\npublic fun box(): kotlin.Unit",
267            block.render_string()
268        )
269    }
270
271    #[test]
272    fn test_with_annotation() {
273        let block = Function::new(Name::from("box"))
274            .annotation(
275                Annotation::new(
276                    ClassLikeTypeName::top_level(
277                        Package::root(),
278                        Name::from("Test"),
279                    )
280                )
281            )
282            .annotation(
283                Annotation::new(
284                    ClassLikeTypeName::top_level(
285                        Package::root(),
286                        Name::from("Test2"),
287                    )
288                )
289            );
290
291        assert_eq!(
292            "@Test()\n@Test2()\npublic fun box(): kotlin.Unit",
293            block.render_string()
294        )
295    }
296}