typeshare_java/
language.rs

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            // https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-IntegralType
64            // Char in Java is 16 bits long, so we need to use String
65            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            // byte in Java is signed, so we need to use short to represent all possible values
71            SpecialRustType::U8 => "Short".into(),
72            // short in Java is signed, so we need to use int to represent all possible values
73            SpecialRustType::U16 => "Integer".into(),
74            // ing in Java is signed, so we need to use long to represent all possible values
75            SpecialRustType::USize | SpecialRustType::U32 => "Long".into(),
76            // long in Java is signed, so we need to use BigInteger to represent all possible values
77            SpecialRustType::U53 | SpecialRustType::U64 => "java.math.BigInteger".into(),
78            // https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-PrimitiveType
79            SpecialRustType::Bool => "Boolean".into(),
80            // https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-FloatingPointType
81            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        // TODO: Generate named types for any anonymous struct variants of this enum
228
229        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        // https://docs.oracle.com/javase/specs/jls/se23/html/jls-3.html#jls-JavaLetter
283        c.is_ascii_alphabetic() || c == '_' || c == '$'
284    }
285
286    #[inline]
287    fn is_java_letter_or_number(&self, c: char) -> bool {
288        // https://docs.oracle.com/javase/specs/jls/se23/html/jls-3.html#jls-JavaLetterOrDigit
289        self.is_java_letter(c) || c.is_ascii_digit()
290    }
291
292    #[inline]
293    fn is_java_reserved_keyword(&self, name: &str) -> bool {
294        // https://docs.oracle.com/javase/specs/jls/se23/html/jls-3.html#jls-ReservedKeyword
295        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        // https://docs.oracle.com/javase/specs/jls/se23/html/jls-3.html#jls-BooleanLiteral
354        matches!(name, "true" | "false")
355    }
356
357    #[inline]
358    fn is_java_null_literal(&self, name: &str) -> bool {
359        // https://docs.oracle.com/javase/specs/jls/se23/html/jls-3.html#jls-NullLiteral
360        matches!(name, "null")
361    }
362
363    fn santitize_itentifier(&self, name: &str) -> String {
364        // https://docs.oracle.com/javase/specs/jls/se23/html/jls-3.html#jls-Identifier
365        let mut chars = name.chars();
366
367        // Ensure the first character is valid "JavaLetter"
368        let first_char = chars
369            .next()
370            .map(|c| if self.is_java_letter(c) { c } else { '_' });
371
372        // Ensure each remaining characters is a valid "JavaLetterOrDigit"
373        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        // Combine and return the sanitized identifier
382        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        // Ensure the first character is valid "JavaLetter"
398        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        // Ensure each remaining characters is a valid "JavaLetterOrDigit"
411        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                        // For Option types, there's no need to check for null values
877                        RustType::Special(SpecialRustType::Option(_)) => {}
878                        // For all other types, ensure the content is not null (or missing)
879                        _ => {
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                        // EXPLANATION: The Gson TypeToken<T> constructor cannot accept types T<...K> where T is generic
891                        //              over types ...K. Such types can be represented using TypeToken.getParametarized(T.class, K.class, ...)
892                        //              but this requires some extra logic to be implemented here.
893                        // REFERENCE: https://github.com/google/gson/blob/259c477cecaea8e73cd19e5207ba63edc04157da/gson/src/main/java/com/google/gson/reflect/TypeToken.java#L40-L44
894                        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}