Skip to content

Commit

Permalink
Adds changes for sequence data type
Browse files Browse the repository at this point in the history
  • Loading branch information
desaikd committed Sep 18, 2024
1 parent c6e0e07 commit d716c37
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 51 deletions.
5 changes: 5 additions & 0 deletions code-gen-projects/schema/sequence.isl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type::{
name: sequence,
type: list,
element: string
}
136 changes: 132 additions & 4 deletions src/bin/ion/commands/generate/generator.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::commands::generate::context::CodeGenContext;
use crate::commands::generate::context::{CodeGenContext, SequenceType};
use crate::commands::generate::model::{
AbstractDataType, DataModelNode, FieldPresence, FieldReference, FullyQualifiedTypeReference,
ScalarBuilder, StructureBuilder, WrappedScalarBuilder,
ScalarBuilder, SequenceBuilder, StructureBuilder, WrappedScalarBuilder, WrappedSequenceBuilder,
};
use crate::commands::generate::result::{
invalid_abstract_data_type_error, invalid_abstract_data_type_raw_error, CodeGenResult,
Expand Down Expand Up @@ -351,14 +351,27 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> {
.any(|it| matches!(it.constraint(), IslConstraintValue::Fields(_, _)))
{
self.build_structure_from_constraints(constraints, code_gen_context, isl_type)?
} else if constraints
.iter()
.any(|it| matches!(it.constraint(), IslConstraintValue::Element(_, _)))
{
if is_nested_type {
self.build_sequence_from_constraints(constraints, code_gen_context, isl_type)?
} else {
self.build_wrapped_sequence_from_constraints(
constraints,
code_gen_context,
isl_type,
)?
}
} else if Self::contains_scalar_constraints(constraints) {
if is_nested_type {
self.build_scalar_from_constraints(constraints, code_gen_context, isl_type)?
} else {
self.build_wrapped_scalar_from_constraints(constraints, code_gen_context, isl_type)?
}
} else {
todo!("Support for sequences, maps, scalars, and tuples not implemented yet.")
todo!("Support for maps and tuples not implemented yet.")
};

let data_model_node = DataModelNode {
Expand Down Expand Up @@ -587,7 +600,6 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> {
isl_type
)))?;

// by default fields aren't closed
wrapped_scalar_builder.base_type(type_name);
found_base_type = true;
}
Expand Down Expand Up @@ -656,6 +668,122 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> {

Ok(AbstractDataType::Scalar(scalar_builder.build()?))
}

/// Builds `AbstractDataType::WrappedSequence` from the given constraints.
/// ```
/// type::{
/// name: foo,
/// type: list,
/// element: string,
/// }
/// ```
/// This method builds `AbstractDataType`as following:
/// ```
/// AbstractDataType::WrappedSequence(
/// WrappedSequence {
/// name: vec!["org", "example", "Foo"] // assuming the namespace here is `org.example`
/// element_type: FullyQualifiedTypeReference { type_name: vec!["String"], parameters: vec![] } // Represents the element type for the list
/// sequence_type: SequenceType::List, // Represents list type for the given sequence
/// doc_comment: None // There is no doc comment defined in above ISL type def
/// source: IslType { .. } // Represents the `IslType` that is getting converted to `AbstractDataType`
/// }
/// )
/// ```
fn build_wrapped_sequence_from_constraints(
&mut self,
constraints: &[IslConstraint],
code_gen_context: &mut CodeGenContext,
parent_isl_type: &IslType,
) -> CodeGenResult<AbstractDataType> {
let mut wrapped_sequence_builder = WrappedSequenceBuilder::default();
wrapped_sequence_builder
.name(self.current_type_fully_qualified_name.to_owned())
.source(parent_isl_type.to_owned());
for constraint in constraints {
match constraint.constraint() {
IslConstraintValue::Element(isl_type_ref, _) => {
let type_name = self
.fully_qualified_type_ref_name(isl_type_ref, code_gen_context)?
.ok_or(invalid_abstract_data_type_raw_error(format!(
"Could not determine `FullQualifiedTypeReference` for type {:?}",
isl_type_ref
)))?;

wrapped_sequence_builder.element_type(type_name);
}
IslConstraintValue::Type(isl_type_ref) => {
if isl_type_ref.name() == "sexp" {
wrapped_sequence_builder.sequence_type(SequenceType::SExp);
} else if isl_type_ref.name() == "list" {
wrapped_sequence_builder.sequence_type(SequenceType::List);
}
}
_ => {
return invalid_abstract_data_type_error(
"Could not determine the abstract data type due to conflicting constraints",
);
}
}
}
Ok(AbstractDataType::WrappedSequence(
wrapped_sequence_builder.build()?,
))
}

