1use std::io::Write;
2
3use convert_case::{Case, Casing as _};
4use typeshare_model::Language;
5use typeshare_model::decorator;
6use typeshare_model::decorator::DecoratorSet;
7use typeshare_model::prelude::*;
8
9use crate::config::HeaderComment;
10use crate::config::JavaConfig;
11use crate::config::JavaSerializerOptions;
12use crate::error::AssertJavaIdentifierError;
13use crate::error::FormatSpecialTypeError;
14use crate::error::WriteDecoratorError;
15use crate::error::WriteEnumError;
16use crate::util::indented_writer::IndentedWriter;
17
18#[derive(Debug)]
19pub struct Java {
20 config: JavaConfig,
21}
22
23impl Language<'_> for Java {
24 type Config = JavaConfig;
25
26 const NAME: &'static str = "java";
27
28 fn new_from_config(config: Self::Config) -> anyhow::Result<Self> {
29 Ok(Self { config })
30 }
31
32 fn output_filename_for_crate(&self, crate_name: &CrateName) -> String {
33 crate_name.as_str().to_case(Case::Pascal) + ".java"
34 }
35
36 fn format_special_type(
37 &self,
38 special_ty: &SpecialRustType,
39 generic_context: &[TypeName],
40 ) -> anyhow::Result<String> {
41 Ok(match special_ty {
42 SpecialRustType::Vec(rtype) => {
43 format!(
44 "java.util.ArrayList<{}>",
45 self.format_type(rtype, generic_context)?
46 )
47 }
48 SpecialRustType::Array(rtype, _) => {
49 format!("{}[]", self.format_type(rtype, generic_context)?)
50 }
51 SpecialRustType::Slice(rtype) => {
52 format!("{}[]", self.format_type(rtype, generic_context)?)
53 }
54 SpecialRustType::HashMap(rtype1, rtype2) => {
55 format!(
56 "java.util.HashMap<{}, {}>",
57 self.format_type(rtype1, generic_context)?,
58 self.format_type(rtype2, generic_context)?
59 )
60 }
61 SpecialRustType::Option(rtype) => self.format_type(rtype, generic_context)?,
62 SpecialRustType::Unit => "Void".into(),
63 SpecialRustType::String | SpecialRustType::Char => "String".into(),
66 SpecialRustType::I8 => "Byte".into(),
67 SpecialRustType::I16 => "Short".into(),
68 SpecialRustType::ISize | SpecialRustType::I32 => "Integer".into(),
69 SpecialRustType::I54 | SpecialRustType::I64 => "Long".into(),
70 SpecialRustType::U8 => "Short".into(),
72 SpecialRustType::U16 => "Integer".into(),
74 SpecialRustType::USize | SpecialRustType::U32 => "Long".into(),
76 SpecialRustType::U53 | SpecialRustType::U64 => "java.math.BigInteger".into(),
78 SpecialRustType::Bool => "Boolean".into(),
80 SpecialRustType::F32 => "Float".into(),
82 SpecialRustType::F64 => "Double".into(),
83 _ => {
84 return Err(
85 FormatSpecialTypeError::UnsupportedSpecialType(special_ty.clone()).into(),
86 );
87 }
88 })
89 }
90
91 fn begin_file(&self, w: &mut impl Write, mode: FilesMode<&CrateName>) -> anyhow::Result<()> {
92 match &self.config.header_comment {
93 HeaderComment::None => {}
94 HeaderComment::Default => {
95 writeln!(w, "/**")?;
96 writeln!(
97 w,
98 " * Generated by typeshare-java {}",
99 env!("CARGO_PKG_VERSION")
100 )?;
101 writeln!(w, " */")?;
102 writeln!(w)?;
103 }
104 HeaderComment::Custom { comment } => {
105 writeln!(w, "/**")?;
106 for comment in comment.split("\n") {
107 writeln!(w, " * {comment}")?;
108 }
109 writeln!(w, " */")?;
110 writeln!(w)?;
111 }
112 }
113
114 if let Some(package) = &self.config.package {
115 if let FilesMode::Multi(crate_name) = mode {
116 writeln!(
117 w,
118 "package {}.{};",
119 package,
120 crate_name.as_str().to_case(Case::Pascal)
121 )?;
122 } else {
123 writeln!(w, "package {package};")?;
124 }
125 writeln!(w)?;
126 }
127
128 match (self.config.namespace_class, mode) {
129 (true, FilesMode::Multi(crate_name)) => {
130 writeln!(
131 w,
132 "public class {} {{",
133 crate_name.as_str().to_case(Case::Pascal),
134 )?;
135 writeln!(w)?;
136 }
137 (true, FilesMode::Single) => {
138 writeln!(w, "public class Namespace {{",)?;
139 writeln!(w)?;
140 }
141 _ => {}
142 }
143
144 Ok(())
145 }
146
147 fn write_imports<'a, Crates, Types>(
148 &self,
149 writer: &mut impl Write,
150 _crate_name: &CrateName,
151 imports: Crates,
152 ) -> anyhow::Result<()>
153 where
154 Crates: IntoIterator<Item = (&'a CrateName, Types)>,
155 Types: IntoIterator<Item = &'a TypeName>,
156 {
157 for (path, ty) in imports {
158 for t in ty {
159 writeln!(
160 writer,
161 "import {}.{path}.{t};",
162 self.config
163 .package
164 .as_ref()
165 .map(|package| format!("{package}."))
166 .unwrap_or_default()
167 )?;
168 }
169 }
170 writeln!(writer).map_err(|err| err.into())
171 }
172
173 fn end_file(&self, w: &mut impl Write, _mode: FilesMode<&CrateName>) -> anyhow::Result<()> {
174 if self.config.namespace_class {
175 writeln!(w, "}}")?;
176 }
177
178 Ok(())
179 }
180
181 fn write_type_alias(&self, _w: &mut impl Write, _t: &RustTypeAlias) -> anyhow::Result<()> {
182 todo!("type aliases are not supported yet")
183 }
184
185 fn write_struct(&self, w: &mut impl Write, rs: &RustStruct) -> anyhow::Result<()> {
186 let mut indented_writer = IndentedWriter::new(
187 w,
188 self.config.indent.char(),
189 if self.config.namespace_class {
190 self.config.indent.size()
191 } else {
192 0
193 },
194 );
195
196 self.write_multiline_comment(&mut indented_writer, 0, &rs.comments)?;
197
198 write!(
199 indented_writer,
200 "public record {}{}{}(",
201 self.config.prefix.as_ref().unwrap_or(&String::default()),
202 rs.id.renamed,
203 if !rs.generic_types.is_empty() {
204 format!("<{}>", rs.generic_types.join(", "))
205 } else {
206 "".to_string()
207 }
208 )?;
209
210 if let Some((last, elements)) = rs.fields.split_last() {
211 writeln!(indented_writer)?;
212 for f in elements.iter() {
213 self.write_element(&mut indented_writer, f, rs.generic_types.as_slice())?;
214 writeln!(indented_writer, ",")?;
215 }
216 self.write_element(&mut indented_writer, last, rs.generic_types.as_slice())?;
217 writeln!(indented_writer)?;
218 }
219
220 writeln!(indented_writer, r") {{}}")?;
221 writeln!(indented_writer)?;
222
223 Ok(())
224 }
225
226 fn write_enum(&self, w: &mut impl Write, e: &RustEnum) -> anyhow::Result<()> {
227 let mut indented_writer = IndentedWriter::new(
230 w,
231 self.config.indent.char(),
232 if self.config.namespace_class {
233 self.config.indent.size()
234 } else {
235 0
236 },
237 );
238
239 self.write_multiline_comment(&mut indented_writer, 0, &e.shared().comments)?;
240 self.write_annotations(&mut indented_writer, &e.shared().decorators)?;
241
242 match e {
243 RustEnum::Unit {
244 shared,
245 unit_variants,
246 } => self.write_unit_enum(&mut indented_writer, shared, unit_variants)?,
247 RustEnum::Algebraic {
248 shared,
249 tag_key,
250 content_key,
251 variants,
252 } => self.write_algebraic_enum(
253 &mut indented_writer,
254 shared,
255 tag_key,
256 content_key,
257 variants,
258 )?,
259 }
260
261 writeln!(w)?;
262
263 Ok(())
264 }
265
266 fn write_const(&self, _w: &mut impl Write, _c: &RustConst) -> anyhow::Result<()> {
267 todo!("constants are not supported yet")
268 }
269}
270
271impl Java {
272 fn indent<S: AsRef<str>>(&self, line: S, indent: usize) -> String {
273 self.config.indent.indent(line, indent)
274 }
275
276 fn indent_whitespace(&self, indent: usize) -> String {
277 self.indent("", indent)
278 }
279
280 #[inline]
281 fn is_java_letter(&self, c: char) -> bool {
282 c.is_ascii_alphabetic() || c == '_' || c == '$'
284 }
285
286 #[inline]
287 fn is_java_letter_or_number(&self, c: char) -> bool {
288 self.is_java_letter(c) || c.is_ascii_digit()
290 }
291
292 #[inline]
293 fn is_java_reserved_keyword(&self, name: &str) -> bool {
294 matches!(
296 name,
297 "abstract"
298 | "continue"
299 | "for"
300 | "new"
301 | "switch"
302 | "assert"
303 | "default"
304 | "if"
305 | "package"
306 | "synchronized"
307 | "boolean"
308 | "do"
309 | "goto"
310 | "private"
311 | "this"
312 | "break"
313 | "double"
314 | "implements"
315 | "protected"
316 | "throw"
317 | "byte"
318 | "else"
319 | "import"
320 | "public"
321 | "throws"
322 | "case"
323 | "enum"
324 | "instanceof"
325 | "return"
326 | "transient"
327 | "catch"
328 | "extends"
329 | "int"
330 | "short"
331 | "try"
332 | "char"
333 | "final"
334 | "interface"
335 | "static"
336 | "void"
337 | "class"
338 | "finally"
339 | "long"
340 | "strictfp"
341 | "volatile"
342 | "const"
343 | "float"
344 | "native"
345 | "super"
346 | "while"
347 | "_"
348 )
349 }
350
351 #[inline]
352 fn is_java_boolean_literal(&self, name: &str) -> bool {
353 matches!(name, "true" | "false")
355 }
356
357 #[inline]
358 fn is_java_null_literal(&self, name: &str) -> bool {
359 matches!(name, "null")
361 }
362
363 fn santitize_itentifier(&self, name: &str) -> String {
364 let mut chars = name.chars();
366
367 let first_char = chars
369 .next()
370 .map(|c| if self.is_java_letter(c) { c } else { '_' });
371
372 let rest: String = chars
374 .filter_map(|c| match c {
375 '-' => Some('_'),
376 c if self.is_java_letter_or_number(c) => Some(c),
377 _ => None,
378 })
379 .collect();
380
381 let name: String = first_char.into_iter().chain(rest.chars()).collect();
383
384 if self.is_java_reserved_keyword(&name)
385 || self.is_java_boolean_literal(&name)
386 || self.is_java_null_literal(&name)
387 {
388 format!("_{name}")
389 } else {
390 name
391 }
392 }
393
394 fn assert_java_identifier<T: AsRef<str>>(&self, name: T) -> anyhow::Result<T> {
395 let mut chars = name.as_ref().chars();
396
397 match chars.next() {
399 Some(char) if self.is_java_letter(char) => {}
400 Some(char) => {
401 return Err(AssertJavaIdentifierError::InvalidCharacter {
402 name: name.as_ref().to_string(),
403 char,
404 }
405 .into());
406 }
407 None => return Err(AssertJavaIdentifierError::EmptyString.into()),
408 }
409
410 for char in chars {
412 if !self.is_java_letter_or_number(char) {
413 return Err(AssertJavaIdentifierError::InvalidCharacter {
414 name: name.as_ref().to_string(),
415 char,
416 }
417 .into());
418 }
419 }
420
421 Ok(name)
422 }
423
424 fn write_element(
425 &self,
426 w: &mut impl Write,
427 f: &RustField,
428 generic_types: &[TypeName],
429 ) -> anyhow::Result<()> {
430 self.write_multiline_comment(w, 1, &f.comments)?;
431 let ty = self.format_type(&f.ty, generic_types)?;
432 write!(
433 w,
434 "{}{} {}",
435 self.indent_whitespace(1),
436 ty,
437 self.santitize_itentifier(f.id.renamed.as_str()),
438 )
439 .map_err(|err| err.into())
440 }
441
442 fn write_unit_enum(
443 &self,
444 w: &mut impl Write,
445 shared: &RustEnumShared,
446 unit_variants: &[RustEnumVariantShared],
447 ) -> anyhow::Result<()> {
448 if !shared.generic_types.is_empty() {
449 todo!("generic types on unit enums are not supported yet");
450 }
451
452 writeln!(
453 w,
454 "public enum {}{} {{",
455 self.config.prefix.as_ref().unwrap_or(&String::default()),
456 self.santitize_itentifier(shared.id.renamed.as_str()),
457 )?;
458
459 if let Some((last_variant, variants)) = unit_variants.split_last() {
460 for variant in variants {
461 self.write_multiline_comment(w, 1, &variant.comments)?;
462 writeln!(
463 w,
464 "{}{},",
465 self.indent_whitespace(1),
466 self.santitize_itentifier(variant.id.renamed.as_str()),
467 )?;
468 }
469 self.write_multiline_comment(w, 1, &last_variant.comments)?;
470 writeln!(
471 w,
472 "{}{}",
473 self.indent_whitespace(1),
474 self.santitize_itentifier(last_variant.id.renamed.as_str()),
475 )?;
476 }
477
478 writeln!(w, "}}")?;
479
480 Ok(())
481 }
482
483 fn write_algebraic_enum(
484 &self,
485 w: &mut impl Write,
486 shared: &RustEnumShared,
487 tag_key: &str,
488 content_key: &str,
489 variants: &[RustEnumVariant],
490 ) -> anyhow::Result<()> {
491 match self.config.serializer {
492 JavaSerializerOptions::None => {
493 Err(WriteEnumError::SerializerRequiredForAlgebraicEnums {
494 name: shared.id.original.to_string(),
495 }
496 .into())
497 }
498 JavaSerializerOptions::Gson => {
499 self.write_algebraic_enum_gson(w, shared, tag_key, content_key, variants)
500 }
501 }
502 }
503
504 fn write_algebraic_enum_gson(
505 &self,
506 w: &mut impl Write,
507 shared: &RustEnumShared,
508 tag_key: &str,
509 content_key: &str,
510 variants: &[RustEnumVariant],
511 ) -> anyhow::Result<()> {
512 if !shared.generic_types.is_empty() {
513 todo!("generic types on unit enums are not supported yet");
514 }
515
516 let java_enum_identifier = self.santitize_itentifier(&format!(
517 "{}{}",
518 self.config.prefix.as_ref().unwrap_or(&String::default()),
519 shared.id.renamed.as_str(),
520 ));
521
522 let java_enum_adapter_identifier = format!("_{java_enum_identifier}Adapter");
523
524 writeln!(
525 w,
526 "@com.google.gson.annotations.JsonAdapter({java_enum_adapter_identifier}.class)",
527 )?;
528 writeln!(w, "public sealed interface {java_enum_identifier}")?;
529
530 let (variant_last, variants_rest) = variants
531 .split_last()
532 .expect("algebraic enum should have at least one variant");
533
534 writeln!(w, "{}permits", self.indent_whitespace(1))?;
535 for variant in variants_rest {
536 writeln!(
537 w,
538 "{}{}.{},",
539 self.indent_whitespace(2),
540 java_enum_identifier,
541 self.santitize_itentifier(variant.shared().id.renamed.as_str()),
542 )?;
543 }
544 writeln!(
545 w,
546 "{}{}.{} {{",
547 self.indent_whitespace(2),
548 java_enum_identifier,
549 self.santitize_itentifier(variant_last.shared().id.renamed.as_str()),
550 )?;
551 writeln!(w)?;
552
553 let mut indented_writer = self.config.indent.to_indented_writer(w);
554
555 for variant in variants {
556 self.write_multiline_comment(&mut indented_writer, 0, &variant.shared().comments)?;
557
558 match variant {
559 RustEnumVariant::Unit(shared) => self.write_algebraic_enum_unit_variant_gson(
560 &mut indented_writer,
561 shared,
562 &java_enum_identifier,
563 &java_enum_adapter_identifier,
564 )?,
565 RustEnumVariant::Tuple {
566 ty: variant_ty,
567 shared: variant_shared,
568 } => {
569 self.write_algebraic_enum_newtype_tuple_variant_gson(
570 &mut indented_writer,
571 shared,
572 variant_shared,
573 content_key,
574 variant_ty,
575 &java_enum_identifier,
576 &java_enum_adapter_identifier,
577 )?;
578 }
579 RustEnumVariant::AnonymousStruct {
580 fields: variant_fields,
581 shared: variant_shared,
582 } => {
583 self.write_algebraic_enum_anonymous_struct_variant_gson(
584 &mut indented_writer,
585 shared,
586 variant_shared,
587 content_key,
588 variant_fields,
589 &java_enum_identifier,
590 &java_enum_adapter_identifier,
591 )?;
592 }
593 _ => return Err(WriteEnumError::UnsupportedEnumVariant(variant.clone()).into()),
594 }
595
596 writeln!(indented_writer)?;
597 }
598
599 writeln!(w, "}}")?;
600 writeln!(w)?;
601
602 self.write_algebraic_enum_adapter_gson(
603 w,
604 shared,
605 tag_key,
606 content_key,
607 variants,
608 &java_enum_identifier,
609 &java_enum_adapter_identifier,
610 )?;
611
612 Ok(())
613 }
614
615 fn write_algebraic_enum_unit_variant_gson(
616 &self,
617 w: &mut impl Write,
618 variant_shared: &RustEnumVariantShared,
619 java_enum_identifier: &str,
620 java_enum_adapter_identifier: &str,
621 ) -> anyhow::Result<()> {
622 writeln!(
623 w,
624 "@com.google.gson.annotations.JsonAdapter({java_enum_adapter_identifier}.class)",
625 )?;
626 writeln!(
627 w,
628 "public record {}() implements {java_enum_identifier} {{}}",
629 self.santitize_itentifier(variant_shared.id.renamed.as_str()),
630 )?;
631 Ok(())
632 }
633
634 #[allow(clippy::too_many_arguments)]
635 fn write_algebraic_enum_newtype_tuple_variant_gson(
636 &self,
637 w: &mut impl Write,
638 enum_shared: &RustEnumShared,
639 variant_shared: &RustEnumVariantShared,
640 content_key: &str,
641 ty: &RustType,
642 java_enum_identifier: &str,
643 java_enum_adapter_identifier: &str,
644 ) -> anyhow::Result<()> {
645 let ty = self.format_type(ty, enum_shared.generic_types.as_ref())?;
646 writeln!(
647 w,
648 "@com.google.gson.annotations.JsonAdapter({java_enum_adapter_identifier}.class)",
649 )?;
650 writeln!(
651 w,
652 "public record {}({} {}) implements {} {{}}",
653 self.santitize_itentifier(variant_shared.id.renamed.as_str()),
654 ty,
655 self.assert_java_identifier(content_key)?,
656 java_enum_identifier,
657 )?;
658 Ok(())
659 }
660
661 #[allow(clippy::too_many_arguments)]
662 fn write_algebraic_enum_anonymous_struct_variant_gson(
663 &self,
664 _w: &mut impl Write,
665 _enum_shared: &RustEnumShared,
666 _variant_shared: &RustEnumVariantShared,
667 _content_key: &str,
668 _fields: &[RustField],
669 _java_enum_identifier: &str,
670 _java_enum_adapter_identifier: &str,
671 ) -> anyhow::Result<()> {
672 todo!("algebraic enum variants with anonymous structs are not supported yet")
673 }
674
675 #[allow(clippy::too_many_arguments)]
676 fn write_algebraic_enum_adapter_gson(
677 &self,
678 w: &mut impl Write,
679 shared: &RustEnumShared,
680 tag_key: &str,
681 content_key: &str,
682 variants: &[RustEnumVariant],
683 java_enum_identifier: &str,
684 java_enum_adapter_identifier: &str,
685 ) -> anyhow::Result<()> {
686 writeln!(
687 w,
688 "private final class {java_enum_adapter_identifier} extends com.google.gson.TypeAdapter<{java_enum_identifier}> {{",
689 )?;
690 writeln!(w)?;
691 writeln!(
692 w,
693 "{}private static com.google.gson.Gson gson = new com.google.gson.Gson();",
694 self.indent_whitespace(1),
695 )?;
696 writeln!(w)?;
697
698 let mut indented_writer = self.config.indent.to_indented_writer(w);
699
700 self.write_algebraic_enum_adapter_write_method_gson(
701 &mut indented_writer,
702 tag_key,
703 content_key,
704 variants,
705 java_enum_identifier,
706 )?;
707 writeln!(indented_writer)?;
708
709 self.write_algebraic_enum_adapter_read_method_gson(
710 &mut indented_writer,
711 shared,
712 tag_key,
713 content_key,
714 variants,
715 java_enum_identifier,
716 )?;
717 writeln!(indented_writer)?;
718
719 writeln!(w, "}}")?;
720
721 Ok(())
722 }
723
724 fn write_algebraic_enum_adapter_write_method_gson(
725 &self,
726 w: &mut impl Write,
727 tag_key: &str,
728 content_key: &str,
729 variants: &[RustEnumVariant],
730 java_enum_identifier: &str,
731 ) -> anyhow::Result<()> {
732 writeln!(w, "@Override")?;
733 writeln!(w, "public void write(")?;
734 writeln!(
735 w,
736 "{}com.google.gson.stream.JsonWriter out,",
737 self.indent_whitespace(1),
738 )?;
739 writeln!(
740 w,
741 "{}{java_enum_identifier} value",
742 self.indent_whitespace(1),
743 )?;
744 writeln!(w, ") throws java.io.IOException {{")?;
745
746 let mut indented_writer = self.config.indent.to_indented_writer(w);
747
748 for variant in variants {
749 writeln!(
750 indented_writer,
751 "if (value instanceof {java_enum_identifier}.{}) {{",
752 self.santitize_itentifier(variant.shared().id.renamed.as_str()),
753 )?;
754 indented_writer.indent();
755 match variant {
756 RustEnumVariant::Unit(shared) => {
757 writeln!(indented_writer, "out.beginObject();")?;
758 writeln!(indented_writer, "out.name(\"{tag_key}\");")?;
759 writeln!(indented_writer, "out.value(\"{}\");", shared.id.renamed)?;
760 writeln!(indented_writer, "out.endObject();")?;
761 writeln!(indented_writer, "return;")?;
762 }
763 RustEnumVariant::Tuple { shared, .. } => {
764 writeln!(
765 indented_writer,
766 "var content = (({java_enum_identifier}.{}) value).{}();",
767 self.santitize_itentifier(shared.id.renamed.as_str()),
768 self.assert_java_identifier(content_key)?,
769 )?;
770 writeln!(indented_writer, "out.beginObject();")?;
771 writeln!(indented_writer, "out.name(\"{tag_key}\");")?;
772 writeln!(indented_writer, "out.value(\"{}\");", shared.id.renamed)?;
773 writeln!(indented_writer, "out.name(\"{content_key}\");")?;
774 writeln!(
775 indented_writer,
776 "gson.toJson(gson.toJsonTree(content), out);"
777 )?;
778 writeln!(indented_writer, "out.endObject();")?;
779 writeln!(indented_writer, "return;")?;
780 }
781 RustEnumVariant::AnonymousStruct { .. } => {
782 todo!("algebraic enum variants with anonymous structs are not supported yet")
783 }
784 _ => return Err(WriteEnumError::UnsupportedEnumVariant(variant.clone()).into()),
785 }
786 indented_writer.dedent();
787 writeln!(indented_writer, "}}")?;
788 writeln!(indented_writer)?;
789 }
790
791 writeln!(
792 indented_writer,
793 "throw new RuntimeException(\"unreachable!\");"
794 )?;
795 writeln!(w, "}}")?;
796
797 Ok(())
798 }
799
800 fn write_algebraic_enum_adapter_read_method_gson(
801 &self,
802 w: &mut impl Write,
803 enum_shared: &RustEnumShared,
804 tag_key: &str,
805 content_key: &str,
806 variants: &[RustEnumVariant],
807 java_enum_identifier: &str,
808 ) -> anyhow::Result<()> {
809 writeln!(w, "@Override")?;
810 writeln!(w, "public {java_enum_identifier} read(")?;
811 writeln!(
812 w,
813 "{}com.google.gson.stream.JsonReader in",
814 self.indent_whitespace(1),
815 )?;
816 writeln!(w, ") throws java.io.IOException {{")?;
817
818 let mut indented_writer = self.config.indent.to_indented_writer(w);
819
820 writeln!(
821 indented_writer,
822 "JsonObject jsonObject = gson.fromJson(in, JsonObject.class);"
823 )?;
824 writeln!(
825 indented_writer,
826 "JsonElement tagElement = jsonObject.get(\"{tag_key}\");",
827 )?;
828 writeln!(indented_writer)?;
829 writeln!(indented_writer, "if (tagElement == null) {{")?;
830 writeln!(
831 indented_writer,
832 "{}throw new java.io.IOException(\"Missing '{tag_key}' field for {java_enum_identifier}\");",
833 self.indent_whitespace(1),
834 )?;
835 writeln!(indented_writer, "}}")?;
836 writeln!(indented_writer)?;
837 writeln!(indented_writer, "if (!tagElement.isJsonPrimitive()) {{")?;
838 writeln!(
839 indented_writer,
840 "{}throw new java.io.IOException(\"Invalid '{tag_key}' field for {java_enum_identifier}\");",
841 self.indent_whitespace(1),
842 )?;
843 writeln!(indented_writer, "}}")?;
844 writeln!(indented_writer)?;
845 writeln!(indented_writer, "String tag = tagElement.getAsString();")?;
846 writeln!(indented_writer)?;
847 writeln!(indented_writer, "return switch (tag) {{")?;
848
849 indented_writer.indent();
850
851 for variant in variants {
852 match variant {
853 RustEnumVariant::Unit(variant_shared) => {
854 writeln!(
855 indented_writer,
856 "case \"{}\" -> new {java_enum_identifier}.{}();",
857 variant_shared.id.renamed,
858 self.santitize_itentifier(variant_shared.id.renamed.as_str()),
859 )?;
860 }
861 RustEnumVariant::Tuple {
862 ty,
863 shared: variant_shared,
864 } => {
865 writeln!(
866 indented_writer,
867 "case \"{}\" -> {{",
868 variant_shared.id.renamed
869 )?;
870 indented_writer.indent();
871 writeln!(
872 indented_writer,
873 "JsonElement contentElement = jsonObject.get(\"{content_key}\");",
874 )?;
875 match ty {
876 RustType::Special(SpecialRustType::Option(_)) => {}
878 _ => {
880 writeln!(indented_writer, "if (contentElement == null) {{")?;
881 writeln!(
882 indented_writer,
883 "\tthrow new java.io.IOException(\"'{}' variant missing '{}'\");",
884 variant_shared.id.renamed, content_key,
885 )?;
886 writeln!(indented_writer, "}}")?;
887 }
888 }
889 if !enum_shared.generic_types.is_empty() {
890 todo!("algebraic enums with generic type parameters are not yet supported");
895 }
896 let java_ty = self.format_type(ty, &[])?;
897 match ty {
898 RustType::Special(SpecialRustType::Vec(_))
899 | RustType::Special(SpecialRustType::Array(_, _))
900 | RustType::Special(SpecialRustType::Slice(_))
901 | RustType::Special(SpecialRustType::HashMap(_, _)) => {
902 writeln!(
903 indented_writer,
904 "var contentType = new com.google.gson.reflect.TypeToken<{java_ty}>() {{}};"
905 )?;
906 writeln!(
907 indented_writer,
908 "{java_ty} content = gson.fromJson(contentElement, contentType);",
909 )?
910 }
911 RustType::Special(SpecialRustType::Unit) => {
912 todo!("algebraic enum variants with unit values are not supported yet")
913 }
914 _ => writeln!(
915 indented_writer,
916 "{java_ty} content = gson.fromJson(contentElement, {java_ty}.class);",
917 )?,
918 }
919 writeln!(
920 indented_writer,
921 "yield new {java_enum_identifier}.{}(content);",
922 self.santitize_itentifier(variant_shared.id.renamed.as_str()),
923 )?;
924 indented_writer.dedent();
925 writeln!(indented_writer, "}}")?;
926 }
927 RustEnumVariant::AnonymousStruct { .. } => {
928 todo!("algebraic enum variants with anonymous structs are not supported yet")
929 }
930 _ => return Err(WriteEnumError::UnsupportedEnumVariant(variant.clone()).into()),
931 }
932 }
933
934 writeln!(
935 indented_writer,
936 "default -> throw new java.io.IOException(\"Unknown variant: \" + tag);",
937 )?;
938
939 indented_writer.dedent();
940 writeln!(indented_writer, "}};")?;
941 writeln!(w, "}}")?;
942
943 Ok(())
944 }
945
946 fn write_annotations(
947 &self,
948 w: &mut impl Write,
949 decorator_set: &DecoratorSet,
950 ) -> anyhow::Result<()> {
951 for decorator_value in decorator_set.get_all(Self::NAME) {
952 if let decorator::Value::Nested(decorator_set) = decorator_value {
953 let annotations = decorator_set.get_all("annotations");
954 self.write_java_annotations(w, annotations)?;
955 }
956 }
957
958 Ok(())
959 }
960
961 fn write_java_annotations(
962 &self,
963 w: &mut impl Write,
964 annotations: &[decorator::Value],
965 ) -> anyhow::Result<()> {
966 for annotation in annotations {
967 match annotation {
968 decorator::Value::String(annotations) => {
969 for annotation in annotations
970 .split("\n")
971 .map(str::trim)
972 .filter(|str| !str.is_empty())
973 {
974 writeln!(w, "{annotation}")?;
975 }
976 }
977 _ => return Err(WriteDecoratorError::InvalidAnnotation(annotation.clone()).into()),
978 }
979 }
980
981 Ok(())
982 }
983
984 fn write_multiline_comment_line(
985 &self,
986 w: &mut impl Write,
987 indent: usize,
988 comment: &str,
989 ) -> std::io::Result<()> {
990 writeln!(w, "{} * {}", self.indent_whitespace(indent), comment,)?;
991 Ok(())
992 }
993
994 fn write_multiline_comment(
995 &self,
996 w: &mut impl Write,
997 indent: usize,
998 comment_lines: &[String],
999 ) -> std::io::Result<()> {
1000 if comment_lines.is_empty() {
1001 return Ok(());
1002 }
1003
1004 writeln!(w, "{}/**", self.indent_whitespace(indent))?;
1005 comment_lines
1006 .iter()
1007 .try_for_each(|comment| self.write_multiline_comment_line(w, indent, comment))?;
1008 writeln!(w, "{} */", self.indent_whitespace(indent))?;
1009
1010 Ok(())
1011 }
1012}