1use crate::errors::Error;
2use crate::ir::IntermediateRepresentation;
3use std::io::Write;
4
5mod cg_data_enum;
6mod cg_enum;
7mod cg_struct;
8mod cg_utils;
9pub mod type_resolver;
10
11#[cfg(test)]
12mod tests;
13
14pub struct JavaClass {
16 name: String,
17 code: String,
18}
19
20impl JavaClass {
21 pub fn new(name: String, code: String) -> Self {
22 JavaClass { name, code }
23 }
24
25 pub fn from_tokens(name: String, tokens: genco::prelude::java::Tokens) -> Result<Self, Error> {
26 let code = tokens
27 .to_string()
28 .map_err(|_| Error::new("Failed to generate Java code"))?;
29 Ok(JavaClass::new(name, code))
30 }
31
32 pub fn name(&self) -> &str {
33 &self.name
34 }
35
36 pub fn code(&self) -> &str {
37 &self.code
38 }
39}
40
41pub fn generate_java_code(ir: &IntermediateRepresentation) -> Result<Vec<JavaClass>, Error> {
44 let mut classes = Vec::new();
45 for struct_ir in ir.structs() {
46 classes.push(cg_struct::generate_data_class(struct_ir)?);
47 }
48 for enum_ir in ir.enums() {
49 classes.push(cg_enum::generate_enum_class(enum_ir)?);
50 }
51 for data_enum_ir in ir.data_enums() {
52 classes.push(cg_data_enum::generate_enum_data_class(data_enum_ir)?);
53 }
54 Ok(classes)
55}
56
57#[macro_export]
85macro_rules! rdc_java {
86 ($($type:ty),*) => {
87 {
88 let mut ir = $crate::ir::IntermediateRepresentation::new($crate::ir::TypeTarget::Java);
89 $(
90 ir.add::<$type>();
91 )*
92 $crate::targets::java::generate_java_code(&ir)
93 }
94 };
95}
96
97pub fn write_java(classes: &[JavaClass], package: &str, directory: &str) -> Result<(), Error> {
115 let mut path = std::path::PathBuf::from(directory);
116 path.push(package.replace('.', "/"));
117 std::fs::create_dir_all(&path).map_err(|_| Error::new("Failed to create directory"))?;
118 for class in classes {
119 let mut file_path = path.clone();
120 file_path.push(format!("{}.java", class.name()));
121 let mut file =
122 std::fs::File::create(file_path).map_err(|_| Error::new("Failed to create file"))?;
123 let code = format!("package {};\n\n{}", package, class.code());
124 file.write_all(code.as_bytes())
125 .map_err(|_| Error::new("Failed to write to file"))?;
126 }
127 Ok(())
128}
129
130#[cfg(test)]
131mod writer_tests {
132 #[allow(unused_imports)]
133 use super::*;
134 use crate as rdc;
135 use crate::targets::java::write_java;
136 use crate::{rdc_java, RDC};
137
138 #[derive(RDC)]
139 #[allow(unused)]
140 struct MyStruct {
141 field1: String,
142 dependency: Dependency,
143 }
144
145 #[derive(RDC)]
146 #[allow(unused)]
147 struct Dependency {
148 field1: String,
149 }
150
151 #[derive(RDC)]
152 #[allow(unused)]
153 enum MyEnum {
154 Variant1,
155 Variant2,
156 }
157
158 #[test]
159 fn test_generate_java_code() {
160 let results = rdc_java!(MyStruct, MyEnum);
161 assert!(results.is_ok());
162 assert_eq!(results.unwrap().len(), 3);
163 }
164
165 #[test]
166 fn test_write_java() {
167 let results = rdc_java!(MyStruct, MyEnum);
168 assert!(results.is_ok());
169 assert!(write_java(
170 &results.unwrap(),
171 "com.example",
172 "target/test-tmp/src/main/java"
173 )
174 .is_ok());
175 assert!(
176 std::path::Path::new("target/test-tmp/src/main/java/com/example/MyStruct.java")
177 .exists()
178 );
179 assert!(
180 std::path::Path::new("target/test-tmp/src/main/java/com/example/MyEnum.java").exists()
181 );
182 assert!(
183 std::path::Path::new("target/test-tmp/src/main/java/com/example/Dependency.java")
184 .exists()
185 );
186 }
187}