1use crate::io::RenderKotlin;
2use crate::spec::{VisibilityModifier, Argument, ClassInheritanceModifier, CodeBlock, CompanionObject, Function, GenericParameter, Name, PrimaryConstructor, Property, SecondaryConstructor, Type, 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(crate) enum ClassMemberNode {
9 Property(Property),
10 Function(Function),
11 Subclass(Class),
12 SecondaryConstructor(SecondaryConstructor),
13 InitBlock(CodeBlock),
14}
15
16impl RenderKotlin for ClassMemberNode {
17 fn render_into(&self, block: &mut CodeBlock) {
18 match self {
19 ClassMemberNode::Property(property) => {
20 block.push_renderable(property);
21 }
22 ClassMemberNode::Function(function) => {
23 block.push_renderable(function);
24 }
25 ClassMemberNode::Subclass(subclass) => {
26 block.push_renderable(subclass);
27 }
28 ClassMemberNode::SecondaryConstructor(secondary_constructor) => {
29 block.push_renderable(secondary_constructor);
30 }
31 ClassMemberNode::InitBlock(code) => {
32 block.push_static_atom(tokens::keyword::INIT);
33 block.push_curly_brackets(|block| {
34 block.push_renderable(code);
35 });
36 }
37 }
38 }
39}
40
41#[derive(Debug, Clone)]
42struct EnumInstance {
43 name: Name,
44 arguments: Vec<Argument>,
45}
46
47#[derive(Debug, Clone)]
73pub struct Class {
74 name: Name,
75 visibility_modifier: VisibilityModifier,
76 inheritance_modifier: ClassInheritanceModifier,
77 member_nodes: Vec<ClassMemberNode>,
78 enum_instances: Vec<EnumInstance>,
79 primary_constructor: Option<PrimaryConstructor>,
80 companion_object: Option<CompanionObject>,
81 generic_parameters: Vec<GenericParameter>,
82 parent_classes: Vec<Type>,
83 is_inner: bool,
84 annotation_slot: AnnotationSlot,
85 kdoc: KdocSlot,
86}
87
88impl Class {
89 pub fn new<NameLike: Into<Name>>(name: NameLike) -> Self {
91 Class {
92 name: name.into(),
93 visibility_modifier: VisibilityModifier::default(),
94 inheritance_modifier: ClassInheritanceModifier::default(),
95 member_nodes: Vec::default(),
96 enum_instances: Vec::default(),
97 primary_constructor: None,
98 companion_object: None,
99 generic_parameters: Vec::default(),
100 parent_classes: Vec::default(),
101 is_inner: false,
102 annotation_slot: AnnotationSlot::vertical(),
103 kdoc: KdocSlot::default(),
104 }
105 }
106
107 pub fn inner(mut self, flag: bool) -> Self {
109 self.is_inner = flag;
110 self
111 }
112
113 pub fn visibility_modifier(mut self, visibility_modifier: VisibilityModifier) -> Self {
115 self.visibility_modifier = visibility_modifier;
116 self
117 }
118
119 pub fn inheritance_modifier(mut self, inheritance_modifier: ClassInheritanceModifier) -> Self {
121 self.inheritance_modifier = inheritance_modifier;
122 self
123 }
124
125 pub fn property(mut self, property: Property) -> Self {
127 self.member_nodes.push(ClassMemberNode::Property(property));
128 self
129 }
130
131 pub fn function(mut self, function: Function) -> Self {
133 self.member_nodes.push(ClassMemberNode::Function(function));
134 self
135 }
136
137 pub fn subclass(mut self, subclass: Class) -> Self {
139 self.member_nodes.push(ClassMemberNode::Subclass(subclass));
140 self
141 }
142
143 pub fn enum_instance<NameLike: Into<Name>>(mut self, name: NameLike, arguments: Vec<Argument>) -> Self {
146 self.enum_instances.push(EnumInstance {
147 name: name.into(),
148 arguments,
149 });
150 self
151 }
152
153 pub fn primary_constructor(mut self, primary_constructor: PrimaryConstructor) -> Self {
155 self.primary_constructor = Some(primary_constructor);
156 self
157 }
158
159 pub fn secondary_constructor(mut self, secondary_constructor: SecondaryConstructor) -> Self {
161 self.member_nodes.push(ClassMemberNode::SecondaryConstructor(secondary_constructor));
162 self
163 }
164
165 pub fn init<CodeBlockLike: Into<CodeBlock>>(mut self, block: CodeBlockLike) -> Self {
167 self.member_nodes.push(ClassMemberNode::InitBlock(block.into()));
168 self
169 }
170
171 pub fn companion_object(mut self, companion_object: CompanionObject) -> Self {
173 self.companion_object = Some(companion_object);
174 self
175 }
176
177 pub fn generic_parameter(mut self, generic_parameter: GenericParameter) -> Self {
180 self.generic_parameters.push(generic_parameter);
181 self
182 }
183
184 pub fn inherits<TypeLike: Into<Type>>(mut self, parent_type: TypeLike) -> Self {
186 self.parent_classes.push(parent_type.into());
187 self
188 }
189
190 mixin_annotation_mutators!();
191 mixin_kdoc_mutators!();
192}
193
194impl RenderKotlin for Class {
195 fn render_into(&self, block: &mut CodeBlock) {
196 block.push_renderable(&self.kdoc);
197 block.push_renderable(&self.annotation_slot);
198
199 block.push_renderable(&self.visibility_modifier);
200 block.push_space();
201 if self.is_inner {
202 block.push_static_atom(tokens::keyword::INNER);
203 block.push_space();
204 }
205 block.push_renderable(&self.inheritance_modifier);
206 block.push_space();
207 if !matches!(
208 self.inheritance_modifier,
209 ClassInheritanceModifier::Interface |
210 ClassInheritanceModifier::Object
211 ) {
212 block.push_static_atom(tokens::keyword::CLASS);
213 block.push_space();
214 }
215 block.push_renderable(&self.name);
216 if !self.generic_parameters.is_empty() {
217 block.push_angle_brackets(|code| {
218 code.push_comma_separated(
219 &self.generic_parameters.iter().map(|it| it.render_definition())
220 .collect::<Vec<CodeBlock>>()
221 );
222 });
223 }
224 block.push_space();
225
226 if let Some(primary_constructor) = &self.primary_constructor {
227 block.push_renderable(primary_constructor);
228 block.push_space();
229 }
230
231 if !self.parent_classes.is_empty() {
232 block.pop_space();
233 block.push_static_atom(tokens::COLON);
234 block.push_space();
235 block.push_comma_separated(
236 &self.parent_classes
237 );
238 block.push_space();
239 }
240
241 block.push_renderable(
242 &GenericParameter::render_type_boundaries_vec_if_required(
243 &self.generic_parameters
244 )
245 );
246
247 block.push_curly_brackets(|class_body_code| {
248 class_body_code.push_new_line();
249
250 if !self.enum_instances.is_empty() {
251 for (inst_idx, instance) in self.enum_instances.iter().enumerate() {
252 class_body_code.push_renderable(&instance.name);
253 class_body_code.push_round_brackets(|arg_code| {
254 arg_code.push_comma_separated(&instance.arguments);
255 });
256
257 if inst_idx != self.enum_instances.len() - 1 {
258 class_body_code.push_static_atom(tokens::COMMA);
259 class_body_code.push_new_line();
260 }
261 }
262
263 class_body_code.push_static_atom(tokens::SEMICOLON);
264 }
265
266 for node in &self.member_nodes {
267 class_body_code.push_renderable(node);
268 class_body_code.push_new_line();
269 }
270
271 if let Some(companion_object) = &self.companion_object {
272 class_body_code.push_renderable(companion_object);
273 class_body_code.push_new_line();
274 }
275 });
276 }
277}
278
279#[cfg(test)]
280mod tests {
281 use crate::spec::{Parameter, GenericInvariance, PropertyGetter, PropertySetter, Type, ClassLikeTypeName, Package, KDoc};
282 use super::*;
283
284 #[test]
285 fn test_class() {
286 let class = Class::new(Name::from("Person"));
287 let code = class.render_string();
288
289 assert_eq!(code, "public final class Person {\n\n}");
290 }
291
292 #[test]
293 fn test_class_with_kdoc() {
294 let class = Class::new(Name::from("Person"))
295 .kdoc(
296 KDoc::from("hello world")
297 .merge(KDoc::from("at here"))
298 );
299 let code = class.render_string();
300
301 assert_eq!(
302 code,
303 "/**\n * hello world\n * at here\n */\npublic final class Person {\n\n}"
304 );
305 }
306
307 #[test]
308 fn test_class_with_property() {
309 let property = Property::new(
310 Name::from("name"),
311 Type::string(),
312 ).initializer(
313 CodeBlock::statement("\"\"")
314 ).getter(
315 PropertyGetter::new(
316 CodeBlock::statement("return field")
317 )
318 ).setter(
319 PropertySetter::new(
320 CodeBlock::statement("field = value")
321 )
322 );
323
324 let class = Class::new(Name::from("Person"))
325 .property(property.clone());
326
327 let code = class.render_string();
328
329 assert_eq!(
330 code,
331 "public final class Person {\n\n public final var name: kotlin.String = \"\"\n set(value) {\n field = value\n }\n get() {\n return field\n }\n\n}"
332 );
333 }
334
335 #[test]
336 fn test_enum() {
337 let class = Class::new(Name::from("Person"))
338 .inheritance_modifier(ClassInheritanceModifier::Enum)
339 .enum_instance(Name::from("Alex"), vec![
340 Argument::new_positional(CodeBlock::atom("23"))
341 ])
342 .enum_instance(Name::from("Vova"), vec![
343 Argument::new_positional(CodeBlock::atom("23"))
344 ])
345 ;
346 let code = class.render_string();
347
348 assert_eq!(
349 code,
350 "public enum class Person {\n\n Alex(23),\n Vova(23);}"
351 );
352 }
353
354 #[test]
355 fn test_with_constructor() {
356 let class = Class::new(Name::from("Person"))
357 .primary_constructor(
358 PrimaryConstructor::new()
359 .property(
360 Property::new(
361 Name::from("name"),
362 Type::string(),
363 )
364 )
365 .parameter(
366 Parameter::new(
367 Name::from("age"),
368 Type::int(),
369 )
370 )
371 );
372
373 assert_eq!(
374 class.render_string(),
375 "public final class Person public constructor(public final val name: kotlin.String, age: kotlin.Int) {\n\n}"
376 );
377 }
378
379 #[test]
380 fn test_with_empty_constructor() {
381 let class = Class::new(Name::from("Person"))
382 .primary_constructor(
383 PrimaryConstructor::new()
384 );
385
386 assert_eq!(
387 class.render_string(),
388 "public final class Person public constructor() {\n\n}"
389 );
390 }
391
392 #[test]
393 fn test_with_init_block() {
394 let class = Class::new(Name::from("Person"))
395 .init(
396 CodeBlock::statement("println(42)")
397 );
398
399 assert_eq!(
400 class.render_string(),
401 "public final class Person {\n\n init{\n println(42)\n }\n}"
402 );
403 }
404
405 #[test]
406 fn test_data_class() {
407 let class = Class::new(Name::from("Person"))
408 .inheritance_modifier(ClassInheritanceModifier::Data)
409 .primary_constructor(
410 PrimaryConstructor::new()
411 .property(
412 Property::new(
413 Name::from("name"),
414 Type::string(),
415 ).initializer(
416 CodeBlock::atom("\"\"")
417 )
418 )
419 );
420
421 assert_eq!(
422 class.render_string(),
423 "public data class Person public constructor(public final val name: kotlin.String = \"\") {\n\n}"
424 );
425 }
426
427 #[test]
428 fn test_data_class_with_secondary_constructor() {
429 let class = Class::new(Name::from("Person"))
430 .inheritance_modifier(ClassInheritanceModifier::Data)
431 .primary_constructor(
432 PrimaryConstructor::new()
433 .property(
434 Property::new(
435 Name::from("name"),
436 Type::string(),
437 )
438 )
439 .property(
440 Property::new(
441 Name::from("age"),
442 Type::int(),
443 )
444 )
445 )
446 .secondary_constructor(
447 SecondaryConstructor::new()
448 .parameter(
449 Parameter::new(
450 Name::from("name"),
451 Type::string(),
452 )
453 )
454 .delegate_argument(
455 Argument::new_positional(
456 CodeBlock::atom("name")
457 )
458 )
459 .delegate_argument(
460 Argument::new_positional(
461 CodeBlock::atom("23")
462 )
463 )
464 .body(
465 CodeBlock::statement("println(42)")
466 )
467 );
468
469 assert_eq!(
470 class.render_string(),
471 "public data class Person public constructor(public final val name: kotlin.String, public final val age: kotlin.Int) {\n\n public constructor(name: kotlin.String) : this(name, 23) {\n println(42)\n }\n}"
472 );
473 }
474
475 #[test]
476 fn test_interface() {
477 let class = Class::new(Name::from("Person"))
478 .inheritance_modifier(ClassInheritanceModifier::Interface);
479
480 assert_eq!(class.render_string(), "public interface Person {\n\n}");
481 }
482
483 #[test]
484 fn test_abstract() {
485 let class = Class::new(Name::from("Person"))
486 .inheritance_modifier(ClassInheritanceModifier::Abstract);
487
488 assert_eq!(class.render_string(), "public abstract class Person {\n\n}");
489 }
490
491 #[test]
492 fn test_object() {
493 let class = Class::new(Name::from("Person"))
494 .inheritance_modifier(ClassInheritanceModifier::Object);
495
496 assert_eq!(class.render_string(), "public object Person {\n\n}");
497 }
498
499 #[test]
500 fn test_class_with_inner() {
501 let class = Class::new(Name::from("Person"))
502 .subclass(
503 Class::new("InnerPerson")
504 .inheritance_modifier(ClassInheritanceModifier::Abstract)
505 .inner(true)
506 );
507
508 assert_eq!(
509 class.render_string(),
510 "public final class Person {\n\n public inner abstract class InnerPerson {\n\n }\n}"
511 );
512 }
513
514 #[test]
515 fn test_sealed() {
516 let class = Class::new(Name::from("Person"))
517 .inheritance_modifier(ClassInheritanceModifier::Sealed);
518
519 assert_eq!(class.render_string(), "public sealed class Person {\n\n}");
520 }
521
522 #[test]
523 fn test_generic_class() {
524 let class = Class::new(Name::from("Box"))
525 .generic_parameter(
526 GenericParameter::new(Name::from("A"))
527 )
528 .generic_parameter(
529 GenericParameter::new(Name::from("B"))
530 .invariance(GenericInvariance::In)
531 )
532 .generic_parameter(
533 GenericParameter::new(Name::from("C"))
534 .invariance(GenericInvariance::Out)
535 );
536
537 assert_eq!(class.render_string(), "public final class Box<A, in B, out C> {\n\n}");
538 }
539
540 #[test]
541 fn test_generic_with_parent() {
542 let class = Class::new(Name::from("Box"))
543 .generic_parameter(
544 GenericParameter::new(Name::from("A"))
545 .invariance(GenericInvariance::In)
546 .type_boundary(Type::string())
547 )
548 .inherits(
549 Type::int()
550 );
551
552 assert_eq!(
553 class.render_string(),
554 "public final class Box<in A>: kotlin.Int where A: kotlin.String {\n\n}"
555 );
556 }
557
558 #[test]
559 fn test_generic_class_with_boundaries() {
560 let class = Class::new(Name::from("Box"))
561 .generic_parameter(
562 GenericParameter::new(Name::from("A"))
563 )
564 .generic_parameter(
565 GenericParameter::new(Name::from("B"))
566 .invariance(GenericInvariance::In)
567 .type_boundary(Type::string())
568 .type_boundary(Type::int())
569 )
570 .generic_parameter(
571 GenericParameter::new(Name::from("C"))
572 .invariance(GenericInvariance::Out)
573 );
574
575 assert_eq!(class.render_string(), "public final class Box<A, in B, out C> where B: kotlin.String, B: kotlin.Int {\n\n}");
576 }
577
578 #[test]
579 fn test_with_annotation() {
580 let class = Class::new(Name::from("Person"))
581 .annotation(
582 Annotation::new(ClassLikeTypeName::top_level(
583 Package::from(Vec::new()),
584 Name::from("Deprecated"),
585 ))
586 );
587
588 assert_eq!(class.render_string(), "@Deprecated()\npublic final class Person {\n\n}");
589 }
590}