1use crate::type_map::{java_boxed_type, java_ffi_type, java_type};
2use alef_codegen::naming::{to_class_name, to_java_name};
3use alef_core::backend::{Backend, BuildConfig, Capabilities, GeneratedFile};
4use alef_core::config::{AlefConfig, Language, resolve_output_dir};
5use alef_core::ir::{ApiSurface, EnumDef, FunctionDef, PrimitiveType, TypeDef, TypeRef};
6use heck::ToSnakeCase;
7use std::fmt::Write;
8use std::path::PathBuf;
9
10pub struct JavaBackend;
11
12impl JavaBackend {
13 fn resolve_main_class(api: &ApiSurface) -> String {
15 to_class_name(&api.crate_name.replace('-', "_"))
16 }
17}
18
19impl Backend for JavaBackend {
20 fn name(&self) -> &str {
21 "java"
22 }
23
24 fn language(&self) -> Language {
25 Language::Java
26 }
27
28 fn capabilities(&self) -> Capabilities {
29 Capabilities {
30 supports_async: true,
31 supports_classes: true,
32 supports_enums: true,
33 supports_option: true,
34 supports_result: true,
35 ..Capabilities::default()
36 }
37 }
38
39 fn generate_bindings(&self, api: &ApiSurface, config: &AlefConfig) -> anyhow::Result<Vec<GeneratedFile>> {
40 let package = config.java_package();
41 let prefix = config.ffi_prefix();
42 let main_class = Self::resolve_main_class(api);
43 let package_path = package.replace('.', "/");
44
45 let output_dir = resolve_output_dir(
46 config.output.java.as_ref(),
47 &config.crate_config.name,
48 "packages/java/src/main/java/",
49 );
50
51 let base_path = PathBuf::from(&output_dir).join(&package_path);
52
53 let mut files = Vec::new();
54
55 files.push(GeneratedFile {
57 path: base_path.join("NativeLib.java"),
58 content: gen_native_lib(api, config, &package, &prefix),
59 generated_header: true,
60 });
61
62 files.push(GeneratedFile {
64 path: base_path.join(format!("{}.java", main_class)),
65 content: gen_main_class(api, config, &package, &main_class, &prefix),
66 generated_header: true,
67 });
68
69 files.push(GeneratedFile {
71 path: base_path.join(format!("{}Exception.java", main_class)),
72 content: gen_exception_class(&package, &main_class),
73 generated_header: true,
74 });
75
76 for typ in &api.types {
78 if !typ.is_opaque && !typ.fields.is_empty() {
79 files.push(GeneratedFile {
80 path: base_path.join(format!("{}.java", typ.name)),
81 content: gen_record_type(&package, typ),
82 generated_header: true,
83 });
84 if typ.has_default {
86 files.push(GeneratedFile {
87 path: base_path.join(format!("{}Builder.java", typ.name)),
88 content: gen_builder_class(&package, typ),
89 generated_header: true,
90 });
91 }
92 }
93 }
94
95 for enum_def in &api.enums {
97 files.push(GeneratedFile {
98 path: base_path.join(format!("{}.java", enum_def.name)),
99 content: gen_enum_class(&package, enum_def),
100 generated_header: true,
101 });
102 }
103
104 for error in &api.errors {
106 for (class_name, content) in alef_codegen::error_gen::gen_java_error_types(error, &package) {
107 files.push(GeneratedFile {
108 path: base_path.join(format!("{}.java", class_name)),
109 content,
110 generated_header: true,
111 });
112 }
113 }
114
115 let _adapter_bodies = alef_adapters::build_adapter_bodies(config, Language::Java)?;
117
118 Ok(files)
119 }
120
121 fn generate_public_api(&self, api: &ApiSurface, config: &AlefConfig) -> anyhow::Result<Vec<GeneratedFile>> {
122 let package = config.java_package();
123 let prefix = config.ffi_prefix();
124 let main_class = Self::resolve_main_class(api);
125 let package_path = package.replace('.', "/");
126
127 let output_dir = resolve_output_dir(
128 config.output.java.as_ref(),
129 &config.crate_config.name,
130 "packages/java/src/main/java/",
131 );
132
133 let base_path = PathBuf::from(&output_dir).join(&package_path);
134
135 let public_class = main_class.trim_end_matches("Rs").to_string();
138 let facade_content = gen_facade_class(api, &package, &public_class, &main_class, &prefix);
139
140 Ok(vec![GeneratedFile {
141 path: base_path.join(format!("{}.java", public_class)),
142 content: facade_content,
143 generated_header: true,
144 }])
145 }
146
147 fn build_config(&self) -> Option<BuildConfig> {
148 Some(BuildConfig {
149 tool: "mvn",
150 crate_suffix: "",
151 depends_on_ffi: true,
152 post_build: vec![],
153 })
154 }
155}
156
157fn gen_native_lib(api: &ApiSurface, config: &AlefConfig, package: &str, prefix: &str) -> String {
162 let mut body = String::with_capacity(2048);
164 let lib_name = config.ffi_lib_name();
167
168 writeln!(body, "final class NativeLib {{").ok();
169 writeln!(body, " private static final Linker LINKER = Linker.nativeLinker();").ok();
170 writeln!(body, " private static final SymbolLookup LIB;").ok();
171 writeln!(body).ok();
172 writeln!(body, " static {{").ok();
173 writeln!(body, " System.loadLibrary(\"{}\");", lib_name).ok();
174 writeln!(body, " LIB = SymbolLookup.loaderLookup();").ok();
175 writeln!(body, " }}").ok();
176 writeln!(body).ok();
177
178 for func in &api.functions {
180 if !func.is_async {
181 let ffi_name = format!("{}_{}", prefix, func.name.to_lowercase());
182 let return_layout = gen_ffi_layout(&func.return_type);
183 let param_layouts: Vec<String> = func.params.iter().map(|p| gen_ffi_layout(&p.ty)).collect();
184
185 let layout_str = gen_function_descriptor(&return_layout, ¶m_layouts);
186
187 let handle_name = format!("{}_{}", prefix.to_uppercase(), func.name.to_uppercase());
188
189 writeln!(
190 body,
191 " static final MethodHandle {} = LINKER.downcallHandle(",
192 handle_name
193 )
194 .ok();
195 writeln!(body, " LIB.find(\"{}\").orElseThrow(),", ffi_name).ok();
196 writeln!(body, " {}", layout_str).ok();
197 writeln!(body, " );").ok();
198 }
199 }
200
201 {
203 let free_name = format!("{}_free_string", prefix);
204 let handle_name = format!("{}_FREE_STRING", prefix.to_uppercase());
205 writeln!(body).ok();
206 writeln!(
207 body,
208 " static final MethodHandle {} = LINKER.downcallHandle(",
209 handle_name
210 )
211 .ok();
212 writeln!(body, " LIB.find(\"{}\").orElseThrow(),", free_name).ok();
213 writeln!(body, " FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)").ok();
214 writeln!(body, " );").ok();
215 }
216
217 {
219 writeln!(
220 body,
221 " static final MethodHandle {}_LAST_ERROR_CODE = LINKER.downcallHandle(",
222 prefix.to_uppercase()
223 )
224 .ok();
225 writeln!(body, " LIB.find(\"{}_last_error_code\").orElseThrow(),", prefix).ok();
226 writeln!(body, " FunctionDescriptor.of(ValueLayout.JAVA_INT)").ok();
227 writeln!(body, " );").ok();
228
229 writeln!(
230 body,
231 " static final MethodHandle {}_LAST_ERROR_CONTEXT = LINKER.downcallHandle(",
232 prefix.to_uppercase()
233 )
234 .ok();
235 writeln!(
236 body,
237 " LIB.find(\"{}_last_error_context\").orElseThrow(),",
238 prefix
239 )
240 .ok();
241 writeln!(body, " FunctionDescriptor.of(ValueLayout.ADDRESS)").ok();
242 writeln!(body, " );").ok();
243 }
244
245 for func in &api.functions {
247 if let TypeRef::Named(name) = &func.return_type {
248 let type_snake = name.to_snake_case();
249 let type_upper = type_snake.to_uppercase();
250
251 let content_handle = format!("{}_{}_CONTENT", prefix.to_uppercase(), type_upper);
253 let content_ffi = format!("{}_{}_content", prefix, type_snake);
254 writeln!(body).ok();
255 writeln!(
256 body,
257 " static final MethodHandle {} = LINKER.downcallHandle(",
258 content_handle
259 )
260 .ok();
261 writeln!(body, " LIB.find(\"{}\").orElseThrow(),", content_ffi).ok();
262 writeln!(
263 body,
264 " FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS)"
265 )
266 .ok();
267 writeln!(body, " );").ok();
268
269 let free_handle = format!("{}_{}_FREE", prefix.to_uppercase(), type_upper);
271 let free_ffi = format!("{}_{}_free", prefix, type_snake);
272 writeln!(
273 body,
274 " static final MethodHandle {} = LINKER.downcallHandle(",
275 free_handle
276 )
277 .ok();
278 writeln!(body, " LIB.find(\"{}\").orElseThrow(),", free_ffi).ok();
279 writeln!(body, " FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)").ok();
280 writeln!(body, " );").ok();
281 }
282 }
283
284 writeln!(body, "}}").ok();
285
286 let mut out = String::with_capacity(body.len() + 512);
288
289 writeln!(out, "// DO NOT EDIT - auto-generated by alef").ok();
290 writeln!(out, "package {};", package).ok();
291 writeln!(out).ok();
292 if body.contains("Arena") {
293 writeln!(out, "import java.lang.foreign.Arena;").ok();
294 }
295 if body.contains("FunctionDescriptor") {
296 writeln!(out, "import java.lang.foreign.FunctionDescriptor;").ok();
297 }
298 if body.contains("Linker") {
299 writeln!(out, "import java.lang.foreign.Linker;").ok();
300 }
301 if body.contains("MemorySegment") {
302 writeln!(out, "import java.lang.foreign.MemorySegment;").ok();
303 }
304 if body.contains("SymbolLookup") {
305 writeln!(out, "import java.lang.foreign.SymbolLookup;").ok();
306 }
307 if body.contains("ValueLayout") {
308 writeln!(out, "import java.lang.foreign.ValueLayout;").ok();
309 }
310 if body.contains("MethodHandle") {
311 writeln!(out, "import java.lang.invoke.MethodHandle;").ok();
312 }
313 writeln!(out).ok();
314
315 out.push_str(&body);
316
317 out
318}
319
320fn gen_main_class(api: &ApiSurface, _config: &AlefConfig, package: &str, class_name: &str, prefix: &str) -> String {
325 let mut body = String::with_capacity(4096);
327
328 writeln!(body, "public final class {} {{", class_name).ok();
329 writeln!(body, " private {}() {{ }}", class_name).ok();
330 writeln!(body).ok();
331
332 for func in &api.functions {
334 gen_sync_function_method(&mut body, func, prefix, class_name);
336 writeln!(body).ok();
337
338 if func.is_async {
340 gen_async_wrapper_method(&mut body, func);
341 writeln!(body).ok();
342 }
343 }
344
345 gen_helper_methods(&mut body);
347
348 writeln!(body, "}}").ok();
349
350 let mut out = String::with_capacity(body.len() + 512);
352
353 writeln!(out, "// DO NOT EDIT - auto-generated by alef").ok();
354 writeln!(out, "package {};", package).ok();
355 writeln!(out).ok();
356 if body.contains("Arena") {
357 writeln!(out, "import java.lang.foreign.Arena;").ok();
358 }
359 if body.contains("FunctionDescriptor") {
360 writeln!(out, "import java.lang.foreign.FunctionDescriptor;").ok();
361 }
362 if body.contains("Linker") {
363 writeln!(out, "import java.lang.foreign.Linker;").ok();
364 }
365 if body.contains("MemorySegment") {
366 writeln!(out, "import java.lang.foreign.MemorySegment;").ok();
367 }
368 if body.contains("SymbolLookup") {
369 writeln!(out, "import java.lang.foreign.SymbolLookup;").ok();
370 }
371 if body.contains("ValueLayout") {
372 writeln!(out, "import java.lang.foreign.ValueLayout;").ok();
373 }
374 if body.contains("List<") {
375 writeln!(out, "import java.util.List;").ok();
376 }
377 if body.contains("Map<") {
378 writeln!(out, "import java.util.Map;").ok();
379 }
380 if body.contains("Optional<") {
381 writeln!(out, "import java.util.Optional;").ok();
382 }
383 if body.contains("HashMap<") || body.contains("new HashMap") {
384 writeln!(out, "import java.util.HashMap;").ok();
385 }
386 if body.contains("CompletableFuture") {
387 writeln!(out, "import java.util.concurrent.CompletableFuture;").ok();
388 }
389 if body.contains("CompletionException") {
390 writeln!(out, "import java.util.concurrent.CompletionException;").ok();
391 }
392 if body.contains("ObjectMapper") || body.contains("readValue") {
393 writeln!(out, "import com.fasterxml.jackson.databind.ObjectMapper;").ok();
394 }
395 writeln!(out).ok();
396
397 out.push_str(&body);
398
399 out
400}
401
402fn gen_sync_function_method(out: &mut String, func: &FunctionDef, prefix: &str, class_name: &str) {
403 let params: Vec<String> = func
404 .params
405 .iter()
406 .map(|p| {
407 let ptype = java_type(&p.ty);
408 format!("{} {}", ptype, to_java_name(&p.name))
409 })
410 .collect();
411
412 let return_type = java_type(&func.return_type);
413
414 writeln!(
415 out,
416 " public static {} {}({}) throws {}Exception {{",
417 return_type,
418 to_java_name(&func.name),
419 params.join(", "),
420 class_name
421 )
422 .ok();
423
424 writeln!(out, " try (var arena = Arena.ofConfined()) {{").ok();
425
426 for param in &func.params {
428 marshal_param_to_ffi(out, &to_java_name(¶m.name), ¶m.ty);
429 }
430
431 let ffi_handle = format!("NativeLib.{}_{}", prefix.to_uppercase(), func.name.to_uppercase());
433
434 let call_args: Vec<String> = func
435 .params
436 .iter()
437 .map(|p| ffi_param_name(&to_java_name(&p.name), &p.ty))
438 .collect();
439
440 if matches!(func.return_type, TypeRef::Unit) {
441 writeln!(out, " {}.invoke({});", ffi_handle, call_args.join(", ")).ok();
442 writeln!(out, " }} catch (Throwable e) {{").ok();
443 writeln!(
444 out,
445 " throw new {}Exception(\"FFI call failed\", e);",
446 class_name
447 )
448 .ok();
449 writeln!(out, " }}").ok();
450 } else if is_ffi_string_return(&func.return_type) {
451 let free_handle = format!("NativeLib.{}_FREE_STRING", prefix.to_uppercase());
452 writeln!(
453 out,
454 " var resultPtr = (MemorySegment) {}.invoke({});",
455 ffi_handle,
456 call_args.join(", ")
457 )
458 .ok();
459 writeln!(out, " if (resultPtr.equals(MemorySegment.NULL)) {{").ok();
460 writeln!(out, " return null;").ok();
461 writeln!(out, " }}").ok();
462 writeln!(
463 out,
464 " String result = resultPtr.reinterpret(Long.MAX_VALUE).getString(0);"
465 )
466 .ok();
467 writeln!(out, " {}.invoke(resultPtr);", free_handle).ok();
468 writeln!(out, " return result;").ok();
469 writeln!(out, " }} catch (Throwable e) {{").ok();
470 writeln!(
471 out,
472 " throw new {}Exception(\"FFI call failed\", e);",
473 class_name
474 )
475 .ok();
476 writeln!(out, " }}").ok();
477 } else if matches!(func.return_type, TypeRef::Named(_)) {
478 let return_type_name = match &func.return_type {
480 TypeRef::Named(name) => name,
481 _ => unreachable!(),
482 };
483 let type_snake = return_type_name.to_snake_case();
484 let free_handle = format!("NativeLib.{}_{}_FREE", prefix.to_uppercase(), type_snake.to_uppercase());
485 let content_handle = format!(
486 "NativeLib.{}_{}_CONTENT",
487 prefix.to_uppercase(),
488 type_snake.to_uppercase()
489 );
490 writeln!(
491 out,
492 " var resultPtr = (MemorySegment) {}.invoke({});",
493 ffi_handle,
494 call_args.join(", ")
495 )
496 .ok();
497 writeln!(out, " if (resultPtr.equals(MemorySegment.NULL)) {{").ok();
498 writeln!(out, " return null;").ok();
499 writeln!(out, " }}").ok();
500 writeln!(
502 out,
503 " var contentPtr = (MemorySegment) {}.invoke(resultPtr);",
504 content_handle
505 )
506 .ok();
507 writeln!(
508 out,
509 " String content = contentPtr.equals(MemorySegment.NULL) ? null :"
510 )
511 .ok();
512 writeln!(
513 out,
514 " contentPtr.reinterpret(Long.MAX_VALUE).getString(0);"
515 )
516 .ok();
517 writeln!(out, " {}.invoke(resultPtr);", free_handle).ok();
518 writeln!(out, " return new {}(", return_type_name).ok();
520 writeln!(out, " java.util.Optional.ofNullable(content),").ok();
521 writeln!(out, " java.util.Optional.empty(),").ok();
522 writeln!(out, " java.util.List.of(),").ok();
523 writeln!(out, " java.util.List.of()").ok();
524 writeln!(out, " );").ok();
525 writeln!(out, " }} catch (Throwable e) {{").ok();
526 writeln!(
527 out,
528 " throw new {}Exception(\"FFI call failed\", e);",
529 class_name
530 )
531 .ok();
532 writeln!(out, " }}").ok();
533 } else {
534 writeln!(
535 out,
536 " return ({}) {}.invoke({});",
537 java_ffi_return_cast(&func.return_type),
538 ffi_handle,
539 call_args.join(", ")
540 )
541 .ok();
542 writeln!(out, " }} catch (Throwable e) {{").ok();
543 writeln!(
544 out,
545 " throw new {}Exception(\"FFI call failed\", e);",
546 class_name
547 )
548 .ok();
549 writeln!(out, " }}").ok();
550 }
551
552 writeln!(out, " }}").ok();
553}
554
555fn gen_async_wrapper_method(out: &mut String, func: &FunctionDef) {
556 let params: Vec<String> = func
557 .params
558 .iter()
559 .map(|p| {
560 let ptype = java_type(&p.ty);
561 format!("{} {}", ptype, to_java_name(&p.name))
562 })
563 .collect();
564
565 let return_type = match &func.return_type {
566 TypeRef::Unit => "Void".to_string(),
567 other => java_boxed_type(other).to_string(),
568 };
569
570 let sync_method_name = to_java_name(&func.name);
571 let async_method_name = format!("{}Async", sync_method_name);
572 let param_names: Vec<String> = func.params.iter().map(|p| to_java_name(&p.name)).collect();
573
574 writeln!(
575 out,
576 " public static CompletableFuture<{}> {}({}) {{",
577 return_type,
578 async_method_name,
579 params.join(", ")
580 )
581 .ok();
582 writeln!(out, " return CompletableFuture.supplyAsync(() -> {{").ok();
583 writeln!(out, " try {{").ok();
584 writeln!(
585 out,
586 " return {}({});",
587 sync_method_name,
588 param_names.join(", ")
589 )
590 .ok();
591 writeln!(out, " }} catch (Throwable e) {{").ok();
592 writeln!(out, " throw new CompletionException(e);").ok();
593 writeln!(out, " }}").ok();
594 writeln!(out, " }});").ok();
595 writeln!(out, " }}").ok();
596}
597
598fn gen_exception_class(package: &str, class_name: &str) -> String {
603 let mut out = String::with_capacity(512);
604
605 writeln!(out, "// DO NOT EDIT - auto-generated by alef").ok();
606 writeln!(out, "package {};", package).ok();
607 writeln!(out).ok();
608
609 writeln!(out, "public class {}Exception extends Exception {{", class_name).ok();
610 writeln!(out, " private final int code;").ok();
611 writeln!(out).ok();
612 writeln!(out, " public {}Exception(int code, String message) {{", class_name).ok();
613 writeln!(out, " super(message);").ok();
614 writeln!(out, " this.code = code;").ok();
615 writeln!(out, " }}").ok();
616 writeln!(out).ok();
617 writeln!(
618 out,
619 " public {}Exception(String message, Throwable cause) {{",
620 class_name
621 )
622 .ok();
623 writeln!(out, " super(message, cause);").ok();
624 writeln!(out, " this.code = -1;").ok();
625 writeln!(out, " }}").ok();
626 writeln!(out).ok();
627 writeln!(out, " public int getCode() {{").ok();
628 writeln!(out, " return code;").ok();
629 writeln!(out, " }}").ok();
630 writeln!(out, "}}").ok();
631
632 out
633}
634
635fn gen_facade_class(api: &ApiSurface, package: &str, public_class: &str, raw_class: &str, _prefix: &str) -> String {
640 let mut body = String::with_capacity(4096);
641
642 writeln!(body, "public final class {} {{", public_class).ok();
643 writeln!(body, " private {}() {{ }}", public_class).ok();
644 writeln!(body).ok();
645
646 for func in &api.functions {
648 let params: Vec<String> = func
650 .params
651 .iter()
652 .map(|p| {
653 let ptype = java_type(&p.ty);
654 format!("{} {}", ptype, to_java_name(&p.name))
655 })
656 .collect();
657
658 let return_type = java_type(&func.return_type);
659
660 if !func.doc.is_empty() {
661 writeln!(body, " /**").ok();
662 for line in func.doc.lines() {
663 writeln!(body, " * {}", line).ok();
664 }
665 writeln!(body, " */").ok();
666 }
667
668 writeln!(
669 body,
670 " public static {} {}({}) throws {}Exception {{",
671 return_type,
672 to_java_name(&func.name),
673 params.join(", "),
674 raw_class
675 )
676 .ok();
677
678 for param in &func.params {
680 if !param.optional {
681 let pname = to_java_name(¶m.name);
682 writeln!(
683 body,
684 " java.util.Objects.requireNonNull({}, \"{} must not be null\");",
685 pname, pname
686 )
687 .ok();
688 }
689 }
690
691 let call_args: Vec<String> = func.params.iter().map(|p| to_java_name(&p.name)).collect();
693
694 if matches!(func.return_type, TypeRef::Unit) {
695 writeln!(
696 body,
697 " {}.{}({});",
698 raw_class,
699 to_java_name(&func.name),
700 call_args.join(", ")
701 )
702 .ok();
703 } else {
704 writeln!(
705 body,
706 " return {}.{}({});",
707 raw_class,
708 to_java_name(&func.name),
709 call_args.join(", ")
710 )
711 .ok();
712 }
713
714 writeln!(body, " }}").ok();
715 writeln!(body).ok();
716
717 let has_optional = func.params.iter().any(|p| p.optional);
719 if has_optional {
720 let required_params: Vec<String> = func
721 .params
722 .iter()
723 .filter(|p| !p.optional)
724 .map(|p| {
725 let ptype = java_type(&p.ty);
726 format!("{} {}", ptype, to_java_name(&p.name))
727 })
728 .collect();
729
730 writeln!(
731 body,
732 " public static {} {}({}) throws {}Exception {{",
733 return_type,
734 to_java_name(&func.name),
735 required_params.join(", "),
736 raw_class
737 )
738 .ok();
739
740 let full_args: Vec<String> = func
742 .params
743 .iter()
744 .map(|p| {
745 if p.optional {
746 "null".to_string()
747 } else {
748 to_java_name(&p.name)
749 }
750 })
751 .collect();
752
753 if matches!(func.return_type, TypeRef::Unit) {
754 writeln!(body, " {}({});", to_java_name(&func.name), full_args.join(", ")).ok();
755 } else {
756 writeln!(
757 body,
758 " return {}({});",
759 to_java_name(&func.name),
760 full_args.join(", ")
761 )
762 .ok();
763 }
764
765 writeln!(body, " }}").ok();
766 writeln!(body).ok();
767 }
768 }
769
770 writeln!(body, "}}").ok();
771
772 let mut out = String::with_capacity(body.len() + 512);
774
775 writeln!(out, "// DO NOT EDIT - auto-generated by alef").ok();
776 writeln!(out, "package {};", package).ok();
777 writeln!(out).ok();
778
779 if body.contains("List<") {
781 writeln!(out, "import java.util.List;").ok();
782 }
783 if body.contains("Map<") {
784 writeln!(out, "import java.util.Map;").ok();
785 }
786 if body.contains("Optional<") {
787 writeln!(out, "import java.util.Optional;").ok();
788 }
789
790 writeln!(out).ok();
791 out.push_str(&body);
792
793 out
794}
795
796const RECORD_LINE_WRAP_THRESHOLD: usize = 100;
803
804fn gen_record_type(package: &str, typ: &TypeDef) -> String {
805 let field_list: Vec<String> = typ
807 .fields
808 .iter()
809 .map(|f| {
810 let ftype = if f.optional {
811 format!("Optional<{}>", java_boxed_type(&f.ty))
812 } else {
813 java_type(&f.ty).to_string()
814 };
815 format!("{} {}", ftype, to_java_name(&f.name))
816 })
817 .collect();
818
819 let single_line = format!("public record {}({}) {{ }}", typ.name, field_list.join(", "));
821
822 let mut record_block = String::new();
824 if single_line.len() > RECORD_LINE_WRAP_THRESHOLD && field_list.len() > 1 {
825 writeln!(record_block, "public record {}(", typ.name).ok();
826 for (i, field) in field_list.iter().enumerate() {
827 let comma = if i < field_list.len() - 1 { "," } else { "" };
828 writeln!(record_block, " {}{}", field, comma).ok();
829 }
830 writeln!(record_block, ") {{").ok();
831 } else {
832 writeln!(record_block, "public record {}({}) {{", typ.name, field_list.join(", ")).ok();
833 }
834
835 if typ.has_default {
837 writeln!(record_block, " public static {}Builder builder() {{", typ.name).ok();
838 writeln!(record_block, " return new {}Builder();", typ.name).ok();
839 writeln!(record_block, " }}").ok();
840 }
841
842 writeln!(record_block, "}}").ok();
843
844 let mut out = String::with_capacity(record_block.len() + 512);
846 writeln!(out, "// DO NOT EDIT - auto-generated by alef").ok();
847 writeln!(out, "package {};", package).ok();
848 writeln!(out).ok();
849 if single_line.contains("List<") {
850 writeln!(out, "import java.util.List;").ok();
851 }
852 if single_line.contains("Map<") {
853 writeln!(out, "import java.util.Map;").ok();
854 }
855 if single_line.contains("Optional<") {
856 writeln!(out, "import java.util.Optional;").ok();
857 }
858 writeln!(out).ok();
859 write!(out, "{}", record_block).ok();
860
861 out
862}
863
864fn gen_enum_class(package: &str, enum_def: &EnumDef) -> String {
869 let mut out = String::with_capacity(1024);
870
871 writeln!(out, "// DO NOT EDIT - auto-generated by alef").ok();
872 writeln!(out, "package {};", package).ok();
873 writeln!(out).ok();
874
875 writeln!(out, "public enum {} {{", enum_def.name).ok();
876
877 for (i, variant) in enum_def.variants.iter().enumerate() {
878 let comma = if i < enum_def.variants.len() - 1 { "," } else { ";" };
879 writeln!(out, " {}{}", variant.name, comma).ok();
880 }
881
882 writeln!(out, "}}").ok();
883
884 out
885}
886
887fn gen_ffi_layout(ty: &TypeRef) -> String {
892 match ty {
893 TypeRef::Primitive(prim) => java_ffi_type(prim).to_string(),
894 TypeRef::String | TypeRef::Char | TypeRef::Path | TypeRef::Json => "ValueLayout.ADDRESS".to_string(),
895 TypeRef::Bytes => "ValueLayout.ADDRESS".to_string(),
896 TypeRef::Optional(inner) => gen_ffi_layout(inner),
897 TypeRef::Vec(_) => "ValueLayout.ADDRESS".to_string(),
898 TypeRef::Map(_, _) => "ValueLayout.ADDRESS".to_string(),
899 TypeRef::Named(_) => "ValueLayout.ADDRESS".to_string(),
900 TypeRef::Unit => "".to_string(),
901 TypeRef::Duration => "ValueLayout.JAVA_LONG".to_string(),
902 }
903}
904
905fn marshal_param_to_ffi(out: &mut String, name: &str, ty: &TypeRef) {
906 match ty {
907 TypeRef::String | TypeRef::Char | TypeRef::Path | TypeRef::Json => {
908 let cname = "c".to_string() + name;
909 writeln!(out, " var {} = arena.allocateFrom({});", cname, name).ok();
910 }
911 TypeRef::Named(_) => {
912 let cname = "c".to_string() + name;
915 writeln!(out, " var {} = MemorySegment.NULL;", cname).ok();
916 }
917 TypeRef::Optional(inner) => {
918 match inner.as_ref() {
920 TypeRef::String | TypeRef::Char | TypeRef::Path | TypeRef::Json => {
921 let cname = "c".to_string() + name;
922 writeln!(
923 out,
924 " var {} = {} != null ? arena.allocateFrom({}) : MemorySegment.NULL;",
925 cname, name, name
926 )
927 .ok();
928 }
929 TypeRef::Named(_) => {
930 let cname = "c".to_string() + name;
932 writeln!(out, " var {} = MemorySegment.NULL;", cname).ok();
933 }
934 _ => {
935 }
937 }
938 }
939 _ => {
940 }
942 }
943}
944
945fn ffi_param_name(name: &str, ty: &TypeRef) -> String {
946 match ty {
947 TypeRef::String | TypeRef::Char | TypeRef::Path | TypeRef::Json => "c".to_string() + name,
948 TypeRef::Named(_) => "c".to_string() + name,
949 TypeRef::Optional(inner) => match inner.as_ref() {
950 TypeRef::String | TypeRef::Char | TypeRef::Path | TypeRef::Json | TypeRef::Named(_) => {
951 "c".to_string() + name
952 }
953 _ => name.to_string(),
954 },
955 _ => name.to_string(),
956 }
957}
958
959fn gen_function_descriptor(return_layout: &str, param_layouts: &[String]) -> String {
962 if return_layout.is_empty() {
963 if param_layouts.is_empty() {
965 "FunctionDescriptor.ofVoid()".to_string()
966 } else {
967 format!("FunctionDescriptor.ofVoid({})", param_layouts.join(", "))
968 }
969 } else {
970 if param_layouts.is_empty() {
972 format!("FunctionDescriptor.of({})", return_layout)
973 } else {
974 format!("FunctionDescriptor.of({}, {})", return_layout, param_layouts.join(", "))
975 }
976 }
977}
978
979fn is_ffi_string_return(ty: &TypeRef) -> bool {
982 match ty {
983 TypeRef::String | TypeRef::Char | TypeRef::Path | TypeRef::Json => true,
984 TypeRef::Optional(inner) => is_ffi_string_return(inner),
985 _ => false,
986 }
987}
988
989fn java_ffi_return_cast(ty: &TypeRef) -> &'static str {
991 match ty {
992 TypeRef::Primitive(prim) => match prim {
993 PrimitiveType::Bool => "boolean",
994 PrimitiveType::U8 | PrimitiveType::I8 => "byte",
995 PrimitiveType::U16 | PrimitiveType::I16 => "short",
996 PrimitiveType::U32 | PrimitiveType::I32 => "int",
997 PrimitiveType::U64 | PrimitiveType::I64 | PrimitiveType::Usize | PrimitiveType::Isize => "long",
998 PrimitiveType::F32 => "float",
999 PrimitiveType::F64 => "double",
1000 },
1001 TypeRef::Bytes | TypeRef::Vec(_) | TypeRef::Map(_, _) | TypeRef::Named(_) => "MemorySegment",
1002 _ => "MemorySegment",
1003 }
1004}
1005
1006fn gen_helper_methods(out: &mut String) {
1007 let needs_read_cstring = out.contains("readCString(");
1009 let needs_read_bytes = out.contains("readBytes(");
1010
1011 if !needs_read_cstring && !needs_read_bytes {
1012 return;
1013 }
1014
1015 writeln!(out, " // Helper methods for FFI marshalling").ok();
1016 writeln!(out).ok();
1017
1018 if needs_read_cstring {
1019 writeln!(out, " private static String readCString(MemorySegment ptr) {{").ok();
1020 writeln!(out, " if (ptr == null || ptr.address() == 0) {{").ok();
1021 writeln!(out, " return null;").ok();
1022 writeln!(out, " }}").ok();
1023 writeln!(out, " return ptr.getUtf8String(0);").ok();
1024 writeln!(out, " }}").ok();
1025 writeln!(out).ok();
1026 }
1027
1028 if needs_read_bytes {
1029 writeln!(
1030 out,
1031 " private static byte[] readBytes(MemorySegment ptr, long len) {{"
1032 )
1033 .ok();
1034 writeln!(out, " if (ptr == null || ptr.address() == 0) {{").ok();
1035 writeln!(out, " return new byte[0];").ok();
1036 writeln!(out, " }}").ok();
1037 writeln!(out, " byte[] bytes = new byte[(int) len];").ok();
1038 writeln!(
1039 out,
1040 " MemorySegment.copy(ptr, ValueLayout.JAVA_BYTE.byteSize() * 0, bytes, 0, (int) len);"
1041 )
1042 .ok();
1043 writeln!(out, " return bytes;").ok();
1044 writeln!(out, " }}").ok();
1045 }
1046}
1047
1048fn format_optional_value(ty: &TypeRef, default: &str) -> String {
1055 if default.contains("Optional.") {
1057 return default.to_string();
1058 }
1059
1060 let inner_ty = match ty {
1062 TypeRef::Optional(inner) => inner.as_ref(),
1063 other => other,
1064 };
1065
1066 let formatted_value = match inner_ty {
1068 TypeRef::Primitive(p) => match p {
1069 PrimitiveType::I64 | PrimitiveType::U64 | PrimitiveType::Isize | PrimitiveType::Usize => {
1070 if default.ends_with('L') || default.ends_with('l') {
1072 default.to_string()
1073 } else if default.parse::<i64>().is_ok() {
1074 format!("{}L", default)
1075 } else {
1076 default.to_string()
1077 }
1078 }
1079 PrimitiveType::F32 => {
1080 if default.ends_with('f') || default.ends_with('F') {
1082 default.to_string()
1083 } else if default.parse::<f32>().is_ok() {
1084 format!("{}f", default)
1085 } else {
1086 default.to_string()
1087 }
1088 }
1089 PrimitiveType::F64 => {
1090 default.to_string()
1092 }
1093 _ => default.to_string(),
1094 },
1095 _ => default.to_string(),
1096 };
1097
1098 format!("Optional.of({})", formatted_value)
1099}
1100
1101fn gen_builder_class(package: &str, typ: &TypeDef) -> String {
1102 let mut body = String::with_capacity(2048);
1103
1104 writeln!(body, "public class {}Builder {{", typ.name).ok();
1105 writeln!(body).ok();
1106
1107 for field in &typ.fields {
1109 let field_name = to_java_name(&field.name);
1110
1111 if field.name.starts_with('_') && field.name[1..].chars().all(|c| c.is_ascii_digit())
1113 || field.name.chars().next().is_none_or(|c| c.is_ascii_digit())
1114 {
1115 continue;
1116 }
1117
1118 let field_type = if field.optional {
1119 format!("Optional<{}>", java_boxed_type(&field.ty))
1120 } else {
1121 java_type(&field.ty).to_string()
1122 };
1123
1124 let default_value = if field.optional {
1125 if let Some(default) = &field.default {
1127 format_optional_value(&field.ty, default)
1129 } else {
1130 "Optional.empty()".to_string()
1132 }
1133 } else {
1134 if let Some(default) = &field.default {
1136 default.clone()
1137 } else {
1138 match &field.ty {
1139 TypeRef::String | TypeRef::Char | TypeRef::Path | TypeRef::Json => "\"\"".to_string(),
1140 TypeRef::Bytes => "new byte[0]".to_string(),
1141 TypeRef::Primitive(p) => match p {
1142 PrimitiveType::Bool => "false".to_string(),
1143 PrimitiveType::F32 | PrimitiveType::F64 => "0.0".to_string(),
1144 _ => "0".to_string(),
1145 },
1146 TypeRef::Vec(_) => "List.of()".to_string(),
1147 TypeRef::Map(_, _) => "Map.of()".to_string(),
1148 TypeRef::Optional(_) => "Optional.empty()".to_string(),
1149 _ => "null".to_string(),
1150 }
1151 }
1152 };
1153
1154 writeln!(body, " private {} {} = {};", field_type, field_name, default_value).ok();
1155 }
1156
1157 writeln!(body).ok();
1158
1159 for field in &typ.fields {
1161 if field.name.starts_with('_') && field.name[1..].chars().all(|c| c.is_ascii_digit())
1163 || field.name.chars().next().is_none_or(|c| c.is_ascii_digit())
1164 {
1165 continue;
1166 }
1167
1168 let field_name = to_java_name(&field.name);
1169 let field_name_pascal = to_class_name(&field.name);
1170 let field_type = if field.optional {
1171 format!("Optional<{}>", java_boxed_type(&field.ty))
1172 } else {
1173 java_type(&field.ty).to_string()
1174 };
1175
1176 writeln!(
1177 body,
1178 " public {}Builder with{}({} value) {{",
1179 typ.name, field_name_pascal, field_type
1180 )
1181 .ok();
1182 writeln!(body, " this.{} = value;", field_name).ok();
1183 writeln!(body, " return this;").ok();
1184 writeln!(body, " }}").ok();
1185 writeln!(body).ok();
1186 }
1187
1188 writeln!(body, " public {} build() {{", typ.name).ok();
1190 writeln!(body, " return new {}(", typ.name).ok();
1191 let non_tuple_fields: Vec<_> = typ
1192 .fields
1193 .iter()
1194 .filter(|f| {
1195 !(f.name.starts_with('_') && f.name[1..].chars().all(|c| c.is_ascii_digit())
1197 || f.name.chars().next().is_none_or(|c| c.is_ascii_digit()))
1198 })
1199 .collect();
1200 for (i, field) in non_tuple_fields.iter().enumerate() {
1201 let field_name = to_java_name(&field.name);
1202 let comma = if i < non_tuple_fields.len() - 1 { "," } else { "" };
1203 writeln!(body, " {}{}", field_name, comma).ok();
1204 }
1205 writeln!(body, " );").ok();
1206 writeln!(body, " }}").ok();
1207
1208 writeln!(body, "}}").ok();
1209
1210 let mut out = String::with_capacity(body.len() + 512);
1212
1213 writeln!(out, "// DO NOT EDIT - auto-generated by alef").ok();
1214 writeln!(out, "package {};", package).ok();
1215 writeln!(out).ok();
1216
1217 if body.contains("List<") {
1218 writeln!(out, "import java.util.List;").ok();
1219 }
1220 if body.contains("Map<") {
1221 writeln!(out, "import java.util.Map;").ok();
1222 }
1223 if body.contains("Optional<") {
1224 writeln!(out, "import java.util.Optional;").ok();
1225 }
1226
1227 writeln!(out).ok();
1228 out.push_str(&body);
1229
1230 out
1231}