kotlin_poet_rs/spec/
function.rs1use 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}