rdc/targets/
java.rs

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
14/// This is a struct that represents a generated Java class.
15pub 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
41/// This function generates Java code from an IntermediateRepresentation.
42/// Result can be saved to files and compiled.
43pub 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/// This macro generates code for all the provided types and their dependencies.
58///
59/// Example:
60/// ```rust
61/// use rdc::{rdc_java, RDC};
62///
63/// #[derive(RDC)]
64/// struct MyStruct {
65///     field1: String,
66///     dependency: Dependency
67/// }
68///
69/// #[derive(RDC)]
70/// struct Dependency {
71///     field1: String,
72/// }
73///
74/// #[derive(RDC)]
75/// enum MyEnum {
76///    Variant1,
77///    Variant2
78/// }
79///
80/// let classes = rdc_java!(MyStruct, MyEnum);
81/// assert!(classes.is_ok());
82/// assert_eq!(classes.unwrap().len(), 3);
83/// ```
84#[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
97/// This function writes generated Java code to files.
98/// ```rust
99/// use rdc::targets::java::{JavaClass, write_java};
100/// use rdc::errors::Error;
101/// fn write_example(classes: Vec<JavaClass>) -> Result<(), Error> {
102///     write_java(classes.as_slice(), "com.example", "src/main/java")
103/// }
104/// ```
105/// This will create a directory structure like this:
106/// ```text
107/// src/main/java
108/// ├── com
109/// │   └── example
110/// │       ├── Dependency.java
111/// │       ├── MyEnum.java
112/// │       └── MyStruct.java
113/// ```
114pub 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}