1use crate::writer::{Writer, comment_lines, escape_char, escape_string, verbatim_lines};
9use alloc::string::{String, ToString};
10use alloc::vec::Vec;
11use zerodds_lang_cst::common::{Doc, Ident, Literal, Path};
12use zerodds_lang_cst::java::{
13 Annotation, AnnotationArg, AnnotationElement, AnnotationType, Block, Class, Constructor, Enum,
14 EnumConstant, Expr, Field, File, Import, Interface, Member, Method, Modifier, Param, Record,
15 Statement, SwitchCase, Type, TypeDecl, TypeParam,
16};
17
18#[must_use]
20pub fn file(file: &File) -> String {
21 let mut w = Writer::spaces4();
22 if !file.doc.is_empty() {
27 line_comment(&mut w, &file.doc);
28 w.blank_line();
29 }
30 if let Some(package) = &file.package {
31 let mut line = String::from("package ");
32 line.push_str(&path_text(package));
33 line.push(';');
34 w.line(&line);
35 w.blank_line();
36 }
37 for import in &file.imports {
38 w.line(&import_text(import));
39 }
40 if !file.imports.is_empty() {
41 w.blank_line();
42 }
43 for (index, decl) in file.types.iter().enumerate() {
44 if index > 0 {
45 w.blank_line();
46 }
47 render_type_decl(&mut w, decl);
48 }
49 w.finish()
50}
51
52#[must_use]
54pub fn type_decl(node: &TypeDecl) -> String {
55 let mut w = Writer::spaces4();
56 render_type_decl(&mut w, node);
57 w.finish()
58}
59
60#[must_use]
62pub fn type_(node: &Type) -> String {
63 type_text(node)
64}
65
66fn import_text(import: &Import) -> String {
67 let mut out = String::from("import ");
68 if import.static_ {
69 out.push_str("static ");
70 }
71 out.push_str(&path_text(&import.path));
72 if import.wildcard {
73 out.push_str(".*");
74 }
75 out.push(';');
76 out
77}
78
79fn render_type_decl(w: &mut Writer, node: &TypeDecl) {
80 match node {
81 TypeDecl::Class(class) => render_class(w, class),
82 TypeDecl::Interface(interface) => render_interface(w, interface),
83 TypeDecl::Enum(e) => render_enum(w, e),
84 TypeDecl::Record(record) => render_record(w, record),
85 TypeDecl::AnnotationType(annotation) => render_annotation_type(w, annotation),
86 TypeDecl::Verbatim(text) => verbatim_lines(w, text),
87 }
88}
89
90fn render_class(w: &mut Writer, node: &Class) {
91 doc(w, &node.doc);
92 annotations(w, &node.annotations);
93 let mut head = modifiers_text(&node.modifiers);
94 head.push_str("class ");
95 head.push_str(node.name.as_str());
96 head.push_str(&generics_text(&node.generics));
97 if let Some(extends) = &node.extends {
98 head.push_str(" extends ");
99 head.push_str(&type_text(extends));
100 }
101 if !node.implements.is_empty() {
102 head.push_str(" implements ");
103 let parts: Vec<String> = node.implements.iter().map(type_text).collect();
104 head.push_str(&parts.join(", "));
105 }
106 head.push_str(" {");
107 w.line(&head);
108 w.indent();
109 render_members(w, &node.members);
110 w.dedent();
111 w.line("}");
112}
113
114fn render_interface(w: &mut Writer, node: &Interface) {
115 doc(w, &node.doc);
116 annotations(w, &node.annotations);
117 let mut head = modifiers_text(&node.modifiers);
118 head.push_str("interface ");
119 head.push_str(node.name.as_str());
120 head.push_str(&generics_text(&node.generics));
121 if !node.extends.is_empty() {
122 head.push_str(" extends ");
123 let parts: Vec<String> = node.extends.iter().map(type_text).collect();
124 head.push_str(&parts.join(", "));
125 }
126 head.push_str(" {");
127 w.line(&head);
128 w.indent();
129 render_members(w, &node.members);
130 w.dedent();
131 w.line("}");
132}
133
134fn render_enum(w: &mut Writer, node: &Enum) {
135 doc(w, &node.doc);
136 annotations(w, &node.annotations);
137 let mut head = modifiers_text(&node.modifiers);
138 head.push_str("enum ");
139 head.push_str(node.name.as_str());
140 if !node.implements.is_empty() {
141 head.push_str(" implements ");
142 let parts: Vec<String> = node.implements.iter().map(type_text).collect();
143 head.push_str(&parts.join(", "));
144 }
145 head.push_str(" {");
146 w.line(&head);
147 w.indent();
148 for (index, constant) in node.constants.iter().enumerate() {
149 let last = index + 1 == node.constants.len();
150 render_enum_constant(w, constant, last, node.members.is_empty());
151 }
152 if !node.members.is_empty() {
153 w.blank_line();
154 render_members(w, &node.members);
155 }
156 w.dedent();
157 w.line("}");
158}
159
160fn render_enum_constant(w: &mut Writer, node: &EnumConstant, last: bool, no_members: bool) {
164 doc(w, &node.doc);
165 annotations(w, &node.annotations);
166 let mut line = String::from(node.name.as_str());
167 if !node.args.is_empty() {
168 line.push('(');
169 let parts: Vec<String> = node.args.iter().map(expr_text).collect();
170 line.push_str(&parts.join(", "));
171 line.push(')');
172 }
173 if !node.body.is_empty() {
174 line.push_str(" {");
175 w.line(&line);
176 w.indent();
177 render_members(w, &node.body);
178 w.dedent();
179 w.line(if last { "}" } else { "}," });
180 return;
181 }
182 if last {
183 if !no_members {
184 line.push(';');
185 }
186 } else {
187 line.push(',');
188 }
189 w.line(&line);
190}
191
192fn render_record(w: &mut Writer, node: &Record) {
193 doc(w, &node.doc);
194 annotations(w, &node.annotations);
195 let mut head = modifiers_text(&node.modifiers);
196 head.push_str("record ");
197 head.push_str(node.name.as_str());
198 head.push_str(&generics_text(&node.generics));
199 head.push('(');
200 head.push_str(¶ms_text(&node.components));
201 head.push(')');
202 if !node.implements.is_empty() {
203 head.push_str(" implements ");
204 let parts: Vec<String> = node.implements.iter().map(type_text).collect();
205 head.push_str(&parts.join(", "));
206 }
207 head.push_str(" {");
208 w.line(&head);
209 w.indent();
210 render_members(w, &node.members);
211 w.dedent();
212 w.line("}");
213}
214
215fn render_annotation_type(w: &mut Writer, node: &AnnotationType) {
216 doc(w, &node.doc);
217 annotations(w, &node.annotations);
218 let mut head = modifiers_text(&node.modifiers);
219 head.push_str("@interface ");
220 head.push_str(node.name.as_str());
221 head.push_str(" {");
222 w.line(&head);
223 w.indent();
224 for element in &node.elements {
225 render_annotation_element(w, element);
226 }
227 w.dedent();
228 w.line("}");
229}
230
231fn render_annotation_element(w: &mut Writer, node: &AnnotationElement) {
232 doc(w, &node.doc);
233 let mut line = type_text(&node.type_);
234 line.push(' ');
235 line.push_str(node.name.as_str());
236 line.push_str("()");
237 if let Some(default) = &node.default {
238 line.push_str(" default ");
239 line.push_str(&expr_text(default));
240 }
241 line.push(';');
242 w.line(&line);
243}
244
245fn render_members(w: &mut Writer, members: &[Member]) {
247 let mut previous_had_body = false;
248 for (index, member) in members.iter().enumerate() {
249 let has_body = !matches!(member, Member::Field(_) | Member::Comment(_));
250 if index > 0 && (has_body || previous_had_body) {
251 w.blank_line();
252 }
253 render_member(w, member);
254 previous_had_body = has_body;
255 }
256}
257
258fn render_member(w: &mut Writer, node: &Member) {
259 match node {
260 Member::Field(field) => render_field(w, field),
261 Member::Method(method) => render_method(w, method),
262 Member::Constructor(constructor) => render_constructor(w, constructor),
263 Member::Nested(decl) => render_type_decl(w, decl),
264 Member::Initialiser { static_, body } => {
265 w.line(if *static_ { "static {" } else { "{" });
266 w.indent();
267 render_block(w, body);
268 w.dedent();
269 w.line("}");
270 }
271 Member::Comment(c) => line_comment(w, c),
272 Member::Verbatim(text) => verbatim_lines(w, text),
273 }
274}
275
276fn render_field(w: &mut Writer, node: &Field) {
277 doc(w, &node.doc);
278 annotations(w, &node.annotations);
279 let mut line = modifiers_text(&node.modifiers);
280 line.push_str(&type_text(&node.type_));
281 line.push(' ');
282 line.push_str(node.name.as_str());
283 if let Some(value) = &node.value {
284 line.push_str(" = ");
285 line.push_str(&expr_text(value));
286 }
287 line.push(';');
288 w.line(&line);
289}
290
291fn render_method(w: &mut Writer, node: &Method) {
292 doc(w, &node.doc);
293 annotations(w, &node.annotations);
294 let mut head = modifiers_text(&node.modifiers);
295 let generics = generics_text(&node.generics);
296 if !generics.is_empty() {
297 head.push_str(&generics);
298 head.push(' ');
299 }
300 head.push_str(&type_text(&node.returns));
301 head.push(' ');
302 head.push_str(node.name.as_str());
303 head.push('(');
304 head.push_str(¶ms_text(&node.params));
305 head.push(')');
306 if !node.throws.is_empty() {
307 head.push_str(" throws ");
308 let parts: Vec<String> = node.throws.iter().map(type_text).collect();
309 head.push_str(&parts.join(", "));
310 }
311 match &node.body {
312 None => {
313 head.push(';');
314 w.line(&head);
315 }
316 Some(block) => {
317 head.push_str(" {");
318 w.line(&head);
319 w.indent();
320 render_block(w, block);
321 w.dedent();
322 w.line("}");
323 }
324 }
325}
326
327fn render_constructor(w: &mut Writer, node: &Constructor) {
328 doc(w, &node.doc);
329 annotations(w, &node.annotations);
330 let mut head = modifiers_text(&node.modifiers);
331 head.push_str(node.name.as_str());
332 head.push('(');
333 head.push_str(¶ms_text(&node.params));
334 head.push(')');
335 if !node.throws.is_empty() {
336 head.push_str(" throws ");
337 let parts: Vec<String> = node.throws.iter().map(type_text).collect();
338 head.push_str(&parts.join(", "));
339 }
340 head.push_str(" {");
341 w.line(&head);
342 w.indent();
343 render_block(w, &node.body);
344 w.dedent();
345 w.line("}");
346}
347
348fn render_block(w: &mut Writer, block: &Block) {
359 for statement in &block.statements {
360 render_statement(w, statement);
361 }
362}
363
364fn render_statement(w: &mut Writer, node: &Statement) {
375 match node {
376 Statement::Declare {
377 final_,
378 type_,
379 name,
380 value,
381 } => {
382 let mut line = String::new();
383 if *final_ {
384 line.push_str("final ");
385 }
386 line.push_str(&type_text(type_));
387 line.push(' ');
388 line.push_str(name.as_str());
389 if let Some(value) = value {
390 line.push_str(" = ");
391 line.push_str(&expr_text(value));
392 }
393 line.push(';');
394 w.line(&line);
395 }
396 Statement::Expr(value) => {
397 let mut line = expr_text(value);
398 line.push(';');
399 w.line(&line);
400 }
401 Statement::Return(value) => {
402 let mut line = String::from("return");
403 if let Some(value) = value {
404 line.push(' ');
405 line.push_str(&expr_text(value));
406 }
407 line.push(';');
408 w.line(&line);
409 }
410 Statement::If {
411 condition,
412 then,
413 otherwise,
414 } => {
415 let mut head = String::from("if (");
416 head.push_str(&expr_text(condition));
417 head.push_str(") {");
418 w.line(&head);
419 w.indent();
420 render_block(w, then);
421 w.dedent();
422 match otherwise {
423 Some(block) => {
424 w.line("} else {");
425 w.indent();
426 render_block(w, block);
427 w.dedent();
428 w.line("}");
429 }
430 None => w.line("}"),
431 }
432 }
433 Statement::For {
434 init,
435 condition,
436 step,
437 body,
438 } => {
439 let mut head = String::from("for (");
440 head.push_str(statement_inline(init).trim_end_matches(';'));
441 head.push_str("; ");
442 head.push_str(&expr_text(condition));
443 head.push_str("; ");
444 head.push_str(&expr_text(step));
445 head.push_str(") {");
446 w.line(&head);
447 w.indent();
448 render_block(w, body);
449 w.dedent();
450 w.line("}");
451 }
452 Statement::While { condition, body } => {
453 let mut head = String::from("while (");
454 head.push_str(&expr_text(condition));
455 head.push_str(") {");
456 w.line(&head);
457 w.indent();
458 render_block(w, body);
459 w.dedent();
460 w.line("}");
461 }
462 Statement::Break => w.line("break;"),
463 Statement::ForEach {
464 type_,
465 binding,
466 range,
467 body,
468 } => {
469 let mut head = String::from("for (");
470 head.push_str(&type_text(type_));
471 head.push(' ');
472 head.push_str(binding.as_str());
473 head.push_str(" : ");
474 head.push_str(&expr_text(range));
475 head.push_str(") {");
476 w.line(&head);
477 w.indent();
478 render_block(w, body);
479 w.dedent();
480 w.line("}");
481 }
482 Statement::Switch { value, cases } => {
483 let mut head = String::from("switch (");
484 head.push_str(&expr_text(value));
485 head.push_str(") {");
486 w.line(&head);
487 for case in cases {
488 render_case(w, case);
489 }
490 w.line("}");
491 }
492 Statement::Throw(value) => {
493 let mut line = String::from("throw ");
494 line.push_str(&expr_text(value));
495 line.push(';');
496 w.line(&line);
497 }
498 Statement::Comment(c) => line_comment(w, c),
499 }
500}
501
502fn render_case(w: &mut Writer, case: &SwitchCase) {
503 if case.labels.is_empty() {
504 w.line("default:");
505 } else {
506 for label in &case.labels {
507 let mut line = String::from("case ");
508 line.push_str(&expr_text(label));
509 line.push(':');
510 w.line(&line);
511 }
512 }
513 w.indent();
514 render_block(w, &case.body);
515 if case.breaks {
516 w.line("break;");
517 }
518 w.dedent();
519}
520
521fn modifiers_text(modifiers: &[Modifier]) -> String {
522 let mut out = String::new();
523 for modifier in modifiers {
524 out.push_str(modifier.as_str());
525 out.push(' ');
526 }
527 out
528}
529
530fn annotations(w: &mut Writer, annotations: &[Annotation]) {
531 for annotation in annotations {
532 w.line(&annotation_text(annotation));
533 }
534}
535
536fn annotation_text(annotation: &Annotation) -> String {
540 let mut out = String::from("@");
541 out.push_str(&path_text(&annotation.path));
542 if !annotation.args.is_empty() {
543 out.push('(');
544 let parts: Vec<String> = annotation
545 .args
546 .iter()
547 .map(|arg| match arg {
548 AnnotationArg::Value(value) => expr_text(value),
549 AnnotationArg::Pair { name, value } => {
550 let mut text = name.as_str().to_string();
551 text.push_str(" = ");
552 text.push_str(&expr_text(value));
553 text
554 }
555 })
556 .collect();
557 out.push_str(&parts.join(", "));
558 out.push(')');
559 }
560 out
561}
562
563fn generics_text(params: &[TypeParam]) -> String {
564 if params.is_empty() {
565 return String::new();
566 }
567 let mut out = String::from("<");
568 let parts: Vec<String> = params
569 .iter()
570 .map(|param| {
571 let mut text = param.name.as_str().to_string();
572 if !param.bounds.is_empty() {
573 text.push_str(" extends ");
574 let bounds: Vec<String> = param.bounds.iter().map(type_text).collect();
575 text.push_str(&bounds.join(" & "));
576 }
577 text
578 })
579 .collect();
580 out.push_str(&parts.join(", "));
581 out.push('>');
582 out
583}
584
585fn params_text(params: &[Param]) -> String {
586 let parts: Vec<String> = params
587 .iter()
588 .map(|param| {
589 let mut text = String::new();
590 for annotation in ¶m.annotations {
591 text.push_str(&annotation_text(annotation));
592 text.push(' ');
593 }
594 if param.final_ {
595 text.push_str("final ");
596 }
597 text.push_str(&type_text(¶m.type_));
598 if param.varargs {
599 text.push_str("...");
600 }
601 text.push(' ');
602 text.push_str(param.name.as_str());
603 text
604 })
605 .collect();
606 parts.join(", ")
607}
608
609fn path_text(path: &Path) -> String {
610 let parts: Vec<&str> = path.segments().iter().map(Ident::as_str).collect();
611 parts.join(".")
612}
613
614fn type_text(node: &Type) -> String {
618 match node {
619 Type::Primitive(primitive) => primitive.as_str().to_string(),
620 Type::Named { path, args } => {
621 let mut out = path_text(path);
622 if !args.is_empty() {
623 out.push('<');
624 let parts: Vec<String> = args.iter().map(type_text).collect();
625 out.push_str(&parts.join(", "));
626 out.push('>');
627 }
628 out
629 }
630 Type::Array(element) => {
631 let mut out = type_text(element);
632 out.push_str("[]");
633 out
634 }
635 Type::Wildcard { extends, super_ } => {
636 let mut out = String::from("?");
637 if let Some(bound) = extends {
638 out.push_str(" extends ");
639 out.push_str(&type_text(bound));
640 } else if let Some(bound) = super_ {
641 out.push_str(" super ");
642 out.push_str(&type_text(bound));
643 }
644 out
645 }
646 Type::Void => "void".to_string(),
647 Type::Var => "var".to_string(),
648 }
649}
650
651#[allow(clippy::too_many_lines)]
658fn expr_text(node: &Expr) -> String {
659 match node {
660 Expr::Literal(literal) => literal_text(literal),
661 Expr::Path(path) => path_text(path),
662 Expr::MethodCall {
663 receiver,
664 name,
665 args,
666 } => {
667 let mut out = String::new();
668 if let Some(receiver) = receiver {
669 out.push_str(&expr_text(receiver));
670 out.push('.');
671 }
672 out.push_str(name.as_str());
673 out.push('(');
674 let parts: Vec<String> = args.iter().map(expr_text).collect();
675 out.push_str(&parts.join(", "));
676 out.push(')');
677 out
678 }
679 Expr::Field { value, name } => {
680 let mut out = expr_text(value);
681 out.push('.');
682 out.push_str(name.as_str());
683 out
684 }
685 Expr::Index { value, index } => {
686 let mut out = expr_text(value);
687 out.push('[');
688 out.push_str(&expr_text(index));
689 out.push(']');
690 out
691 }
692 Expr::New { type_, args } => {
693 let mut out = String::from("new ");
694 out.push_str(&type_text(type_));
695 out.push('(');
696 let parts: Vec<String> = args.iter().map(expr_text).collect();
697 out.push_str(&parts.join(", "));
698 out.push(')');
699 out
700 }
701 Expr::NewArray {
702 element,
703 lengths,
704 elements,
705 } => {
706 let mut base = element.as_ref();
710 let mut trailing = 0;
711 while let Type::Array(inner) = base {
712 base = inner;
713 trailing += 1;
714 }
715 if lengths.is_empty() {
716 let mut out = String::from("new ");
717 out.push_str(&type_text(element));
718 out.push_str("[]{");
719 let parts: Vec<String> = elements.iter().map(expr_text).collect();
720 out.push_str(&parts.join(", "));
721 out.push('}');
722 return out;
723 }
724 let mut out = String::from("new ");
725 out.push_str(&type_text(base));
726 for length in lengths {
727 out.push('[');
728 out.push_str(&expr_text(length));
729 out.push(']');
730 }
731 for _ in 0..trailing {
732 out.push_str("[]");
733 }
734 out
735 }
736 Expr::Binary { op, lhs, rhs } => {
737 let mut out = expr_text(lhs);
738 out.push(' ');
739 out.push_str(op);
740 out.push(' ');
741 out.push_str(&expr_text(rhs));
742 out
743 }
744 Expr::Paren(value) => {
745 let mut out = String::from("(");
746 out.push_str(&expr_text(value));
747 out.push(')');
748 out
749 }
750 Expr::Cast { type_, value } => {
751 let mut out = String::from("(");
752 out.push_str(&type_text(type_));
753 out.push_str(") ");
754 out.push_str(&expr_text(value));
755 out
756 }
757 Expr::InstanceOf { value, type_ } => {
758 let mut out = expr_text(value);
759 out.push_str(" instanceof ");
760 out.push_str(&type_text(type_));
761 out
762 }
763 Expr::Ternary {
764 condition,
765 then,
766 otherwise,
767 } => {
768 let mut out = expr_text(condition);
769 out.push_str(" ? ");
770 out.push_str(&expr_text(then));
771 out.push_str(" : ");
772 out.push_str(&expr_text(otherwise));
773 out
774 }
775 Expr::This => "this".to_string(),
776 Expr::Lambda { params, body } => {
777 let mut out = String::from("(");
778 let parts: Vec<&str> = params.iter().map(Ident::as_str).collect();
779 out.push_str(&parts.join(", "));
780 out.push_str(") -> ");
781 out.push_str(&expr_text(body));
782 out
783 }
784 Expr::Annotation(annotation) => annotation_text(annotation),
785 Expr::ArrayInit(elements) => {
786 let parts: Vec<String> = elements.iter().map(expr_text).collect();
787 let mut out = String::from("{");
788 out.push_str(&parts.join(", "));
789 out.push('}');
790 out
791 }
792 }
793}
794
795fn literal_text(literal: &Literal) -> String {
796 match literal {
797 Literal::Int(text) | Literal::Float(text) => text.clone(),
798 Literal::Bool(value) => {
799 if *value {
800 "true".to_string()
801 } else {
802 "false".to_string()
803 }
804 }
805 Literal::Char(ch) => {
806 let mut out = String::from("'");
807 out.push_str(&escape_char(*ch));
808 out.push('\'');
809 out
810 }
811 Literal::Str(text) => {
812 let mut out = String::from("\"");
813 out.push_str(&escape_string(text));
814 out.push('"');
815 out
816 }
817 Literal::Bytes(bytes) => {
818 let mut out = String::from("new byte[]{");
819 let parts: Vec<String> = bytes
820 .iter()
821 .map(|b| alloc::format!("(byte) 0x{b:02X}"))
822 .collect();
823 out.push_str(&parts.join(", "));
824 out.push('}');
825 out
826 }
827 Literal::Null => "null".to_string(),
828 }
829}
830
831fn doc(w: &mut Writer, doc: &Doc) {
832 if doc.is_empty() {
833 return;
834 }
835 w.line("/**");
837 comment_lines(w, " * ", doc.as_lines());
838 w.line(" */");
839}
840
841fn line_comment(w: &mut Writer, doc: &Doc) {
847 if doc.is_empty() {
848 return;
849 }
850 comment_lines(w, "// ", doc.as_lines());
851}
852
853fn statement_inline(node: &Statement) -> String {
860 match node {
861 Statement::Declare {
862 final_,
863 type_,
864 name,
865 value,
866 } => {
867 let mut out = String::new();
868 if *final_ {
869 out.push_str("final ");
870 }
871 out.push_str(&type_text(type_));
872 out.push(' ');
873 out.push_str(name.as_str());
874 if let Some(value) = value {
875 out.push_str(" = ");
876 out.push_str(&expr_text(value));
877 }
878 out
879 }
880 Statement::Expr(value) => expr_text(value),
881 _ => String::new(),
882 }
883}