From d32daf2ea16a85ee7a5b0fdb1c5b23c83e26b8a0 Mon Sep 17 00:00:00 2001 From: Khushboo Desai Date: Wed, 15 May 2024 14:56:43 -0700 Subject: [PATCH 1/4] Changes to generate anonymous types as child classes in Java * Adds a new template anonymous_Type.templ which has macros to generate anonymous types as child classes * Modifies class.templ to include anonymous types as child classes * Modifies setter methods in class.templ to include base types in the arguments instead of `AnonymousType`s * Modifies tests to remove reference to `AnonymousType`s * Adds new util structure `AnonymousType` which will be used to render anonymous types as chilsren and modify setter methods to include base types --- .../test/java/org/example/CodeGenTest.java | 21 +- .../ion/commands/beta/generate/generator.rs | 64 +++++- .../templates/java/anonymous_type.templ | 209 ++++++++++++++++++ .../beta/generate/templates/java/class.templ | 46 ++-- src/bin/ion/commands/beta/generate/utils.rs | 11 + 5 files changed, 328 insertions(+), 23 deletions(-) create mode 100644 src/bin/ion/commands/beta/generate/templates/java/anonymous_type.templ 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 a731f14..387b997 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 @@ -26,7 +26,13 @@ class CodeGenTest { a.add("foo"); a.add("bar"); a.add("baz"); - StructWithFields s = new StructWithFields("hello", 12, new AnonymousType3(a), 10e2); + StructWithFields s = new StructWithFields(); + + // set all the fields of `StructWithFields` + s.setA("hello"); + s.setB(12); + s.setC(a); + s.setD(10e2); // getter tests for `StructWithFields` assertEquals("hello", s.getA(), "s.getA() should return \"hello\""); @@ -39,7 +45,7 @@ class CodeGenTest { assertEquals("hi", s.getA(), "s.getA() should return \"hi\""); s.setB(6); assertEquals(6, s.getB(), "s.getB() should return `6`"); - s.setC(new AnonymousType3(new ArrayList())); + s.setC(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`"); @@ -51,7 +57,14 @@ class CodeGenTest { a.add(1); a.add(2); a.add(3); - NestedStruct n = new NestedStruct("hello", 12, new AnonymousType1(false, new AnonymousType2(a))); + NestedStruct n = new NestedStruct(); + + // set all the fields of `NestedStruct` + n.setA("hello"); + n.setB(12); + n.setC(false, a); + + // getter tests for `NestedStruct` 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`"); @@ -64,6 +77,8 @@ class CodeGenTest { assertEquals(6, n.getB(), "s.getB() should return `6`"); n.getC().setD(true); assertEquals(true, n.getC().getD(), "s.getC().getD() should return `true`"); + n.getC().setE(new ArrayList()); + assertEquals(0, n.getC().getE().getValue().size(), "s.getC().getE().getValue().size() should return ArrayList fo size 0"); } @Test void roundtripGoodTestForStructWithFields() throws IOException { diff --git a/src/bin/ion/commands/beta/generate/generator.rs b/src/bin/ion/commands/beta/generate/generator.rs index bf8dc98..f05f8a1 100644 --- a/src/bin/ion/commands/beta/generate/generator.rs +++ b/src/bin/ion/commands/beta/generate/generator.rs @@ -1,6 +1,8 @@ use crate::commands::beta::generate::context::{AbstractDataType, CodeGenContext, SequenceType}; use crate::commands::beta::generate::result::{invalid_abstract_data_type_error, CodeGenResult}; -use crate::commands::beta::generate::utils::{Field, JavaLanguage, Language, RustLanguage}; +use crate::commands::beta::generate::utils::{ + AnonymousType, Field, JavaLanguage, Language, RustLanguage, +}; use crate::commands::beta::generate::utils::{IonSchemaType, Template}; use convert_case::{Case, Casing}; use ion_schema::isl::isl_constraint::{IslConstraint, IslConstraintValue}; @@ -210,6 +212,47 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { Ok(()) } + /// generates an anonymous type that can be part of another type definition. + /// This will be used by the parent type to add this anonymous type in its namespace or module. + fn generate_anonymous_type( + &mut self, + isl_type_name: &String, + isl_type: &IslType, + anonymous_types: &mut Vec, + ) -> CodeGenResult<()> { + // Add an object called `anonymous_types` in tera context + // This will have a list of `anonymous_type` where each will include fields, a target_kind_name and abstract_data_type + let mut tera_fields = vec![]; + let mut code_gen_context = CodeGenContext::new(); + let mut nested_anonymous_types = vec![]; + let constraints = isl_type.constraints(); + for constraint in constraints { + self.map_constraint_to_abstract_data_type( + &mut nested_anonymous_types, + &mut tera_fields, + constraint, + &mut code_gen_context, + )?; + } + + // TODO: verify the `occurs` value within a field, by default the fields are optional. + if let Some(abstract_data_type) = &code_gen_context.abstract_data_type { + // Add the anonymous type into parent type's tera context + anonymous_types.push(AnonymousType { + target_kind_name: isl_type_name.to_case(Case::UpperCamel), + fields: tera_fields, + abstract_data_type: abstract_data_type.to_owned(), + anonymous_types: nested_anonymous_types, + }); + } else { + return invalid_abstract_data_type_error( + "Can not determine abstract data type, constraints are mapping not mapping to an abstract data type.", + ); + } + + Ok(()) + } + fn generate_abstract_data_type( &mut self, isl_type_name: &String, @@ -218,6 +261,7 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { let mut context = Context::new(); let mut tera_fields = vec![]; let mut code_gen_context = CodeGenContext::new(); + let mut anonymous_types = vec![]; // Set the ISL type name for the generated abstract data type context.insert("target_kind_name", &isl_type_name.to_case(Case::UpperCamel)); @@ -225,6 +269,7 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { let constraints = isl_type.constraints(); for constraint in constraints { self.map_constraint_to_abstract_data_type( + &mut anonymous_types, &mut tera_fields, constraint, &mut code_gen_context, @@ -246,6 +291,7 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { if let Some(abstract_data_type) = &code_gen_context.abstract_data_type { context.insert("fields", &tera_fields); context.insert("abstract_data_type", abstract_data_type); + context.insert("anonymous_types", &anonymous_types); } else { return invalid_abstract_data_type_error( "Can not determine abstract data type, constraints are mapping not mapping to an abstract data type.", @@ -291,7 +337,11 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { } /// Provides name of the type reference that will be used for generated abstract data type - fn type_reference_name(&mut self, isl_type_ref: &IslTypeRef) -> CodeGenResult> { + fn type_reference_name( + &mut self, + isl_type_ref: &IslTypeRef, + anonymous_types: &mut Vec, + ) -> CodeGenResult> { Ok(match isl_type_ref { IslTypeRef::Named(name, _) => { let schema_type: IonSchemaType = name.into(); @@ -302,7 +352,7 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { } IslTypeRef::Anonymous(type_def, _) => { let name = self.next_anonymous_type_name(); - self.generate_abstract_data_type(&name, type_def)?; + self.generate_anonymous_type(&name, type_def, anonymous_types)?; Some(name) } @@ -319,13 +369,14 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { /// Maps the given constraint value to an abstract data type fn map_constraint_to_abstract_data_type( &mut self, + anonymous_types: &mut Vec, tera_fields: &mut Vec, constraint: &IslConstraint, code_gen_context: &mut CodeGenContext, ) -> CodeGenResult<()> { match constraint.constraint() { IslConstraintValue::Element(isl_type, _) => { - let type_name = self.type_reference_name(isl_type)?; + let type_name = self.type_reference_name(isl_type, anonymous_types)?; self.verify_and_update_abstract_data_type( AbstractDataType::Sequence { @@ -360,7 +411,8 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { code_gen_context, )?; for (name, value) in fields.iter() { - let type_name = self.type_reference_name(value.type_reference())?; + let type_name = + self.type_reference_name(value.type_reference(), anonymous_types)?; self.generate_struct_field( tera_fields, @@ -371,7 +423,7 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { } } IslConstraintValue::Type(isl_type) => { - let type_name = self.type_reference_name(isl_type)?; + let type_name = self.type_reference_name(isl_type, anonymous_types)?; self.verify_and_update_abstract_data_type( if isl_type.name() == "list" { diff --git a/src/bin/ion/commands/beta/generate/templates/java/anonymous_type.templ b/src/bin/ion/commands/beta/generate/templates/java/anonymous_type.templ new file mode 100644 index 0000000..2cd9050 --- /dev/null +++ b/src/bin/ion/commands/beta/generate/templates/java/anonymous_type.templ @@ -0,0 +1,209 @@ +{# following macro defines an anonymous type as children class for its parent type definition #} +{% macro anonymous_type(target_kind_name, fields, abstract_data_type, nested_anonymous_types) -%} + public static class {{ target_kind_name }} { + {% for field in fields -%} + private {{ field.value_type }} {{ field.name | camel }}; + {% endfor -%} + + public {{ target_kind_name }}() {} + + {% 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 %} + {% if field.value_type is containing("AnonymousType") -%} + public void set{% filter upper_camel -%}{{ field.name }}{% endfilter -%}( + {{ self::define_params_for_anonymous_type(anonymous_types=nested_anonymous_types, field=field, abstract_data_type=abstract_data_type, initial_field_name=field.name) }} + ) { + {{ self::initialize_anonymous_type(anonymous_types=nested_anonymous_types, field=field, abstract_data_type=abstract_data_type) }} + this.{{ field.name | camel }} = {{ field.name | camel }}; + return; + {% else -%} + public void set{% filter upper_camel -%}{{ field.name }}{% endfilter -%}({{ field.value_type }} {{ field.name | camel }}) { + this.{{ field.name | camel }} = {{ field.name | camel }}; + return; + {% endif -%} + } + {% 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) { + {# Initializes 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` #} + {% if abstract_data_type["Sequence"].sequence_type == "List" %} + if(reader.getType() != IonType.LIST) { + throw new IonException("Expected list, found " + reader.getType() + " while reading {{ target_kind_name }}."); + } + {% elif abstract_data_type["Sequence"].sequence_type == "SExp" %} + if(reader.getType() != IonType.SEXP) { + throw new IonException("Expected sexpression, found " + reader.getType() + " while reading {{ target_kind_name }}."); + } + {% endif %} + 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"].element_type | is_built_in_type == false %} + value.add({{ abstract_data_type["Sequence"].element_type }}.readFrom(reader)); + {% else %} + {% if abstract_data_type["Sequence"].element_type == "bytes[]" %} + value.add(reader.newBytes()); + {% else %} + value.add(reader.{{ abstract_data_type["Sequence"].element_type | camel }}Value()); + {% endif %} + {% endif %} + } + reader.stepOut(); + {% endif %} + {{ target_kind_name }} {{ target_kind_name | camel }} = new {{ target_kind_name }}(); + {% for field in fields -%} + {{ target_kind_name | camel }}.{{ field.name | camel }} = {{ field.name | camel }}; + {% endfor %} + + return {{ target_kind_name | camel }}; + } + + + /** + * 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 #} + {% if abstract_data_type["Sequence"].sequence_type == "List" %} + writer.stepIn(IonType.LIST); + {% else %} + writer.stepIn(IonType.SEXP); + {% endif %} + for ({{ abstract_data_type["Sequence"].element_type }} value: this.value) { + {% if abstract_data_type["Sequence"].element_type | is_built_in_type == false %} + value.writeTo(writer); + {% else %} + writer.write{{ abstract_data_type["Sequence"].element_type | upper_camel }}(value); + {% endif %} + } + writer.stepOut(); + {% endif %} + } + + {% for inline_type in nested_anonymous_types -%} + {{ self::anonymous_type(target_kind_name=inline_type.target_kind_name, fields=inline_type.fields, abstract_data_type=inline_type.abstract_data_type, nested_anonymous_types=inline_type.anonymous_types) }} + {% endfor -%} + } +{% endmacro anonymous_type -%} + +{# following macro defines statements to initialize anonymous types for setter methods #} +{% macro initialize_anonymous_type(anonymous_types, field, abstract_data_type) %} + {% set map = anonymous_types | group_by(attribute="target_kind_name") %} + {% if abstract_data_type is object and abstract_data_type is containing("Sequence") %} + {% set inline_type = map[abstract_data_type["Sequence"].element_type][0] %} + {% else %} + {% set inline_type = map[field.value_type][0] %} + {% endif %} + {{ inline_type.target_kind_name }} {{ field.name | camel }} = new {{ inline_type.target_kind_name }}(); + {% for inline_type_field in inline_type.fields %} + {{ field.name | camel }}.set{{ inline_type_field.name | upper_camel }}({{ inline_type_field.name | camel }}); + {% endfor %} +{% endmacro %} + +{# following macro defines arguments to setter methods for anonymous types #} +{% macro define_params_for_anonymous_type(anonymous_types, field, abstract_data_type, initial_field_name) %} + {% set map = anonymous_types | group_by(attribute="target_kind_name") %} + {% if abstract_data_type is object and abstract_data_type is containing("Sequence") %} + {% set inline_type = map[abstract_data_type["Sequence"].element_type][0] %} + {% else -%} + {% set inline_type = map[field.value_type][0] %} + {% endif -%} + {% for inline_type_field in inline_type.fields | sort(attribute="name") %} + {% if inline_type_field.value_type is containing("AnonymousType") %} + {{ self::define_params_for_anonymous_type(anonymous_types=inline_type.anonymous_types, field=inline_type_field, abstract_data_type=inline_type.abstract_data_type, initial_field_name=initial_field_name) }} + {% else %} + {% if inline_type_field.name == "value" and not initial_field_name == field.name %} + {{ inline_type_field.value_type }} {{ field.name | camel }} + {% else %} + {{ inline_type_field.value_type }} {{ inline_type_field.name | camel }} + {% endif %} + {% endif %} + {% if not loop.last -%},{% endif -%} + {% endfor %} +{% endmacro %} \ No newline at end of file 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 8bc6d65..e7a558c 100644 --- a/src/bin/ion/commands/beta/generate/templates/java/class.templ +++ b/src/bin/ion/commands/beta/generate/templates/java/class.templ @@ -1,3 +1,6 @@ +{# Includes the macros for anonymous types that will be added as child classes #} +{% import "anonymous_type.templ" as macros %} + package {{ namespace }}; import java.util.ArrayList; import com.amazon.ion.IonReader; @@ -6,24 +9,31 @@ 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 {{ field.value_type }} {{ field.name | camel }}; -{% endfor %} +public class {{ target_kind_name }} { + {% for field in fields -%} + private {{ field.value_type }} {{ field.name | camel }}; + {% 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 %} - } + public {{ target_kind_name }}() {} {% 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 }}; + {% for field in fields %} + {% if field.value_type is containing("AnonymousType") %} + public void set{% filter upper_camel %}{{ field.name }}{% endfilter %}( + {{ macros::define_params_for_anonymous_type(anonymous_types=anonymous_types, field=field, abstract_data_type=abstract_data_type, initial_field_name=field.name) }} + ) { + {{ macros::initialize_anonymous_type(anonymous_types=anonymous_types, field=field, abstract_data_type=abstract_data_type) }} + this.{{ field.name | camel }} = {{ field.name | camel }}; + return; + {% else %} + public void set{% filter upper_camel %}{{ field.name }}{% endfilter %}({{ field.value_type }} {{ field.name | camel }}) { + this.{{ field.name | camel }} = {{ field.name | camel }}; + return; + {% endif %} } {% endfor %} @@ -35,7 +45,7 @@ public final class {{ target_kind_name }} { * 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 #} + {# Initializes all the fields of this class #} {% for field in fields -%} {{ field.value_type }} {{ field.name | camel }} = {% if field.value_type == "boolean" %} @@ -110,9 +120,13 @@ public final class {{ target_kind_name }} { } reader.stepOut(); {% endif %} - return new {{ target_kind_name }}({% for field in fields | sort(attribute="name") -%}{{ field.name | camel }}{% if not loop.last %},{% endif %}{% endfor %}); - } + {{ target_kind_name }} {{ target_kind_name | camel }} = new {{ target_kind_name }}(); + {% for field in fields -%} + {{ target_kind_name | camel }}.{{ field.name | camel }} = {{ field.name | camel }}; + {% endfor %} + return {{ target_kind_name | camel }}; + } /** * Writes a {{ target_kind_name }} as Ion from an {@link IonWriter}. @@ -159,4 +173,8 @@ public final class {{ target_kind_name }} { writer.stepOut(); {% endif %} } + + {% for inline_type in anonymous_types -%} + {{ macros::anonymous_type(target_kind_name=inline_type.target_kind_name, fields=inline_type.fields, abstract_data_type=inline_type.abstract_data_type, nested_anonymous_types=inline_type.anonymous_types) }} + {% endfor -%} } diff --git a/src/bin/ion/commands/beta/generate/utils.rs b/src/bin/ion/commands/beta/generate/utils.rs index 1328bd3..a941d4c 100644 --- a/src/bin/ion/commands/beta/generate/utils.rs +++ b/src/bin/ion/commands/beta/generate/utils.rs @@ -29,6 +29,17 @@ pub struct Field { pub(crate) isl_type_name: String, } +/// Represents an anonymous type that can be a part of another type definition. +/// This will be used by the template engine to add these intermediate data models for anonymous types +/// in to the parent type definition's module/namespace. +#[derive(Serialize)] +pub struct AnonymousType { + pub(crate) target_kind_name: String, + pub(crate) fields: Vec, + pub(crate) abstract_data_type: AbstractDataType, + pub(crate) anonymous_types: Vec, +} + pub trait Language { /// Provides a file extension based on programming language fn file_extension() -> String; From f34a3fe433468b996ce6b741a0f6745b941db06f Mon Sep 17 00:00:00 2001 From: Khushboo Desai Date: Wed, 15 May 2024 15:51:24 -0700 Subject: [PATCH 2/4] Adds changes for generating anonymous types in child modules for Rust * Adds a new `anonymous_type.templ` template to generate a child module for anonymous types * Modifies `struct.templ` to use anonymous_type template --- .../templates/rust/anonymous_type.templ | 135 ++++++++++ .../beta/generate/templates/rust/struct.templ | 231 +++++++++--------- 2 files changed, 256 insertions(+), 110 deletions(-) create mode 100644 src/bin/ion/commands/beta/generate/templates/rust/anonymous_type.templ diff --git a/src/bin/ion/commands/beta/generate/templates/rust/anonymous_type.templ b/src/bin/ion/commands/beta/generate/templates/rust/anonymous_type.templ new file mode 100644 index 0000000..1024b57 --- /dev/null +++ b/src/bin/ion/commands/beta/generate/templates/rust/anonymous_type.templ @@ -0,0 +1,135 @@ +{# following macro defines an anonymous type as children class for its parent type definition #} +{% macro anonymous_type(target_kind_name, fields, abstract_data_type, nested_anonymous_types) -%} + #[derive(Debug, Clone, Default)] + pub struct {{ target_kind_name }} { + {% for field in fields -%} + {{ 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_type }},{% endfor %}) -> Self { + Self { + {% for field in fields -%} + {{ field.name | snake }}, + {% endfor %} + } + } + + + {% for field in fields -%}pub fn {{ field.name | snake }}(&self) -> &{{ field.value_type }} { + &self.{{ field.name | snake }} + } + {% endfor %} + + + pub fn read_from(reader: &mut Reader) -> SerdeResult { + let mut abstract_data_type = {{ target_kind_name }}::default(); + {% if abstract_data_type == "Value"%} + 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_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()?; + while reader.next()? != StreamItem::Nothing { + if let Some(field_name) = reader.field_name()?.text() { + match field_name { + {% for field in fields -%} + {% 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_type | lower | replace(from="string", to ="str") }}()?{% endif %}{% if field.value_type | lower== "string" %} .to_string() {% endif %}; } + {% endif %} + {% endfor %} + _ => { + {% if abstract_data_type["Structure"] %} + return validation_error( + "Can not read field name:{{ field.name }} for {{ target_kind_name }} as it doesn't exist in the given schema type definition." + ); + {% endif %} + } + } + } + } + reader.step_out()?; + {% elif abstract_data_type is object and abstract_data_type is containing("Sequence") %} + {% if abstract_data_type["Sequence"].sequence_type == "List" %} + if reader.ion_type() != Some(IonType::List) { + return validation_error(format!( + "Expected list, found {} while reading {{ target_kind_name }}.", reader.ion_type().unwrap() + )); + } + {% elif abstract_data_type["Sequence"].sequence_type == "SExp" %} + if reader.ion_type() != Some(IonType::SExp) { + return validation_error(format!( + "Expected sexpression, found {} while reading {{ target_kind_name }}.", reader.ion_type().unwrap() + )); + } + {% endif %} + reader.step_in()?; + + abstract_data_type.value = { + let mut values = vec![]; + + while reader.next()? != StreamItem::Nothing { + {% if abstract_data_type["Sequence"].element_type | is_built_in_type == false %} + values.push({{ abstract_data_type["Sequence"].element_type }}::read_from(reader)?); + {% else %} + values.push(reader.read_{% if fields[0].isl_type_name == "symbol" %}symbol()?.text().unwrap(){% else %}{{ abstract_data_type["Sequence"].element_type | lower | replace(from="string", to ="str") }}()?{% endif %}{% if abstract_data_type["Sequence"].element_type | lower== "string" %} .to_string() {% endif %}); + {% endif %} + } + values + }; + reader.step_out()?; + {% else %} + return validation_error("Can not resolve read API template for {{ target_kind_name }}"); + {% endif %} + Ok(abstract_data_type) + } + + pub fn write_to(&self, writer: &mut W) -> SerdeResult<()> { + {% if abstract_data_type == "Value" %} + {% for field in fields %} + {% 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_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_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_type | lower }}{% endif %}(self.{{ field.name | snake }}.to_owned())?; + {% endif %} + {% endfor %} + writer.step_out()?; + {% elif abstract_data_type is object and abstract_data_type is containing("Sequence") %} + {% if abstract_data_type["Sequence"].sequence_type == "List" %} + writer.step_in(IonType::List)?; + {% else %} + writer.step_in(IonType::SExp)?; + {% endif %} + for value in &self.value { + {% if abstract_data_type["Sequence"].element_type | is_built_in_type == false %} + value.write_to(writer)?; + {% else %} + writer.write_{% if fields[0].isl_type_name == "symbol" %}symbol{% else %}{{ abstract_data_type["Sequence"].element_type | lower }}{% endif %}(value.to_owned())?; + {% endif %} + } + writer.step_out()?; + {% endif %} + Ok(()) + } + } + + {% for inline_type in nested_anonymous_types -%} + {{ self::anonymous_type(target_kind_name=inline_type.target_kind_name, fields=inline_type.fields, abstract_data_type=inline_type.abstract_data_type, nested_anonymous_types=inline_type.anonymous_types) }} + {% endfor -%} +{% endmacro %} \ No newline at end of file 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 5ed8692..a229ff7 100644 --- a/src/bin/ion/commands/beta/generate/templates/rust/struct.templ +++ b/src/bin/ion/commands/beta/generate/templates/rust/struct.templ @@ -1,128 +1,139 @@ -#[derive(Debug, Clone, Default)] -pub struct {{ target_kind_name }} { -{% for field in fields -%} - {{ field.name | snake | indent(first = true) }}: {{ field.value_type }}, -{% endfor %} -} +{# Includes the macros for anonymous types that will be added as child classes #} +{% import "anonymous_type.templ" as macros %} +use {{ target_kind_name | snake }}::{{ target_kind_name }}; +pub mod {{ target_kind_name | snake }} { + use super::*; -impl {{ target_kind_name }} { - 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 }}, - {% endfor %} - } + #[derive(Debug, Clone, Default)] + pub struct {{ target_kind_name }} { + {% for field in fields -%} + {{ 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_type }},{% endfor %}) -> Self { + Self { + {% for field in fields -%} + {{ field.name | snake }}, + {% endfor %} + } + } + - {% for field in fields -%}pub fn {{ field.name | snake }}(&self) -> &{{ field.value_type }} { - &self.{{ field.name | snake }} - } - {% endfor %} + {% for field in fields -%}pub fn {{ field.name | snake }}(&self) -> &{{ field.value_type }} { + &self.{{ field.name | snake }} + } + {% endfor %} - pub fn read_from(reader: &mut Reader) -> SerdeResult { - let mut abstract_data_type = {{ target_kind_name }}::default(); - {% if abstract_data_type == "Value"%} - 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_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()?; - while reader.next()? != StreamItem::Nothing { - if let Some(field_name) = reader.field_name()?.text() { - match field_name { - {% for field in fields -%} - {% 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_type | lower | replace(from="string", to ="str") }}()?{% endif %}{% if field.value_type | lower== "string" %} .to_string() {% endif %}; } + pub fn read_from(reader: &mut Reader) -> SerdeResult { + let mut abstract_data_type = {{ target_kind_name }}::default(); + {% if abstract_data_type == "Value"%} + 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_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()?; + while reader.next()? != StreamItem::Nothing { + if let Some(field_name) = reader.field_name()?.text() { + match field_name { + {% for field in fields -%} + {% 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_type | lower | replace(from="string", to ="str") }}()?{% endif %}{% if field.value_type | lower== "string" %} .to_string() {% endif %}; } + {% endif %} + {% endfor %} + _ => { + {% if abstract_data_type["Structure"] %} + return validation_error( + "Can not read field name:{{ field.name }} for {{ target_kind_name }} as it doesn't exist in the given schema type definition." + ); {% endif %} - {% endfor %} - _ => { - {% if abstract_data_type["Structure"] %} - return validation_error( - "Can not read field name:{{ field.name }} for {{ target_kind_name }} as it doesn't exist in the given schema type definition." - ); - {% endif %} - } + } + } } } - } - reader.step_out()?; - {% elif abstract_data_type is object and abstract_data_type is containing("Sequence") %} - {% if abstract_data_type["Sequence"].sequence_type == "List" %} - if reader.ion_type() != Some(IonType::List) { - return validation_error(format!( - "Expected list, found {} while reading {{ target_kind_name }}.", reader.ion_type().unwrap() - )); - } - {% elif abstract_data_type["Sequence"].sequence_type == "SExp" %} - if reader.ion_type() != Some(IonType::SExp) { - return validation_error(format!( - "Expected sexpression, found {} while reading {{ target_kind_name }}.", reader.ion_type().unwrap() - )); - } - {% endif %} - reader.step_in()?; + reader.step_out()?; + {% elif abstract_data_type is object and abstract_data_type is containing("Sequence") %} + {% if abstract_data_type["Sequence"].sequence_type == "List" %} + if reader.ion_type() != Some(IonType::List) { + return validation_error(format!( + "Expected list, found {} while reading {{ target_kind_name }}.", reader.ion_type().unwrap() + )); + } + {% elif abstract_data_type["Sequence"].sequence_type == "SExp" %} + if reader.ion_type() != Some(IonType::SExp) { + return validation_error(format!( + "Expected sexpression, found {} while reading {{ target_kind_name }}.", reader.ion_type().unwrap() + )); + } + {% endif %} + reader.step_in()?; - abstract_data_type.value = { - let mut values = vec![]; + abstract_data_type.value = { + let mut values = vec![]; - while reader.next()? != StreamItem::Nothing { - {% if abstract_data_type["Sequence"].element_type | is_built_in_type == false %} - values.push({{ abstract_data_type["Sequence"].element_type }}::read_from(reader)?); + while reader.next()? != StreamItem::Nothing { + {% if abstract_data_type["Sequence"].element_type | is_built_in_type == false %} + values.push({{ abstract_data_type["Sequence"].element_type }}::read_from(reader)?); + {% else %} + values.push(reader.read_{% if fields[0].isl_type_name == "symbol" %}symbol()?.text().unwrap(){% else %}{{ abstract_data_type["Sequence"].element_type | lower | replace(from="string", to ="str") }}()?{% endif %}{% if abstract_data_type["Sequence"].element_type | lower== "string" %} .to_string() {% endif %}); + {% endif %} + } + values + }; + reader.step_out()?; + {% else %} + return validation_error("Can not resolve read API template for {{ target_kind_name }}"); + {% endif %} + Ok(abstract_data_type) + } + + pub fn write_to(&self, writer: &mut W) -> SerdeResult<()> { + {% if abstract_data_type == "Value" %} + {% for field in fields %} + {% if field.value_type | is_built_in_type == false %} + self.{{ field.name | snake }}.write_to(writer)?; {% else %} - values.push(reader.read_{% if fields[0].isl_type_name == "symbol" %}symbol()?.text().unwrap(){% else %}{{ abstract_data_type["Sequence"].element_type | lower | replace(from="string", to ="str") }}()?{% endif %}{% if abstract_data_type["Sequence"].element_type | lower== "string" %} .to_string() {% endif %}); + writer.write_{% if field.isl_type_name == "symbol" %}symbol{% else %}{{ field.value_type | lower }}{% endif %}(self.value)?; {% endif %} - } - values - }; - reader.step_out()?; - {% else %} - return validation_error("Can not resolve read API template for {{ target_kind_name }}"); - {% endif %} - Ok(abstract_data_type) - } - - pub fn write_to(&self, writer: &mut W) -> SerdeResult<()> { - {% if abstract_data_type == "Value" %} - {% for field in fields %} - {% 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_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_type | is_built_in_type == false %} - self.{{ field.name | snake }}.write_to(writer)?; + {% 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_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_type | lower }}{% endif %}(self.{{ field.name | snake }}.to_owned())?; + {% endif %} + {% endfor %} + writer.step_out()?; + {% elif abstract_data_type is object and abstract_data_type is containing("Sequence") %} + {% if abstract_data_type["Sequence"].sequence_type == "List" %} + writer.step_in(IonType::List)?; {% 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_type | lower }}{% endif %}(self.{{ field.name | snake }}.to_owned())?; + writer.step_in(IonType::SExp)?; {% endif %} - {% endfor %} - writer.step_out()?; - {% elif abstract_data_type is object and abstract_data_type is containing("Sequence") %} - {% if abstract_data_type["Sequence"].sequence_type == "List" %} - writer.step_in(IonType::List)?; - {% else %} - writer.step_in(IonType::SExp)?; + for value in &self.value { + {% if abstract_data_type["Sequence"].element_type | is_built_in_type == false %} + value.write_to(writer)?; + {% else %} + writer.write_{% if fields[0].isl_type_name == "symbol" %}symbol{% else %}{{ abstract_data_type["Sequence"].element_type | lower }}{% endif %}(value.to_owned())?; + {% endif %} + } + writer.step_out()?; {% endif %} - for value in &self.value { - {% if abstract_data_type["Sequence"].element_type | is_built_in_type == false %} - value.write_to(writer)?; - {% else %} - writer.write_{% if fields[0].isl_type_name == "symbol" %}symbol{% else %}{{ abstract_data_type["Sequence"].element_type | lower }}{% endif %}(value.to_owned())?; - {% endif %} - } - writer.step_out()?; - {% endif %} - Ok(()) + Ok(()) + } } + + {% for inline_type in anonymous_types -%} + {{ macros::anonymous_type(target_kind_name=inline_type.target_kind_name, fields=inline_type.fields, abstract_data_type=inline_type.abstract_data_type, nested_anonymous_types=inline_type.anonymous_types) }} + {% endfor -%} } From 06edcda30e8ec16ac8a0cfc1f3502f0930f938b3 Mon Sep 17 00:00:00 2001 From: Khushboo Desai Date: Wed, 22 May 2024 14:56:00 -0700 Subject: [PATCH 3/4] Adds suggested changes --- src/bin/ion/commands/beta/generate/generator.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bin/ion/commands/beta/generate/generator.rs b/src/bin/ion/commands/beta/generate/generator.rs index f05f8a1..97a6797 100644 --- a/src/bin/ion/commands/beta/generate/generator.rs +++ b/src/bin/ion/commands/beta/generate/generator.rs @@ -216,7 +216,7 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { /// This will be used by the parent type to add this anonymous type in its namespace or module. fn generate_anonymous_type( &mut self, - isl_type_name: &String, + type_name: &String, isl_type: &IslType, anonymous_types: &mut Vec, ) -> CodeGenResult<()> { @@ -239,14 +239,14 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { if let Some(abstract_data_type) = &code_gen_context.abstract_data_type { // Add the anonymous type into parent type's tera context anonymous_types.push(AnonymousType { - target_kind_name: isl_type_name.to_case(Case::UpperCamel), + target_kind_name: type_name.to_case(Case::UpperCamel), fields: tera_fields, abstract_data_type: abstract_data_type.to_owned(), anonymous_types: nested_anonymous_types, }); } else { return invalid_abstract_data_type_error( - "Can not determine abstract data type, constraints are mapping not mapping to an abstract data type.", + "Can not determine abstract data type, specified constraints do not map to an abstract data type.", ); } @@ -294,7 +294,7 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { context.insert("anonymous_types", &anonymous_types); } else { return invalid_abstract_data_type_error( - "Can not determine abstract data type, constraints are mapping not mapping to an abstract data type.", + "Can not determine abstract data type, specified constraints do not map to an abstract data type.", ); } From e4675b9ed0a5e376916e0c451be51d2e416df3ad Mon Sep 17 00:00:00 2001 From: Khushboo Desai Date: Thu, 23 May 2024 11:10:04 -0700 Subject: [PATCH 4/4] Renames all references of `anonymous type` to `nested type` --- .../ion/commands/beta/generate/generator.rs | 60 +++++++++---------- .../beta/generate/templates/java/class.templ | 12 ++-- ...anonymous_type.templ => nested_type.templ} | 24 ++++---- ...anonymous_type.templ => nested_type.templ} | 4 +- .../beta/generate/templates/rust/struct.templ | 6 +- src/bin/ion/commands/beta/generate/utils.rs | 8 +-- tests/cli.rs | 20 +++---- 7 files changed, 67 insertions(+), 67 deletions(-) rename src/bin/ion/commands/beta/generate/templates/java/{anonymous_type.templ => nested_type.templ} (87%) rename src/bin/ion/commands/beta/generate/templates/rust/{anonymous_type.templ => nested_type.templ} (96%) diff --git a/src/bin/ion/commands/beta/generate/generator.rs b/src/bin/ion/commands/beta/generate/generator.rs index 97a6797..20e8fab 100644 --- a/src/bin/ion/commands/beta/generate/generator.rs +++ b/src/bin/ion/commands/beta/generate/generator.rs @@ -1,7 +1,7 @@ use crate::commands::beta::generate::context::{AbstractDataType, CodeGenContext, SequenceType}; use crate::commands::beta::generate::result::{invalid_abstract_data_type_error, CodeGenResult}; use crate::commands::beta::generate::utils::{ - AnonymousType, Field, JavaLanguage, Language, RustLanguage, + Field, JavaLanguage, Language, NestedType, RustLanguage, }; use crate::commands::beta::generate::utils::{IonSchemaType, Template}; use convert_case::{Case, Casing}; @@ -26,8 +26,8 @@ pub(crate) struct CodeGenerator<'a, L: Language> { // This field is used by Java code generation to get the namespace for generated code. // For Rust code generation, this will be set to None. namespace: Option<&'a str>, - // Represents a counter for naming anonymous type definitions - pub(crate) anonymous_type_counter: usize, + // Represents a counter for naming nested type definitions + pub(crate) nested_type_counter: usize, phantom: PhantomData, } @@ -58,7 +58,7 @@ impl<'a> CodeGenerator<'a, RustLanguage> { Self { output, namespace: None, - anonymous_type_counter: 0, + nested_type_counter: 0, tera, phantom: PhantomData, } @@ -76,7 +76,7 @@ impl<'a> CodeGenerator<'a, JavaLanguage> { Self { output, namespace: Some(namespace), - anonymous_type_counter: 0, + nested_type_counter: 0, tera, phantom: PhantomData, } @@ -166,8 +166,8 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { schema_system: &mut SchemaSystem, ) -> CodeGenResult<()> { for authority in authorities { - // Sort the directory paths to ensure anonymous type names are always ordered based - // on directory path. (anonymous type name uses a counter in its name to represent that type) + // Sort the directory paths to ensure nested type names are always ordered based + // on directory path. (nested type name uses a counter in its name to represent that type) let mut paths = fs::read_dir(authority)?.collect::, _>>()?; paths.sort_by_key(|dir| dir.path()); for schema_file in paths { @@ -212,16 +212,16 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { Ok(()) } - /// generates an anonymous type that can be part of another type definition. - /// This will be used by the parent type to add this anonymous type in its namespace or module. - fn generate_anonymous_type( + /// generates an nested type that can be part of another type definition. + /// This will be used by the parent type to add this nested type in its namespace or module. + fn generate_nested_type( &mut self, type_name: &String, isl_type: &IslType, - anonymous_types: &mut Vec, + nested_types: &mut Vec, ) -> CodeGenResult<()> { - // Add an object called `anonymous_types` in tera context - // This will have a list of `anonymous_type` where each will include fields, a target_kind_name and abstract_data_type + // Add an object called `nested_types` in tera context + // This will have a list of `nested_type` where each will include fields, a target_kind_name and abstract_data_type let mut tera_fields = vec![]; let mut code_gen_context = CodeGenContext::new(); let mut nested_anonymous_types = vec![]; @@ -237,12 +237,12 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { // TODO: verify the `occurs` value within a field, by default the fields are optional. if let Some(abstract_data_type) = &code_gen_context.abstract_data_type { - // Add the anonymous type into parent type's tera context - anonymous_types.push(AnonymousType { + // Add the nested type into parent type's tera context + nested_types.push(NestedType { target_kind_name: type_name.to_case(Case::UpperCamel), fields: tera_fields, abstract_data_type: abstract_data_type.to_owned(), - anonymous_types: nested_anonymous_types, + nested_types: nested_anonymous_types, }); } else { return invalid_abstract_data_type_error( @@ -261,7 +261,7 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { let mut context = Context::new(); let mut tera_fields = vec![]; let mut code_gen_context = CodeGenContext::new(); - let mut anonymous_types = vec![]; + let mut nested_types = vec![]; // Set the ISL type name for the generated abstract data type context.insert("target_kind_name", &isl_type_name.to_case(Case::UpperCamel)); @@ -269,7 +269,7 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { let constraints = isl_type.constraints(); for constraint in constraints { self.map_constraint_to_abstract_data_type( - &mut anonymous_types, + &mut nested_types, &mut tera_fields, constraint, &mut code_gen_context, @@ -291,7 +291,7 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { if let Some(abstract_data_type) = &code_gen_context.abstract_data_type { context.insert("fields", &tera_fields); context.insert("abstract_data_type", abstract_data_type); - context.insert("anonymous_types", &anonymous_types); + context.insert("nested_types", &nested_types); } else { return invalid_abstract_data_type_error( "Can not determine abstract data type, specified constraints do not map to an abstract data type.", @@ -340,7 +340,7 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { fn type_reference_name( &mut self, isl_type_ref: &IslTypeRef, - anonymous_types: &mut Vec, + nested_types: &mut Vec, ) -> CodeGenResult> { Ok(match isl_type_ref { IslTypeRef::Named(name, _) => { @@ -351,32 +351,32 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { unimplemented!("Imports in schema are not supported yet!"); } IslTypeRef::Anonymous(type_def, _) => { - let name = self.next_anonymous_type_name(); - self.generate_anonymous_type(&name, type_def, anonymous_types)?; + let name = self.next_nested_type_name(); + self.generate_nested_type(&name, type_def, nested_types)?; Some(name) } }) } - /// Provides the name of the next anonymous type - fn next_anonymous_type_name(&mut self) -> String { - self.anonymous_type_counter += 1; - let name = format!("AnonymousType{}", self.anonymous_type_counter); + /// Provides the name of the next nested type + fn next_nested_type_name(&mut self) -> String { + self.nested_type_counter += 1; + let name = format!("NestedType{}", self.nested_type_counter); name } /// Maps the given constraint value to an abstract data type fn map_constraint_to_abstract_data_type( &mut self, - anonymous_types: &mut Vec, + nested_types: &mut Vec, tera_fields: &mut Vec, constraint: &IslConstraint, code_gen_context: &mut CodeGenContext, ) -> CodeGenResult<()> { match constraint.constraint() { IslConstraintValue::Element(isl_type, _) => { - let type_name = self.type_reference_name(isl_type, anonymous_types)?; + let type_name = self.type_reference_name(isl_type, nested_types)?; self.verify_and_update_abstract_data_type( AbstractDataType::Sequence { @@ -412,7 +412,7 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { )?; for (name, value) in fields.iter() { let type_name = - self.type_reference_name(value.type_reference(), anonymous_types)?; + self.type_reference_name(value.type_reference(), nested_types)?; self.generate_struct_field( tera_fields, @@ -423,7 +423,7 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { } } IslConstraintValue::Type(isl_type) => { - let type_name = self.type_reference_name(isl_type, anonymous_types)?; + let type_name = self.type_reference_name(isl_type, nested_types)?; self.verify_and_update_abstract_data_type( if isl_type.name() == "list" { 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 e7a558c..74c87f3 100644 --- a/src/bin/ion/commands/beta/generate/templates/java/class.templ +++ b/src/bin/ion/commands/beta/generate/templates/java/class.templ @@ -1,5 +1,5 @@ {# Includes the macros for anonymous types that will be added as child classes #} -{% import "anonymous_type.templ" as macros %} +{% import "nested_type.templ" as macros %} package {{ namespace }}; import java.util.ArrayList; @@ -22,11 +22,11 @@ public class {{ target_kind_name }} { {% endfor %} {% for field in fields %} - {% if field.value_type is containing("AnonymousType") %} + {% if field.value_type is containing("NestedType") %} public void set{% filter upper_camel %}{{ field.name }}{% endfilter %}( - {{ macros::define_params_for_anonymous_type(anonymous_types=anonymous_types, field=field, abstract_data_type=abstract_data_type, initial_field_name=field.name) }} + {{ macros::define_params_for_anonymous_type(nested_types=nested_types, field=field, abstract_data_type=abstract_data_type, initial_field_name=field.name) }} ) { - {{ macros::initialize_anonymous_type(anonymous_types=anonymous_types, field=field, abstract_data_type=abstract_data_type) }} + {{ macros::initialize_anonymous_type(nested_types=nested_types, field=field, abstract_data_type=abstract_data_type) }} this.{{ field.name | camel }} = {{ field.name | camel }}; return; {% else %} @@ -174,7 +174,7 @@ public class {{ target_kind_name }} { {% endif %} } - {% for inline_type in anonymous_types -%} - {{ macros::anonymous_type(target_kind_name=inline_type.target_kind_name, fields=inline_type.fields, abstract_data_type=inline_type.abstract_data_type, nested_anonymous_types=inline_type.anonymous_types) }} + {% for inline_type in nested_types -%} + {{ macros::nested_type(target_kind_name=inline_type.target_kind_name, fields=inline_type.fields, abstract_data_type=inline_type.abstract_data_type, nested_anonymous_types=inline_type.nested_types) }} {% endfor -%} } diff --git a/src/bin/ion/commands/beta/generate/templates/java/anonymous_type.templ b/src/bin/ion/commands/beta/generate/templates/java/nested_type.templ similarity index 87% rename from src/bin/ion/commands/beta/generate/templates/java/anonymous_type.templ rename to src/bin/ion/commands/beta/generate/templates/java/nested_type.templ index 2cd9050..b9cb20d 100644 --- a/src/bin/ion/commands/beta/generate/templates/java/anonymous_type.templ +++ b/src/bin/ion/commands/beta/generate/templates/java/nested_type.templ @@ -1,5 +1,5 @@ {# following macro defines an anonymous type as children class for its parent type definition #} -{% macro anonymous_type(target_kind_name, fields, abstract_data_type, nested_anonymous_types) -%} +{% macro nested_type(target_kind_name, fields, abstract_data_type, nested_anonymous_types) -%} public static class {{ target_kind_name }} { {% for field in fields -%} private {{ field.value_type }} {{ field.name | camel }}; @@ -14,11 +14,11 @@ {% for field in fields %} - {% if field.value_type is containing("AnonymousType") -%} + {% if field.value_type is containing("NestedType") -%} public void set{% filter upper_camel -%}{{ field.name }}{% endfilter -%}( - {{ self::define_params_for_anonymous_type(anonymous_types=nested_anonymous_types, field=field, abstract_data_type=abstract_data_type, initial_field_name=field.name) }} + {{ self::define_params_for_anonymous_type(nested_types=nested_anonymous_types, field=field, abstract_data_type=abstract_data_type, initial_field_name=field.name) }} ) { - {{ self::initialize_anonymous_type(anonymous_types=nested_anonymous_types, field=field, abstract_data_type=abstract_data_type) }} + {{ self::initialize_anonymous_type(nested_types=nested_anonymous_types, field=field, abstract_data_type=abstract_data_type) }} this.{{ field.name | camel }} = {{ field.name | camel }}; return; {% else -%} @@ -167,14 +167,14 @@ } {% for inline_type in nested_anonymous_types -%} - {{ self::anonymous_type(target_kind_name=inline_type.target_kind_name, fields=inline_type.fields, abstract_data_type=inline_type.abstract_data_type, nested_anonymous_types=inline_type.anonymous_types) }} + {{ self::nested_type(target_kind_name=inline_type.target_kind_name, fields=inline_type.fields, abstract_data_type=inline_type.abstract_data_type, nested_anonymous_types=inline_type.nested_types) }} {% endfor -%} } -{% endmacro anonymous_type -%} +{% endmacro nested_type -%} {# following macro defines statements to initialize anonymous types for setter methods #} -{% macro initialize_anonymous_type(anonymous_types, field, abstract_data_type) %} - {% set map = anonymous_types | group_by(attribute="target_kind_name") %} +{% macro initialize_anonymous_type(nested_types, field, abstract_data_type) %} + {% set map = nested_types | group_by(attribute="target_kind_name") %} {% if abstract_data_type is object and abstract_data_type is containing("Sequence") %} {% set inline_type = map[abstract_data_type["Sequence"].element_type][0] %} {% else %} @@ -187,16 +187,16 @@ {% endmacro %} {# following macro defines arguments to setter methods for anonymous types #} -{% macro define_params_for_anonymous_type(anonymous_types, field, abstract_data_type, initial_field_name) %} - {% set map = anonymous_types | group_by(attribute="target_kind_name") %} +{% macro define_params_for_anonymous_type(nested_types, field, abstract_data_type, initial_field_name) %} + {% set map = nested_types | group_by(attribute="target_kind_name") %} {% if abstract_data_type is object and abstract_data_type is containing("Sequence") %} {% set inline_type = map[abstract_data_type["Sequence"].element_type][0] %} {% else -%} {% set inline_type = map[field.value_type][0] %} {% endif -%} {% for inline_type_field in inline_type.fields | sort(attribute="name") %} - {% if inline_type_field.value_type is containing("AnonymousType") %} - {{ self::define_params_for_anonymous_type(anonymous_types=inline_type.anonymous_types, field=inline_type_field, abstract_data_type=inline_type.abstract_data_type, initial_field_name=initial_field_name) }} + {% if inline_type_field.value_type is containing("NestedType") %} + {{ self::define_params_for_anonymous_type(nested_types=inline_type.nested_types, field=inline_type_field, abstract_data_type=inline_type.abstract_data_type, initial_field_name=initial_field_name) }} {% else %} {% if inline_type_field.name == "value" and not initial_field_name == field.name %} {{ inline_type_field.value_type }} {{ field.name | camel }} diff --git a/src/bin/ion/commands/beta/generate/templates/rust/anonymous_type.templ b/src/bin/ion/commands/beta/generate/templates/rust/nested_type.templ similarity index 96% rename from src/bin/ion/commands/beta/generate/templates/rust/anonymous_type.templ rename to src/bin/ion/commands/beta/generate/templates/rust/nested_type.templ index 1024b57..86dafd5 100644 --- a/src/bin/ion/commands/beta/generate/templates/rust/anonymous_type.templ +++ b/src/bin/ion/commands/beta/generate/templates/rust/nested_type.templ @@ -1,5 +1,5 @@ {# following macro defines an anonymous type as children class for its parent type definition #} -{% macro anonymous_type(target_kind_name, fields, abstract_data_type, nested_anonymous_types) -%} +{% macro nested_type(target_kind_name, fields, abstract_data_type, nested_anonymous_types) -%} #[derive(Debug, Clone, Default)] pub struct {{ target_kind_name }} { {% for field in fields -%} @@ -130,6 +130,6 @@ } {% for inline_type in nested_anonymous_types -%} - {{ self::anonymous_type(target_kind_name=inline_type.target_kind_name, fields=inline_type.fields, abstract_data_type=inline_type.abstract_data_type, nested_anonymous_types=inline_type.anonymous_types) }} + {{ self::nested_type(target_kind_name=inline_type.target_kind_name, fields=inline_type.fields, abstract_data_type=inline_type.abstract_data_type, nested_anonymous_types=inline_type.nested_types) }} {% endfor -%} {% endmacro %} \ No newline at end of file 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 a229ff7..0dcdc0d 100644 --- a/src/bin/ion/commands/beta/generate/templates/rust/struct.templ +++ b/src/bin/ion/commands/beta/generate/templates/rust/struct.templ @@ -1,5 +1,5 @@ {# Includes the macros for anonymous types that will be added as child classes #} -{% import "anonymous_type.templ" as macros %} +{% import "nested_type.templ" as macros %} use {{ target_kind_name | snake }}::{{ target_kind_name }}; pub mod {{ target_kind_name | snake }} { use super::*; @@ -133,7 +133,7 @@ pub mod {{ target_kind_name | snake }} { } } - {% for inline_type in anonymous_types -%} - {{ macros::anonymous_type(target_kind_name=inline_type.target_kind_name, fields=inline_type.fields, abstract_data_type=inline_type.abstract_data_type, nested_anonymous_types=inline_type.anonymous_types) }} + {% for inline_type in nested_types -%} + {{ macros::nested_type(target_kind_name=inline_type.target_kind_name, fields=inline_type.fields, abstract_data_type=inline_type.abstract_data_type, nested_anonymous_types=inline_type.nested_types) }} {% endfor -%} } diff --git a/src/bin/ion/commands/beta/generate/utils.rs b/src/bin/ion/commands/beta/generate/utils.rs index a941d4c..b2fdcae 100644 --- a/src/bin/ion/commands/beta/generate/utils.rs +++ b/src/bin/ion/commands/beta/generate/utils.rs @@ -29,15 +29,15 @@ pub struct Field { pub(crate) isl_type_name: String, } -/// Represents an anonymous type that can be a part of another type definition. -/// This will be used by the template engine to add these intermediate data models for anonymous types +/// Represents an nested type that can be a part of another type definition. +/// This will be used by the template engine to add these intermediate data models for nested types /// in to the parent type definition's module/namespace. #[derive(Serialize)] -pub struct AnonymousType { +pub struct NestedType { pub(crate) target_kind_name: String, pub(crate) fields: Vec, pub(crate) abstract_data_type: AbstractDataType, - pub(crate) anonymous_types: Vec, + pub(crate) nested_types: Vec, } pub trait Language { diff --git a/tests/cli.rs b/tests/cli.rs index 07dee21..44a5c71 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -270,17 +270,17 @@ fn test_write_all_values(#[case] number: i32, #[case] expected_output: &str) -> &["reference: OtherType"], &["pub fn reference(&self) -> &OtherType {"] )] -#[case::struct_with_anonymous_type( +#[case::struct_with_nested_type( r#" type::{ - name: struct_with_anonymous_type, + name: struct_with_nested_type, fields: { - anonymous_type: { type: int } + nested_type: { type: int } } } "#, - &["anonymous_type: AnonymousType1"], - &["pub fn anonymous_type(&self) -> &AnonymousType1 {"] + &["nested_type: NestedType1"], + &["pub fn nested_type(&self) -> &NestedType1 {"] )] /// Calls ion-cli beta generate with different schema file. Pass the test if the return value contains the expected properties and accessors. fn test_code_generation_in_rust( @@ -382,17 +382,17 @@ fn test_code_generation_in_rust( &["public OtherType getReference() {"] )] #[case( - "StructWithAnonymousType", + "StructWithNestedType", r#" type::{ - name: struct_with_anonymous_type, + name: struct_with_nested_type, fields: { - anonymous_type: { type: int } + nested_type: { type: int } } } "#, - &["private AnonymousType1 anonymousType;"], - &["public AnonymousType1 getAnonymousType() {"] + &["private NestedType1 nestedType;"], + &["public NestedType1 getNestedType() {"] )] /// Calls ion-cli beta generate with different schema file. Pass the test if the return value contains the expected properties and accessors. fn test_code_generation_in_java(