/// Builds `AbstractDataType::Sequence` from the given constraints.
/// ```
/// {
/// type: list,
/// element: string,
/// }
/// ```
/// This method builds `AbstractDataType`as following:
/// ```
/// AbstractDataType::Sequence(
/// Sequence {
/// element_type: FullyQualifiedTypeReference { type_name: vec!["String"], parameters: vec![] } // Represents the element type for the list
/// sequence_type: SequenceType::List, // Represents list type for the given sequence
/// doc_comment: None // There is no doc comment defined in above ISL type def
/// source: IslType { .. } // Represents the `IslType` that is getting converted to `AbstractDataType`
/// }
/// )
/// ```
fn build_sequence_from_constraints(
&mut self,
constraints: &[IslConstraint],
code_gen_context: &mut CodeGenContext,
parent_isl_type: &IslType,
) -> CodeGenResult<AbstractDataType> {
let mut sequence_builder = SequenceBuilder::default();
sequence_builder.source(parent_isl_type.to_owned());
for constraint in constraints {
match constraint.constraint() {
IslConstraintValue::Element(isl_type_ref, _) => {
let type_name = self
.fully_qualified_type_ref_name(isl_type_ref, code_gen_context)?
.ok_or(invalid_abstract_data_type_raw_error(format!(
"Could not determine `FullQualifiedTypeReference` for type {:?}",
isl_type_ref
)))?;

sequence_builder.element_type(type_name);
}
IslConstraintValue::Type(isl_type_ref) => {
if isl_type_ref.name() == "sexp" {
sequence_builder.sequence_type(SequenceType::SExp);
} else if isl_type_ref.name() == "list" {
sequence_builder.sequence_type(SequenceType::List);
}
}
_ => {
return invalid_abstract_data_type_error(
"Could not determine the abstract data type due to conflicting constraints",
);
}
}
}
Ok(AbstractDataType::Sequence(sequence_builder.build()?))
}
}

