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::error::FormatSpecialTypeError;
12use crate::error::WriteDecoratorError;
13use crate::util::indented_writer::IndentedWriter;
14
15#[derive(Debug)]
16pub struct Java {
17 config: JavaConfig,
18}
19
20impl Language<'_> for Java {
21 type Config = JavaConfig;
22
23 const NAME: &'static str = "java";
24
25 fn new_from_config(config: Self::Config) -> anyhow::Result<Self> {
26 Ok(Self { config })
27 }
28
29 fn output_filename_for_crate(&self, crate_name: &CrateName) -> String {
30 crate_name.as_str().to_case(Case::Pascal) + ".java"
31 }
32
33 fn format_special_type(
34 &self,
35 special_ty: &SpecialRustType,
36 generic_context: &[TypeName],
37 ) -> anyhow::Result<String> {
38 Ok(match special_ty {
39 SpecialRustType::Vec(rtype) => {
40 format!(
41 "java.util.ArrayList<{}>",
42 self.format_type(rtype, generic_context)?
43 )
44 }
45 SpecialRustType::Array(rtype, _) => {
46 format!("{}[]", self.format_type(rtype, generic_context)?)
47 }
48 SpecialRustType::Slice(rtype) => {
49 format!("{}[]", self.format_type(rtype, generic_context)?)
50 }
51 SpecialRustType::HashMap(rtype1, rtype2) => {
52 format!(
53 "java.util.HashMap<{}, {}>",
54 self.format_type(rtype1, generic_context)?,
55 self.format_type(rtype2, generic_context)?
56 )
57 }
58 SpecialRustType::Option(rtype) => self.format_type(rtype, generic_context)?,
59 SpecialRustType::Unit => "Void".into(),
60 SpecialRustType::String | SpecialRustType::Char => "String".into(),
63 SpecialRustType::I8 => "byte".into(),
64 SpecialRustType::I16 => "short".into(),
65 SpecialRustType::ISize | SpecialRustType::I32 => "int".into(),
66 SpecialRustType::I54 | SpecialRustType::I64 => "long".into(),
67 SpecialRustType::U8 => "short".into(),
69 SpecialRustType::U16 => "int".into(),
71 SpecialRustType::USize | SpecialRustType::U32 => "long".into(),
73 SpecialRustType::U53 | SpecialRustType::U64 => "java.math.BigInteger".into(),
75 SpecialRustType::Bool => "boolean".into(),
77 SpecialRustType::F32 => "float".into(),
79 SpecialRustType::F64 => "double".into(),
80 _ => {
81 return Err(
82 FormatSpecialTypeError::UnsupportedSpecialType(special_ty.clone()).into(),
83 );
84 }
85 })
86 }
87
88 fn begin_file(&self, w: &mut impl Write, mode: FilesMode<&CrateName>) -> anyhow::Result<()> {
89 match &self.config.header_comment {
90 HeaderComment::None => {}
91 HeaderComment::Default => {
92 writeln!(w, "/**")?;
93 writeln!(
94 w,
95 " * Generated by typeshare-java {}",
96 env!("CARGO_PKG_VERSION")
97 )?;
98 writeln!(w, " */")?;
99 writeln!(w)?;
100 }
101 HeaderComment::Custom { comment } => {
102 writeln!(w, "/**")?;
103 for comment in comment.split("\n") {
104 writeln!(w, " * {comment}")?;
105 }
106 writeln!(w, " */")?;
107 writeln!(w)?;
108 }
109 }
110
111 if let Some(package) = &self.config.package {
112 if let FilesMode::Multi(crate_name) = mode {
113 writeln!(
114 w,
115 "package {}.{};",
116 package,
117 crate_name.as_str().to_case(Case::Pascal)
118 )?;
119 } else {
120 writeln!(w, "package {package};")?;
121 }
122 writeln!(w)?;
123 }
124
125 match (self.config.namespace_class, mode) {
126 (true, FilesMode::Multi(crate_name)) => {
127 writeln!(
128 w,
129 "public class {} {{",
130 crate_name.as_str().to_case(Case::Pascal),
131 )?;
132 writeln!(w)?;
133 }
134 (true, FilesMode::Single) => {
135 writeln!(w, "public class Namespace {{",)?;
136 writeln!(w)?;
137 }
138 _ => {}
139 }
140
141 Ok(())
142 }
143
144 fn write_imports<'a, Crates, Types>(
145 &self,
146 writer: &mut impl Write,
147 _crate_name: &CrateName,
148 imports: Crates,
149 ) -> anyhow::Result<()>
150 where
151 Crates: IntoIterator<Item = (&'a CrateName, Types)>,
152 Types: IntoIterator<Item = &'a TypeName>,
153 {
154 for (path, ty) in imports {
155 for t in ty {
156 writeln!(
157 writer,
158 "import {}.{path}.{t};",
159 self.config
160 .package
161 .as_ref()
162 .map(|package| format!("{package}."))
163 .unwrap_or_default()
164 )?;
165 }
166 }
167 writeln!(writer).map_err(|err| err.into())
168 }
169
170 fn end_file(&self, w: &mut impl Write, _mode: FilesMode<&CrateName>) -> anyhow::Result<()> {
171 if self.config.namespace_class {
172 writeln!(w, "}}")?;
173 }
174
175 Ok(())
176 }
177
178 fn write_type_alias(&self, _w: &mut impl Write, _t: &RustTypeAlias) -> anyhow::Result<()> {
179 todo!("type aliases are not supported yet")
180 }
181
182 fn write_struct(&self, w: &mut impl Write, rs: &RustStruct) -> anyhow::Result<()> {
183 let mut indented_writer =
184 IndentedWriter::new(w, if self.config.namespace_class { 1 } else { 0 });
185
186 self.write_comments(&mut indented_writer, 0, &rs.comments)?;
187
188 write!(
189 indented_writer,
190 "public record {}{}{}(",
191 self.config.prefix.as_ref().unwrap_or(&String::default()),
192 rs.id.renamed,
193 if !rs.generic_types.is_empty() {
194 format!("<{}>", rs.generic_types.join(", "))
195 } else {
196 "".to_string()
197 }
198 )?;
199
200 if let Some((last, elements)) = rs.fields.split_last() {
201 writeln!(indented_writer)?;
202 for f in elements.iter() {
203 self.write_element(&mut indented_writer, f, rs.generic_types.as_slice())?;
204 writeln!(indented_writer, ",")?;
205 }
206 self.write_element(&mut indented_writer, last, rs.generic_types.as_slice())?;
207 writeln!(indented_writer)?;
208 }
209
210 writeln!(indented_writer, r") {{}}")?;
211 writeln!(indented_writer)?;
212
213 Ok(())
214 }
215
216 fn write_enum(&self, w: &mut impl Write, e: &RustEnum) -> anyhow::Result<()> {
217 let mut indented_writer =
220 IndentedWriter::new(w, if self.config.namespace_class { 1 } else { 0 });
221
222 self.write_comments(&mut indented_writer, 0, &e.shared().comments)?;
223
224 match e {
225 RustEnum::Unit {
226 shared,
227 unit_variants,
228 } => self.write_unit_enum(&mut indented_writer, shared, unit_variants)?,
229 RustEnum::Algebraic { .. } => todo!("algebraic enums are not supported yet"),
230 }
231
232 writeln!(w)?;
233
234 Ok(())
235 }
236
237 fn write_const(&self, _w: &mut impl Write, _c: &RustConst) -> anyhow::Result<()> {
238 todo!("constants are not supported yet")
239 }
240}
241
242impl Java {
243 #[inline]
244 fn is_java_letter(&self, c: char) -> bool {
245 c.is_ascii_alphabetic() || c == '_' || c == '$'
247 }
248
249 #[inline]
250 fn is_java_letter_or_number(&self, c: char) -> bool {
251 self.is_java_letter(c) || c.is_ascii_digit()
253 }
254
255 #[inline]
256 fn is_java_reserved_keyword(&self, name: &str) -> bool {
257 matches!(
259 name,
260 "abstract"
261 | "continue"
262 | "for"
263 | "new"
264 | "switch"
265 | "assert"
266 | "default"
267 | "if"
268 | "package"
269 | "synchronized"
270 | "boolean"
271 | "do"
272 | "goto"
273 | "private"
274 | "this"
275 | "break"
276 | "double"
277 | "implements"
278 | "protected"
279 | "throw"
280 | "byte"
281 | "else"
282 | "import"
283 | "public"
284 | "throws"
285 | "case"
286 | "enum"
287 | "instanceof"
288 | "return"
289 | "transient"
290 | "catch"
291 | "extends"
292 | "int"
293 | "short"
294 | "try"
295 | "char"
296 | "final"
297 | "interface"
298 | "static"
299 | "void"
300 | "class"
301 | "finally"
302 | "long"
303 | "strictfp"
304 | "volatile"
305 | "const"
306 | "float"
307 | "native"
308 | "super"
309 | "while"
310 | "_"
311 )
312 }
313
314 #[inline]
315 fn is_java_boolean_literal(&self, name: &str) -> bool {
316 matches!(name, "true" | "false")
318 }
319
320 #[inline]
321 fn is_java_null_literal(&self, name: &str) -> bool {
322 matches!(name, "null")
324 }
325
326 fn santitize_itentifier(&self, name: &str) -> String {
327 let mut chars = name.chars();
329
330 let first_char = chars
332 .next()
333 .map(|c| if self.is_java_letter(c) { c } else { '_' });
334
335 let rest: String = chars
337 .filter_map(|c| match c {
338 '-' => Some('_'),
339 c if self.is_java_letter_or_number(c) => Some(c),
340 _ => None,
341 })
342 .collect();
343
344 let name: String = first_char.into_iter().chain(rest.chars()).collect();
346
347 if self.is_java_reserved_keyword(&name)
348 || self.is_java_boolean_literal(&name)
349 || self.is_java_null_literal(&name)
350 {
351 format!("_{name}")
352 } else {
353 name
354 }
355 }
356
357 fn write_element(
358 &self,
359 w: &mut impl Write,
360 f: &RustField,
361 generic_types: &[TypeName],
362 ) -> anyhow::Result<()> {
363 self.write_comments(w, 1, &f.comments)?;
364 let ty = self.format_type(&f.ty, generic_types)?;
365 write!(
366 w,
367 "\t{} {}",
368 ty,
369 self.santitize_itentifier(f.id.renamed.as_str()),
370 )
371 .map_err(|err| err.into())
372 }
373
374 fn write_unit_enum(
375 &self,
376 w: &mut impl Write,
377 shared: &RustEnumShared,
378 unit_variants: &[RustEnumVariantShared],
379 ) -> anyhow::Result<()> {
380 self.write_annotations(w, &shared.decorators)?;
381
382 writeln!(
383 w,
384 "public enum {}{} {{",
385 self.config.prefix.as_ref().unwrap_or(&String::default()),
386 &shared.id.renamed
387 )?;
388
389 if let Some((last_variant, variants)) = unit_variants.split_last() {
390 for variant in variants {
391 self.write_comments(w, 1, &variant.comments)?;
392 writeln!(
393 w,
394 "\t{},",
395 self.santitize_itentifier(variant.id.renamed.as_str()),
396 )?;
397 }
398 self.write_comments(w, 1, &last_variant.comments)?;
399 writeln!(
400 w,
401 "\t{}",
402 self.santitize_itentifier(last_variant.id.renamed.as_str()),
403 )?;
404 }
405
406 writeln!(w, "}}")?;
407
408 Ok(())
409 }
410
411 fn write_annotations(
412 &self,
413 w: &mut impl Write,
414 decorator_set: &DecoratorSet,
415 ) -> anyhow::Result<()> {
416 for decorator_value in decorator_set.get_all(Self::NAME) {
417 if let decorator::Value::Nested(decorator_set) = decorator_value {
418 let annotations = decorator_set.get_all("annotations");
419 self.write_java_annotations(w, annotations)?;
420 }
421 }
422
423 Ok(())
424 }
425
426 fn write_java_annotations(
427 &self,
428 w: &mut impl Write,
429 annotations: &[decorator::Value],
430 ) -> anyhow::Result<()> {
431 for annotation in annotations {
432 match annotation {
433 decorator::Value::String(annotations) => {
434 for annotation in annotations
435 .split("\n")
436 .map(str::trim)
437 .filter(|str| !str.is_empty())
438 {
439 writeln!(w, "{annotation}")?;
440 }
441 }
442 _ => return Err(WriteDecoratorError::InvalidAnnotation(annotation.clone()).into()),
443 }
444 }
445
446 Ok(())
447 }
448
449 fn write_comment(
450 &self,
451 w: &mut impl Write,
452 indent: usize,
453 comment: &str,
454 ) -> std::io::Result<()> {
455 writeln!(w, "{}/// {}", "\t".repeat(indent), comment)?;
456 Ok(())
457 }
458
459 fn write_comments(
460 &self,
461 w: &mut impl Write,
462 indent: usize,
463 comments: &[String],
464 ) -> std::io::Result<()> {
465 comments
466 .iter()
467 .try_for_each(|comment| self.write_comment(w, indent, comment))
468 }
469}