diff --git a/code-gen-projects/input/bad/nested_struct/mismatched_type.ion b/code-gen-projects/input/bad/nested_struct/mismatched_type.ion new file mode 100644 index 0000000..e8c48db --- /dev/null +++ b/code-gen-projects/input/bad/nested_struct/mismatched_type.ion @@ -0,0 +1,8 @@ +// nested struct with type mismatched fields +{ + A: "hello", + B: 12, + C: { + D: 1e0, // expected type: bool + } +} diff --git a/code-gen-projects/input/bad/struct_with_fields/mismatched_sequence_element.ion b/code-gen-projects/input/bad/struct_with_fields/mismatched_sequence_element.ion new file mode 100644 index 0000000..9d60dd2 --- /dev/null +++ b/code-gen-projects/input/bad/struct_with_fields/mismatched_sequence_element.ion @@ -0,0 +1,8 @@ +// struct with mismatched sequence element +{ + A: "hello", + B: 12, + C: [1, 2, 3], + D: 10e2 +} + diff --git a/code-gen-projects/input/bad/struct_with_fields/mismatched_type.ion b/code-gen-projects/input/bad/struct_with_fields/mismatched_type.ion new file mode 100644 index 0000000..675df97 --- /dev/null +++ b/code-gen-projects/input/bad/struct_with_fields/mismatched_type.ion @@ -0,0 +1,8 @@ +// simple struct with type mismatched fields +{ + A: "hello", + B: false, // expected field type: int + C: ["foo", "bar", "baz"], + D: 10e2 +} + diff --git a/code-gen-projects/input/good/nested_struct/empty_values.ion b/code-gen-projects/input/good/nested_struct/empty_values.ion new file mode 100644 index 0000000..545e6b2 --- /dev/null +++ b/code-gen-projects/input/good/nested_struct/empty_values.ion @@ -0,0 +1,8 @@ +// nested struct with empty string and zeros +{ + C: { + D: false, + }, + A: "", + B: 0, +} diff --git a/code-gen-projects/input/nested_struct.ion b/code-gen-projects/input/good/nested_struct/valid_fields.ion similarity index 52% rename from code-gen-projects/input/nested_struct.ion rename to code-gen-projects/input/good/nested_struct/valid_fields.ion index e0b35eb..391c8cd 100644 --- a/code-gen-projects/input/nested_struct.ion +++ b/code-gen-projects/input/good/nested_struct/valid_fields.ion @@ -1,7 +1,8 @@ +// nested struct with all valid fields { A: "hello", B: 12, C: { D: false, } -} \ No newline at end of file +} diff --git a/code-gen-projects/input/good/nested_struct/valid_unordered_fields.ion b/code-gen-projects/input/good/nested_struct/valid_unordered_fields.ion new file mode 100644 index 0000000..da21667 --- /dev/null +++ b/code-gen-projects/input/good/nested_struct/valid_unordered_fields.ion @@ -0,0 +1,9 @@ +// nested struct with unordered fields +{ + B: 12, + A: "hello", + C: { + D: false, + } +} + diff --git a/code-gen-projects/input/good/struct_with_fields/empty_values.ion b/code-gen-projects/input/good/struct_with_fields/empty_values.ion new file mode 100644 index 0000000..67986dc --- /dev/null +++ b/code-gen-projects/input/good/struct_with_fields/empty_values.ion @@ -0,0 +1,7 @@ +// struct with empty list, empty string and zeros +{ + C: [], + A: "", + B: 0, + D: 0e0, +} diff --git a/code-gen-projects/input/good/struct_with_fields/valid_fields.ion b/code-gen-projects/input/good/struct_with_fields/valid_fields.ion new file mode 100644 index 0000000..f306089 --- /dev/null +++ b/code-gen-projects/input/good/struct_with_fields/valid_fields.ion @@ -0,0 +1,7 @@ +// simple struct with all valid fields +{ + A: "hello", + B: 12, + C: ["foo", "bar", "baz"], + D: 10e2 +} diff --git a/code-gen-projects/input/struct_with_fields.ion b/code-gen-projects/input/good/struct_with_fields/valid_unordered_fields.ion similarity index 52% rename from code-gen-projects/input/struct_with_fields.ion rename to code-gen-projects/input/good/struct_with_fields/valid_unordered_fields.ion index c94337a..80b9a4e 100644 --- a/code-gen-projects/input/struct_with_fields.ion +++ b/code-gen-projects/input/good/struct_with_fields/valid_unordered_fields.ion @@ -1,5 +1,8 @@ + +// struct with unordered fields { + C: ["foo", "bar", "baz"], A: "hello", B: 12, - C: ["foo", "bar", "baz"], -} \ No newline at end of file + D: 10e2, +} diff --git a/code-gen-projects/java/code-gen-demo/build.gradle.kts b/code-gen-projects/java/code-gen-demo/build.gradle.kts index fb52d99..cfb1817 100644 --- a/code-gen-projects/java/code-gen-demo/build.gradle.kts +++ b/code-gen-projects/java/code-gen-demo/build.gradle.kts @@ -65,4 +65,8 @@ tasks { tasks.named("test") { // Use JUnit Platform for unit tests. useJUnitPlatform() + testLogging { + showStandardStreams = true + events("skipped", "failed") + } } diff --git a/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java b/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java index 75951fd..3e093bc 100644 --- a/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java +++ b/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java @@ -3,24 +3,133 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; import java.util.ArrayList; +import com.amazon.ion.system.IonReaderBuilder; +import com.amazon.ion.IonReader; +import com.amazon.ion.system.IonTextWriterBuilder; +import com.amazon.ion.IonWriter; +import com.amazon.ion.IonSystem; +import com.amazon.ion.system.IonSystemBuilder; +import com.amazon.ion.IonLoader; +import com.amazon.ion.IonException; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.File; class CodeGenTest { - // TODO: Add roundtrip tests after read-write APIs are supported for Java code generation + private static final IonSystem ionSystem = IonSystemBuilder.standard().build(); + private static final IonLoader ionLoader = ionSystem.getLoader(); + @Test void getterAndSetterTestForStructWithFields() { ArrayList a = new ArrayList(); a.add("foo"); a.add("bar"); a.add("baz"); - StructWithFields s = new StructWithFields("hello", 12, new AnonymousType2(a)); + StructWithFields s = new StructWithFields("hello", 12, new AnonymousType2(a), 10e2); + + // getter tests for `StructWithFields` assertEquals("hello", s.getA(), "s.getA() should return \"hello\""); assertEquals(12, s.getB(), "s.getB() should return `12`"); assertEquals(3, s.getC().getValue().size(), "s.getC().getValue() should return ArrayList fo size 3"); + assertEquals(10e2, s.getD(), "s.getD() should return `10e2`"); + + // setter tests for `StructWithFields` + s.setA("hi"); + assertEquals("hi", s.getA(), "s.getA() should return \"hi\""); + s.setB(6); + assertEquals(6, s.getB(), "s.getB() should return `6`"); + s.setC(new AnonymousType2(new ArrayList())); + assertEquals(true, s.getC().getValue().isEmpty(), "s.getC().isEmpty() should return `true`"); + s.setD(11e3); + assertEquals(11e3 ,s.getD(), "s.getD() should return `11e3`"); } @Test void getterAndSetterTestForNestedStruct() { - NestedStruct n = new NestedStruct("hello", 12, new AnonymousType1(false)); - assertEquals("hello", n.getA(), "n.getA() should return \"hello\""); - assertEquals(12, n.getB(), "n.getB() should return `12`"); - assertEquals(false, n.getC().getD(), "n.getC().getD() should return `false`"); + // getter tests for `NestedStruct` + NestedStruct n = new NestedStruct("hello", 12, new AnonymousType1(false)); + assertEquals("hello", n.getA(), "n.getA() should return \"hello\""); + assertEquals(12, n.getB(), "n.getB() should return `12`"); + assertEquals(false, n.getC().getD(), "n.getC().getD() should return `false`"); + + // setter tests for `NestedStruct` + n.setA("hi"); + assertEquals("hi", n.getA(), "s.getA() should return \"hi\""); + n.setB(6); + assertEquals(6, n.getB(), "s.getB() should return `6`"); + n.getC().setD(true); + assertEquals(true, n.getC().getD(), "s.getC().getD() should return `true`"); + } + + @Test void roundtripGoodTestForStructWithFields() throws IOException { + File dir = new File("./../../input/good/struct_with_fields"); + String[] fileNames = dir.list(); + for (String fileName : fileNames) { + File f = new File(dir, fileName); + InputStream inputStream = new FileInputStream(f); + IonTextWriterBuilder b = IonTextWriterBuilder.standard(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IonReaderBuilder readerBuilder = IonReaderBuilder.standard(); + try (IonReader reader = readerBuilder.build(inputStream)) { + reader.next(); + StructWithFields s = StructWithFields.readFrom(reader); + IonWriter writer = b.build(out); + s.writeTo(writer); + writer.close(); + assertEquals(ionLoader.load(f), ionLoader.load(out.toByteArray())); + } + } + } + + @Test void roundtripBadTestForStructWithFields() throws IOException { + File dir = new File("./../../input/bad/struct_with_fields"); + String[] fileNames = dir.list(); + for (String fileName : fileNames) { + File f = new File(dir, fileName); + InputStream inputStream = new FileInputStream(f); + IonTextWriterBuilder b = IonTextWriterBuilder.standard(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IonReaderBuilder readerBuilder = IonReaderBuilder.standard(); + try (IonReader reader = readerBuilder.build(inputStream)) { + reader.next(); + assertThrows(Throwable.class, () -> { StructWithFields s = StructWithFields.readFrom(reader); }); + } + } + } + + @Test void roundtripGoodTestForNestedStruct() throws IOException { + File dir = new File("./../../input/good/nested_struct"); + String[] fileNames = dir.list(); + for (String fileName : fileNames) { + File f = new File(dir, fileName); + InputStream inputStream = new FileInputStream(f); + IonTextWriterBuilder b = IonTextWriterBuilder.standard(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IonReaderBuilder readerBuilder = IonReaderBuilder.standard(); + try (IonReader reader = readerBuilder.build(inputStream)) { + reader.next(); + NestedStruct n = NestedStruct.readFrom(reader); + IonWriter writer = b.build(out); + n.writeTo(writer); + writer.close(); + assertEquals(ionLoader.load(f), ionLoader.load(out.toByteArray())); + } } + } + + @Test void roundtripBadTestForNestedStruct() throws IOException { + File dir = new File("./../../input/bad/nested_struct"); + String[] fileNames = dir.list(); + for (String fileName : fileNames) { + File f = new File(dir, fileName); + InputStream inputStream = new FileInputStream(f); + IonTextWriterBuilder b = IonTextWriterBuilder.standard(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IonReaderBuilder readerBuilder = IonReaderBuilder.standard(); + try (IonReader reader = readerBuilder.build(inputStream)) { + reader.next(); + assertThrows(Throwable.class, () -> { NestedStruct n = NestedStruct.readFrom(reader); }); + } + } + } } diff --git a/code-gen-projects/rust/code-gen-demo/Cargo.lock b/code-gen-projects/rust/code-gen-demo/Cargo.lock index 8b0484b..e4a1fb7 100644 --- a/code-gen-projects/rust/code-gen-demo/Cargo.lock +++ b/code-gen-projects/rust/code-gen-demo/Cargo.lock @@ -95,6 +95,7 @@ name = "code-gen-demo" version = "0.1.0" dependencies = [ "ion-rs", + "test-generator", ] [[package]] @@ -121,8 +122,8 @@ checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" dependencies = [ "fnv", "ident_case", - "proc-macro2", - "quote", + "proc-macro2 1.0.79", + "quote 1.0.35", "strsim", "syn 2.0.55", ] @@ -134,7 +135,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", - "quote", + "quote 1.0.35", "syn 2.0.55", ] @@ -144,8 +145,8 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee5df75c70b95bd3aacc8e2fd098797692fb1d54121019c4de481e42f04c8a1" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.79", + "quote 1.0.35", "syn 1.0.109", ] @@ -165,6 +166,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "hashbrown" version = "0.12.3" @@ -344,6 +351,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid", +] + [[package]] name = "proc-macro2" version = "1.0.79" @@ -353,13 +369,22 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", +] + [[package]] name = "quote" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.79", ] [[package]] @@ -383,8 +408,8 @@ version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.79", + "quote 1.0.35", "syn 2.0.55", ] @@ -422,8 +447,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ "darling", - "proc-macro2", - "quote", + "proc-macro2 1.0.79", + "quote 1.0.35", "syn 2.0.55", ] @@ -439,14 +464,25 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid", +] + [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.79", + "quote 1.0.35", "unicode-ident", ] @@ -456,11 +492,23 @@ version = "2.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.79", + "quote 1.0.35", "unicode-ident", ] +[[package]] +name = "test-generator" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b23be2add79223226e1cb6446cb3e37506a5927089870687a0f1149bb7a073a" +dependencies = [ + "glob", + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", +] + [[package]] name = "thiserror" version = "1.0.58" @@ -476,8 +524,8 @@ version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.79", + "quote 1.0.35", "syn 2.0.55", ] @@ -518,6 +566,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + [[package]] name = "wasm-bindgen" version = "0.2.92" @@ -537,8 +591,8 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2", - "quote", + "proc-macro2 1.0.79", + "quote 1.0.35", "syn 2.0.55", "wasm-bindgen-shared", ] @@ -549,7 +603,7 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ - "quote", + "quote 1.0.35", "wasm-bindgen-macro-support", ] @@ -559,8 +613,8 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.79", + "quote 1.0.35", "syn 2.0.55", "wasm-bindgen-backend", "wasm-bindgen-shared", diff --git a/code-gen-projects/rust/code-gen-demo/Cargo.toml b/code-gen-projects/rust/code-gen-demo/Cargo.toml index b93b5d2..22741f2 100644 --- a/code-gen-projects/rust/code-gen-demo/Cargo.toml +++ b/code-gen-projects/rust/code-gen-demo/Cargo.toml @@ -8,5 +8,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] -ion-rs = {version = "1.0.0-rc.2", features = ["experimental"]} \ No newline at end of file +[dev-dependencies] +ion-rs = { version = "1.0.0-rc.2", features = ["experimental"] } +test-generator = "0.3" \ No newline at end of file diff --git a/code-gen-projects/rust/code-gen-demo/src/lib.rs b/code-gen-projects/rust/code-gen-demo/src/lib.rs index 5e50879..8741095 100644 --- a/code-gen-projects/rust/code-gen-demo/src/lib.rs +++ b/code-gen-projects/rust/code-gen-demo/src/lib.rs @@ -10,6 +10,8 @@ mod tests { use ion_rs::ReaderBuilder; use ion_rs::TextWriterBuilder; use std::fs; + use test_generator::test_resources; + include!(concat!(env!("OUT_DIR"), "/ion_generated_code.rs")); #[test] @@ -18,12 +20,9 @@ mod tests { assert_eq!(result, 4); } - #[test] - fn test_roundtrip_generated_code_structs_with_fields() -> IonResult<()> { - let ion_string = fs::read_to_string(&format!( - "{}/../../input/struct_with_fields.ion", - env!("CARGO_MANIFEST_DIR") - ))?; + #[test_resources("../../input/good/struct_with_fields/**/*.ion")] + fn roundtrip_good_test_generated_code_structs_with_fields(file_name: &str) -> IonResult<()> { + let ion_string = fs::read_to_string(file_name)?; let mut reader = ReaderBuilder::new().build(ion_string.clone())?; let mut buffer = Vec::new(); let mut text_writer = TextWriterBuilder::default().build(&mut buffer)?; @@ -42,12 +41,21 @@ mod tests { Ok(()) } - #[test] - fn test_roundtrip_generated_code_nested_structs() -> IonResult<()> { - let ion_string = fs::read_to_string(&format!( - "{}/../../input/nested_struct.ion", - env!("CARGO_MANIFEST_DIR") - ))?; + #[test_resources("../../input/bad/struct_with_fields/**/*.ion")] + fn roundtrip_bad_test_generated_code_structs_with_fields(file_name: &str) -> IonResult<()> { + let ion_string = fs::read_to_string(file_name)?; + let mut reader = ReaderBuilder::new().build(ion_string.clone())?; + // read given Ion value using Ion reader + reader.next()?; + let result = StructWithFields::read_from(&mut reader); + assert!(result.is_err()); + + Ok(()) + } + + #[test_resources("../../input/good/nested_struct/**/*.ion")] + fn roundtrip_good_test_generated_code_nested_structs(file_name: &str) -> IonResult<()> { + let ion_string = fs::read_to_string(file_name)?; let mut reader = ReaderBuilder::new().build(ion_string.clone())?; let mut buffer = Vec::new(); let mut text_writer = TextWriterBuilder::default().build(&mut buffer)?; @@ -65,4 +73,16 @@ mod tests { Ok(()) } + + #[test_resources("../../input/bad/nested_struct/**/*.ion")] + fn roundtrip_bad_test_generated_code_nested_structs(file_name: &str) -> IonResult<()> { + let ion_string = fs::read_to_string(file_name)?; + let mut reader = ReaderBuilder::new().build(ion_string.clone())?; + // read given Ion value using Ion reader + reader.next()?; + let result = NestedStruct::read_from(&mut reader); + assert!(result.is_err()); + + Ok(()) + } } diff --git a/code-gen-projects/schema/struct_with_fields.isl b/code-gen-projects/schema/struct_with_fields.isl index dc70102..f03fbb0 100644 --- a/code-gen-projects/schema/struct_with_fields.isl +++ b/code-gen-projects/schema/struct_with_fields.isl @@ -3,7 +3,8 @@ type::{ fields: { A: string, B: int, - C: { element: string } + C: { element: string }, + D: float, } } diff --git a/src/bin/ion/commands/beta/generate/generator.rs b/src/bin/ion/commands/beta/generate/generator.rs index 9e64bb2..6695be4 100644 --- a/src/bin/ion/commands/beta/generate/generator.rs +++ b/src/bin/ion/commands/beta/generate/generator.rs @@ -364,7 +364,7 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { ) -> CodeGenResult<()> { tera_fields.push(Field { name: field_name.to_string(), - value: abstract_data_type_name, + value_type: abstract_data_type_name, isl_type_name, }); Ok(()) diff --git a/src/bin/ion/commands/beta/generate/templates/java/class.templ b/src/bin/ion/commands/beta/generate/templates/java/class.templ index 968040f..4a457c0 100644 --- a/src/bin/ion/commands/beta/generate/templates/java/class.templ +++ b/src/bin/ion/commands/beta/generate/templates/java/class.templ @@ -1,19 +1,149 @@ package {{ namespace }}; import java.util.ArrayList; +import com.amazon.ion.IonReader; +import com.amazon.ion.IonException; +import com.amazon.ion.IonWriter; +import com.amazon.ion.IonType; +import java.io.IOException; public final class {{ target_kind_name }} { {% for field in fields -%} - private final {{ field.value }} {{ field.name | camel }}; + private {{ field.value_type }} {{ field.name | camel }}; {% endfor %} - public {{ target_kind_name }}({% for field in fields | sort(attribute="name") -%}{{ field.value }} {{ field.name | camel }}{% if not loop.last %},{% endif %}{% endfor %}) { + public {{ target_kind_name }}({% for field in fields | sort(attribute="name") -%}{{ field.value_type }} {{ field.name | camel }}{% if not loop.last %},{% endif %}{% endfor %}) { {% for field in fields -%} this.{{ field.name | camel }} = {{ field.name | camel }}; {% endfor %} } - {% for field in fields -%}public {{ field.value }} get{% filter upper_camel %}{{ field.name }}{% endfilter %}() { + {% for field in fields -%}public {{ field.value_type }} get{% filter upper_camel %}{{ field.name }}{% endfilter %}() { return this.{{ field.name | camel }}; } {% endfor %} + + {% for field in fields -%}public {{ field.value_type }} set{% filter upper_camel %}{{ field.name }}{% endfilter %}({{ field.value_type }} {{ field.name | camel }}) { + return this.{{ field.name | camel }} = {{ field.name | camel }}; + } + {% endfor %} + + + /** + * Reads a {{ target_kind_name }} from an {@link IonReader}. + * + * This method does not advance the reader at the current level. + * The caller is responsible for positioning the reader on the value to read. + */ + public static {{ target_kind_name }} readFrom(IonReader reader) { + {# Intializes all the fields of this class #} + {% for field in fields -%} + {{ field.value_type }} {{ field.name | camel }} = + {% if field.value_type == "boolean" %} + false + {% elif field.value_type == "int" or field.value_type == "double" %} + 0 + {% else %} + null + {% endif %}; + {% endfor %} + {% if abstract_data_type == "Value"%} + {# Reads `Value` class with a single field `value` #} + value = {% if fields[0].value_type | is_built_in_type %} + {% if fields[0].value_type == "bytes[]" %} + reader.newBytes(); + {% else %} + reader.{{ fields[0].value_type | camel }}Value(); + {% endif %} + {% else %} + {{ fields[0].value_type }}.readFrom(reader); + {% endif %} + {% elif abstract_data_type is object and abstract_data_type is containing("Structure") %} + {# Reads `Structure` class with multiple fields based on `field.name` #} + reader.stepIn(); + while (reader.hasNext()) { + reader.next(); + String fieldName = reader.getFieldName(); + switch(fieldName) { + {% for field in fields %} + case "{{ field.name }}": + {{ field.name | camel }} = {% if field.value_type | is_built_in_type %} + {% if field.value_type == "bytes[]" %} + reader.newBytes(); + {% else %} + reader.{{ field.value_type | camel }}Value(); + {% endif %} + {% else %} + {{ field.value_type }}.readFrom(reader); + {% endif %} + break; + {% endfor %} + default: + throw new IonException("Can not read field name:" + fieldName + " for {{ target_kind_name }} as it doesn't exist in the given schema type definition."); + } + } + reader.stepOut(); + {% elif abstract_data_type is object and abstract_data_type is containing("Sequence") %} + {# Reads `Sequence` class with a single field `value` that is an `ArrayList` #} + reader.stepIn(); + value = new {{ fields[0].value_type }}(); + {# Iterate through the `ArraList` and read each element in it based on the data type provided in `abstract_data_type[Sequence]` #} + while (reader.hasNext()) { + reader.next(); + {% if abstract_data_type["Sequence"] | is_built_in_type == false %} + value.add({{ abstract_data_type["Sequence"] }}.readFrom(reader)); + {% else %} + {% if abstract_data_type["Sequence"] == "bytes[]" %} + value.add(reader.newBytes()); + {% else %} + value.add(reader.{{ abstract_data_type["Sequence"] | camel }}Value()); + {% endif %} + {% endif %} + } + reader.stepOut(); + {% endif %} + return new {{ target_kind_name }}({% for field in fields | sort(attribute="name") -%}{{ field.name | camel }}{% if not loop.last %},{% endif %}{% endfor %}); + } + + + /** + * Writes a {{ target_kind_name }} as Ion from an {@link IonWriter}. + * + * This method does not close the writer after writing is complete. + * The caller is responsible for closing the stream associated with the writer. + */ + public void writeTo(IonWriter writer) throws IOException { + {% if abstract_data_type == "Value" %} + {# Writes `Value` class with a single field `value` as an Ion value #} + {% for field in fields %} + {% if field.value_type | is_built_in_type == false %} + this.{{ field.name | camel }}.writeTo(writer)?; + {% else %} + writer.write{{ field.isl_type_name | upper_camel }}(this.value); + {% endif %} + {% endfor %} + {% elif abstract_data_type is object and abstract_data_type is containing("Structure") %} + {# Writes `Structure` class with multiple fields based on `field.name` as an Ion struct #} + writer.stepIn(IonType.STRUCT); + {% for field in fields %} + writer.setFieldName("{{ field.name }}"); + {% if field.value_type | is_built_in_type == false %} + this.{{ field.name | camel }}.writeTo(writer); + {% else %} + writer.write{{ field.isl_type_name | upper_camel }}(this.{{ field.name | camel }}); + {% endif %} + {% endfor %} + writer.stepOut(); + {% elif abstract_data_type is object and abstract_data_type is containing("Sequence") %} + {# Writes `Sequence` class with a single field `value` that is an `ArrayList` as an Ion sequence #} + writer.stepIn(IonType.LIST); + for ({{ abstract_data_type["Sequence"] }} value: this.value) { + {% if abstract_data_type["Sequence"] | is_built_in_type == false %} + value.writeTo(writer); + {% else %} + writer.write{{ abstract_data_type["Sequence"] | upper_camel }}(value); + {% endif %} + } + writer.stepOut(); + {% endif %} + } } diff --git a/src/bin/ion/commands/beta/generate/templates/rust/struct.templ b/src/bin/ion/commands/beta/generate/templates/rust/struct.templ index e331076..6e1e265 100644 --- a/src/bin/ion/commands/beta/generate/templates/rust/struct.templ +++ b/src/bin/ion/commands/beta/generate/templates/rust/struct.templ @@ -1,12 +1,12 @@ #[derive(Debug, Clone, Default)] pub struct {{ target_kind_name }} { {% for field in fields -%} - {{ field.name | snake | indent(first = true) }}: {{ field.value }}, + {{ field.name | snake | indent(first = true) }}: {{ field.value_type }}, {% endfor %} } impl {{ target_kind_name }} { - pub fn new({% for field in fields | sort(attribute="name") -%}{{ field.name | snake }}: {{ field.value }},{% endfor %}) -> Self { + pub fn new({% for field in fields | sort(attribute="name") -%}{{ field.name | snake }}: {{ field.value_type }},{% endfor %}) -> Self { Self { {% for field in fields -%} {{ field.name | snake }}, @@ -15,7 +15,7 @@ impl {{ target_kind_name }} { } - {% for field in fields -%}pub fn {{ field.name | snake }}(&self) -> &{{ field.value }} { + {% for field in fields -%}pub fn {{ field.name | snake }}(&self) -> &{{ field.value_type }} { &self.{{ field.name | snake }} } {% endfor %} @@ -24,10 +24,10 @@ impl {{ target_kind_name }} { pub fn read_from(reader: &mut Reader) -> IonResult { let mut abstract_data_type = {{ target_kind_name }}::default(); {% if abstract_data_type == "Value"%} - abstract_data_type.value = {% if fields[0].value | is_built_in_type == false %} - {{ fields[0].value }}::read_from(reader)?; + abstract_data_type.value = {% if fields[0].value_type | is_built_in_type == false %} + {{ fields[0].value_type }}::read_from(reader)?; {% else %} - reader.read_{% if fields[0].isl_type_name == "symbol" %}symbol()?.text().unwrap(){% else %}{{ fields[0].value | replace(from="string", to ="str") }}()?{% endif %}{% if fields[0].value | lower == "string" %} .to_string() {% endif %}; + reader.read_{% if fields[0].isl_type_name == "symbol" %}symbol()?.text().unwrap(){% else %}{{ fields[0].value_type | replace(from="string", to ="str") }}()?{% endif %}{% if fields[0].value_type | lower == "string" %} .to_string() {% endif %}; {% endif %} {% elif abstract_data_type is object and abstract_data_type is containing("Structure") %} reader.step_in()?; @@ -35,10 +35,10 @@ impl {{ target_kind_name }} { if let Some(field_name) = reader.field_name()?.text() { match field_name { {% for field in fields -%} - {% if field.value | is_built_in_type == false %} - "{{ field.name }}" => { abstract_data_type.{{ field.name | snake }} = {{ field.value }}::read_from(reader)?; } + {% if field.value_type | is_built_in_type == false %} + "{{ field.name }}" => { abstract_data_type.{{ field.name | snake }} = {{ field.value_type }}::read_from(reader)?; } {% else %} - "{{ field.name }}" => { abstract_data_type.{{ field.name | snake}} = reader.read_{% if field.isl_type_name == "symbol" %}symbol()?.text().unwrap(){% else %}{{ field.value | lower | replace(from="string", to ="str") }}()?{% endif %}{% if field.value | lower== "string" %} .to_string() {% endif %}; } + "{{ field.name }}" => { abstract_data_type.{{ field.name | snake}} = reader.read_{% if field.isl_type_name == "symbol" %}symbol()?.text().unwrap(){% else %}{{ field.value_type | lower | replace(from="string", to ="str") }}()?{% endif %}{% if field.value_type | lower== "string" %} .to_string() {% endif %}; } {% endif %} {% endfor %} _ => { @@ -76,21 +76,21 @@ impl {{ target_kind_name }} { pub fn write_to(&self, writer: &mut W) -> IonResult<()> { {% if abstract_data_type == "Value" %} {% for field in fields %} - {% if field.value | is_built_in_type == false %} + {% if field.value_type | is_built_in_type == false %} self.{{ field.name | snake }}.write_to(writer)?; {% else %} - writer.write_{% if field.isl_type_name == "symbol" %}symbol{% else %}{{ field.value | lower }}{% endif %}(self.value)?; + writer.write_{% if field.isl_type_name == "symbol" %}symbol{% else %}{{ field.value_type | lower }}{% endif %}(self.value)?; {% endif %} {% endfor %} {% elif abstract_data_type is object and abstract_data_type is containing("Structure") %} writer.step_in(IonType::Struct)?; {% for field in fields %} writer.set_field_name("{{ field.name }}"); - {% if field.value | is_built_in_type == false %} + {% if field.value_type | is_built_in_type == false %} self.{{ field.name | snake }}.write_to(writer)?; {% else %} {# TODO: Change the following `to_owned` to only be used when writing i64,f32,f64,bool which require owned value as input #} - writer.write_{% if field.isl_type_name == "symbol" %}symbol{% else %}{{ field.value | lower }}{% endif %}(self.{{ field.name | snake }}.to_owned())?; + writer.write_{% if field.isl_type_name == "symbol" %}symbol{% else %}{{ field.value_type | lower }}{% endif %}(self.{{ field.name | snake }}.to_owned())?; {% endif %} {% endfor %} writer.step_out()?; diff --git a/src/bin/ion/commands/beta/generate/utils.rs b/src/bin/ion/commands/beta/generate/utils.rs index a1f1b71..d66594c 100644 --- a/src/bin/ion/commands/beta/generate/utils.rs +++ b/src/bin/ion/commands/beta/generate/utils.rs @@ -9,7 +9,7 @@ use std::fmt::{Display, Formatter}; #[derive(Serialize)] pub struct Field { pub(crate) name: String, - pub(crate) value: String, + pub(crate) value_type: String, pub(crate) isl_type_name: String, } @@ -88,7 +88,7 @@ impl Language for JavaLanguage { } fn is_built_in_type(name: &str) -> bool { - matches!(name, "int" | "String" | "boolean" | "byte[]" | "float") + matches!(name, "int" | "String" | "boolean" | "byte[]" | "double") } fn template_name(template: &Template) -> String { diff --git a/tests/cli.rs b/tests/cli.rs index df51c9a..1beb817 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -336,7 +336,7 @@ fn test_code_generation_in_rust( } } "#, - &["private final int id;", "private final String name;"], + &["private int id;", "private String name;"], &["public String getName() {", "public int getId() {"] )] #[case( @@ -347,7 +347,7 @@ fn test_code_generation_in_rust( type: int // this will be a field in struct } "#, - &["private final int value;"], + &["private int value;"], &["public int getValue() {"] )] #[case( @@ -358,7 +358,7 @@ fn test_code_generation_in_rust( element: string // this will be a sequence field in struct } "#, - &["private final ArrayList value;"], + &["private ArrayList value;"], &["public ArrayList getValue() {"] )] #[case( @@ -376,7 +376,7 @@ fn test_code_generation_in_rust( type: int } "#, - &["private final OtherType reference;"], + &["private OtherType reference;"], &["public OtherType getReference() {"] )] #[case( @@ -389,7 +389,7 @@ fn test_code_generation_in_rust( } } "#, - &["private final AnonymousType1 anonymousType;"], + &["private AnonymousType1 anonymousType;"], &["public AnonymousType1 getAnonymousType() {"] )] /// Calls ion-cli beta generate with different schema file. Pass the test if the return value contains the expected properties and accessors.