#[cfg(test)]
Expand Down
61 changes: 49 additions & 12 deletions src/bin/ion/commands/generate/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,13 @@ impl FullyQualifiedTypeReference {
#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum AbstractDataType {
// Represents a scalar type which also has a name attached to it and is nominally distinct from its base type.
#[allow(dead_code)]
WrappedScalar(WrappedScalar),
// Represents a scalar value (e.g. a string or integer or user defined type)
#[allow(dead_code)]
Scalar(Scalar),
// A series of zero or more values whose type is described by the nested `element_type`
#[allow(dead_code)]
Sequence(Sequence),
// Represents a sequence type which also has anme attached to it and is nominally distinct from its element type.
WrappedSequence(WrappedSequence),
// A collection of field name/value pairs (e.g. a map)
Structure(Structure),
}
Expand All @@ -192,7 +191,12 @@ impl AbstractDataType {
AbstractDataType::Scalar(Scalar { doc_comment, .. }) => {
doc_comment.as_ref().map(|s| s.as_str())
}
AbstractDataType::Sequence(Sequence { doc_comment, .. }) => Some(doc_comment.as_str()),
AbstractDataType::Sequence(Sequence { doc_comment, .. }) => {
doc_comment.as_ref().map(|s| s.as_str())
}
AbstractDataType::WrappedSequence(WrappedSequence { doc_comment, .. }) => {
doc_comment.as_ref().map(|s| s.as_str())
}
AbstractDataType::Structure(Structure { doc_comment, .. }) => {
doc_comment.as_ref().map(|s| s.as_str())
}
Expand All @@ -206,6 +210,7 @@ impl AbstractDataType {
}
AbstractDataType::Scalar(s) => Some(s.base_type.to_owned()),
AbstractDataType::Sequence(seq) => Some(seq.element_type.to_owned()),
AbstractDataType::WrappedSequence(seq) => Some(seq.element_type.to_owned()),
AbstractDataType::Structure(structure) => Some(structure.name.to_owned().into()),
}
}
Expand Down Expand Up @@ -289,8 +294,6 @@ impl WrappedScalar {

/// Represents series of zero or more values whose type is described by the nested `element_type`
/// and sequence type is described by nested `sequence_type` (e.g. List or SExp).
/// If there is no `element` constraint present in schema type then `element_type` will be None.
/// If there is no `type` constraint present in schema type then `sequence_type` will be None.
/// e.g. Given below ISL,
/// ```
/// type::{
Expand All @@ -308,11 +311,12 @@ impl WrappedScalar {
#[allow(dead_code)]
#[derive(Debug, Clone, Builder, PartialEq, Serialize)]
#[builder(setter(into))]
pub struct Sequence {
pub struct WrappedSequence {
// Represents the fully qualified name for this data model
name: FullyQualifiedTypeName,
// Represents doc comment for the generated code
doc_comment: String,
#[builder(default)]
doc_comment: Option<String>,
// Represents the fully qualified name with namespace where each element of vector stores a module name or class/struct name.
// _Note: that a hashmap with (FullQualifiedTypeReference, DataModel) pairs will be stored in code generator to get information on the element_type name used here._
element_type: FullyQualifiedTypeReference,
Expand All @@ -326,6 +330,41 @@ pub struct Sequence {
source: IslType,
}

/// Represents series of zero or more values whose type is described by the nested `element_type`
/// and sequence type is described by nested `sequence_type` (e.g. List or SExp).
/// e.g. Given below ISL,
/// ```
/// type::{
/// name: sequence_type,
/// element: int,
/// type: list
/// }
/// ```
/// Corresponding generated code in Rust would look like following:
/// ```
/// struct SequenceType {
/// value: Vec<i64>
/// }
/// ```
#[derive(Debug, Clone, Builder, PartialEq, Serialize)]
#[builder(setter(into))]
pub struct Sequence {
// Represents doc comment for the generated code
#[builder(default)]
pub(crate) doc_comment: Option<String>,
// Represents the fully qualified name with namespace where each element of vector stores a module name or class/struct name.
// _Note: that a hashmap with (FullQualifiedTypeReference, DataModel) pairs will be stored in code generator to get information on the element_type name used here._
pub(crate) element_type: FullyQualifiedTypeReference,
// Represents the type of the sequence which is either `sexp` or `list`.
pub(crate) sequence_type: SequenceType,
// Represents the source ISL type which can be used to get other constraints useful for this type.
// For example, getting the length of this sequence from `container_length` constraint or getting a `regex` value for string type.
// This will also be useful for `text` type to verify if this is a `string` or `symbol`.
// TODO: `IslType` does not implement `Serialize`, define a custom implementation or define methods on this field that returns values which could be serialized.
#[serde(skip_serializing)]
pub(crate) source: IslType,
}

/// Represents a collection of field name/value pairs (e.g. a map)
/// e.g. Given below ISL,
/// ```
Expand Down Expand Up @@ -448,8 +487,7 @@ mod model_tests {
#[test]
fn sequence_builder_test() {
let expected_seq = Sequence {
name: vec![],
doc_comment: "This is sequence type of strings".to_string(),
doc_comment: Some("This is sequence type of strings".to_string()),
element_type: FullyQualifiedTypeReference {
type_name: vec!["String".to_string()],
parameters: vec![],
Expand All @@ -465,8 +503,7 @@ mod model_tests {

// sets all the information about the sequence except the `element_type`
seq_builder
.name(vec![])
.doc_comment("This is sequence type of strings")
.doc_comment(Some("This is sequence type of strings".to_string()))
.sequence_type(SequenceType::List)
.source(anonymous_type(vec![
type_constraint(named_type_ref("list")),
Expand Down
9 changes: 9 additions & 0 deletions src/bin/ion/commands/generate/result.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::commands::generate::model::{
ScalarBuilderError, SequenceBuilderError, StructureBuilderError, WrappedScalarBuilderError,
WrappedSequenceBuilderError,
};
use ion_schema::result::IonSchemaError;
use thiserror::Error;
Expand Down Expand Up @@ -71,6 +72,14 @@ impl From<SequenceBuilderError> for CodeGenError {
}
}

impl From<WrappedSequenceBuilderError> for CodeGenError {
fn from(value: WrappedSequenceBuilderError) -> Self {
CodeGenError::DataModelBuilderError {
description: value.to_string(),
}
}
}

impl From<StructureBuilderError> for CodeGenError {
fn from(value: StructureBuilderError) -> Self {
CodeGenError::DataModelBuilderError {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
{{ sequence_info["element_type"] | fully_qualified_type_name }} value
{% elif inline_type.code_gen_type is containing("Scalar") %}
{% set scalar_info = model.code_gen_type["WrappedScalar"] %}
{% set base_type = scalar_info["name"]["parameters"][0] | fully_qualified_type_name %}
{% set base_type = scalar_info["base_type"] | fully_qualified_type_name %}
{{ base_type }} value
{% endif %}
{% endmacro %}
Loading

0 comments on commit d716c37

Please sign in to comment.