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