Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modifies DataModel and Language implementations for code generator #81

Merged
merged 5 commits into from
Feb 19, 2024

Conversation

desaikd
Copy link
Contributor

@desaikd desaikd commented Feb 7, 2024

Issues #76, #71, #69

Description of changes:

This PR works on modifying DataModel and Language implementations for code generator.

List of changes:

  • Modifies definition of DataModel
    • Renames DataModel to AbstractDataType
    • Adds parameter to Sequence enum variant to store data type information for the sequence
  • Adds Language trait for generator
    • Adds Language as trait insatead of enum
    • Adds RustLanguage and JavaLanguage as implementations of
      Language
    • Uses Language trait as generic for CodeGenerator
    • Moves read and write API generation code to templates
    • Reorganizes CodeGenerator to keep common behavior between languages
      in CodeGenerator<L>
    • Moves Rust related generation cod e in CodeGenerator<Rust>
    • Moves Java related generation cod e in CodeGenerator<Java>

Test

Tests for code generation in Rust and Java
(Note: This PR only includes reorganization and renaming changes)


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

* Renames `DataModel` to `AbstractDataType`
* Adds parameter to `Sequence` enum variant to store data type
information for the sequence
* Adds `Language` as trait insatead of enum
* Adds `RustLanguage` and `JavaLanguage` as implementations of
`Language`
* Uses `Language` trait as generic for `CodeGenerator`
* Moves read and write API generation code to templates
* Reorganizes `CodeGenerator` to keep common behavior between languages
in `CodeGenerator<L>`
* Moves Rust related generation cod e in `CodeGenerator<Rust>`
* Moves Java related generation cod e in `CodeGenerator<Java>`
// TODO: Make Sequence parameterized over data type.
// add a data type for sequence here that can be used to read elements for that data type.
Sequence, // a struct with a sequence/collection value (used for `element` constraint)
pub enum AbstractDataType {
Copy link
Contributor Author

@desaikd desaikd Feb 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(🗺️ PR tour) Renames DataModel to AbstractDataType. Also modifies Sequence enum variant to store information about sequence data type. (#69)

pub enum Language {
Rust,
Java,
pub trait Language {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(🗺️ PR tour) Adds a trait implementation of Language instead of an enum (#71). This trait will be implemented for different programming languages and can be used as a generic on CodeGenerator to trigger specific behavior based on programming language.

@@ -76,25 +171,15 @@ pub enum Template {
Struct, // Represents a template for a Rust struct or Java class
}

impl Template {
/// Returns a string that represent the template file name based on given programming language.
pub fn name(&self, language: &Language) -> &str {
Copy link
Contributor Author

@desaikd desaikd Feb 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(🗺️ PR tour)This has been moved on to template_as_string in JavaLanguage and RustLanguage.

@@ -116,26 +201,6 @@ pub enum IonSchemaType {
SchemaDefined(String), // A user defined schema type
}

impl IonSchemaType {
/// Maps the given ISL type name to a target type
pub fn target_type(&self, language: &Language) -> &str {
Copy link
Contributor Author

@desaikd desaikd Feb 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(🗺️ PR tour) This has been moved to target_type in JavaLanguage and RustLanguage.

tests/cli.rs Outdated
@@ -297,7 +297,7 @@ fn test_code_generation_in_rust(
let mut cmd = Command::cargo_bin("ion")?;
let temp_dir = TempDir::new()?;
let input_schema_path = temp_dir.path().join("test_schema.isl");
let mut input_schema_file = File::create(&input_schema_path)?;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(🗺️ PR tour)This file contains some clippy suggestions.

/// Generates Generates a read API for an abstract data type.
/// This adds statements for reading each the Ion value(s) that collectively represent the given abstract data type.
// TODO: add support for Java
fn generate_read_api(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(🗺️ PR tour) Both generate_read_api and generate_write_api have been removed as the code here is moved to the template itself.

code_gen_context: &mut CodeGenContext,
) -> CodeGenResult<String> {
Ok(match isl_type_ref {
IslTypeRef::Named(name, _) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(🗺️ PR tour) type_reference_name has been added that stores the type reference name and hence this method has been removed.

}

/// Generates an appropriately typed sequence in the target programming language to use as a field value
pub fn generate_sequence_field_value(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(🗺️ PR tour)generate_sequence_field_name has been moved to new language based implementation Language::target_type_as_sequence which returns sequence type based on programming language.

e.g.
target_type = "Foo" returns "ArrayList"
target_type = "Foo" returns "Vec"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RE: Language::target_type_as_sequence: is it true that for any language L, no matter what target_type is the return value will be the same?

Said another way, could we simplify this method to Language::sequence_type?

Having now read down to the implementation, I see that it's making a sequence with target_type as the type parameter. (ArrayList<Foo>, Vec<Foo>).

How about sequence_of_target_type?

)
}

fn render_generated_code(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(🗺️ PR tour) This method is just extracted from previous generate method for better modularization of the code.

/// For more information: <https://docs.rs/tera/1.19.0/tera/struct.Tera.html#method.register_filter>
///
/// [tera]: <https://docs.rs/tera/latest/tera/>
pub fn is_built_in_type(
Copy link
Contributor Author

@desaikd desaikd Feb 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(🗺️ PR tour) This is a filter method that is used in templates for read and write API generation. Previously the code to generate read and write API was inside CodeGenerator but now it has been moved to the template itself for ease of maintenance.

@desaikd desaikd marked this pull request as ready for review February 7, 2024 22:44
@desaikd desaikd requested a review from zslayton February 7, 2024 22:45
src/bin/ion/commands/beta/generate/context.rs Outdated Show resolved Hide resolved
// add a data type for sequence here that can be used to read elements for that data type.
Sequence, // a struct with a sequence/collection value (used for `element` constraint)
pub enum AbstractDataType {
// a struct with a scalar value (used for `type` constraint)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These comments and the Display impl below include a lot of ISL-specific details. The ADT is the middle ground between ISL and a target language, so I think ideally it wouldn't know about or mention either.

pub enum AbstractDataType {
  // A scalar value (e.g. a string or integer)
  Value,
  // A series of zero or more values whose type is described by the nested `String` (e.g. a list)
  Sequence(String)
  // A collection of field name/value pairs (e.g. a map)
  Struct
}

impl Display for AbstractDataType {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}",
            match self {
                AbstractDataType::Value => "scalar value",
                AbstractDataType::Sequence(_) => "sequence",
                AbstractDataType::Struct => "struct",
            }
        )
    }
}

Which ADT variant would be used for a user-defined data type that looked like this?

struct Point2D {
  x: f64,
  y: f64,
}

If it's Value, then we shouldn't describe it as "scalar". If it's Struct, then it would be good to mention "user defined types" as a use case for Struct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. This specific example would be struct but if we had a type definition like below:

type::{
  name: "id", 
  type: int
}

with following POJO representation:

struct Id {
    value: i64,
}

Then this would be Value. All the structs with a single field that represents the data type(e.g. integer) for that type definition(e.g. Id) will be under Value.

src/bin/ion/commands/beta/generate/generator.rs Outdated Show resolved Hide resolved
src/bin/ion/commands/beta/generate/generator.rs Outdated Show resolved Hide resolved
src/bin/ion/commands/beta/generate/generator.rs Outdated Show resolved Hide resolved
Java,
pub trait Language {
/// Provides a file extension based on programming language
fn file_extension() -> String;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like most (all?) of these -> String methods could be -> &str.

src/bin/ion/commands/beta/generate/utils.rs Outdated Show resolved Hide resolved
/// e.g.
/// Template<RustLanguage>::Struct -> "struct"
/// Template<JavaLanguage>::Class -> "class"
fn template_as_string(template: &Template) -> String;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a method on Template instead? (Template::name(&self) -> &str)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Note: The doc comment here is outdated as Template isn't really generic over Language)
Template is not actually generic over Language. I tried that approach but that leads to having two implementations of template_as_string for Template<RustLanguage> and Template<JavaLanguage> which means there is no Template<Language>.
Hence I have kept Language::template_as_string with all other language related config methods.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about naming it template_name(...) to communicate what the resulting String represents?

Float => "double",
Bool => "boolean",
Blob | Clob => "byte[]",
SchemaDefined(name) => name,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other types like Timestamp are TODOs, yeah?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, other types are TODOs.

src/bin/ion/commands/beta/generate/utils.rs Outdated Show resolved Hide resolved
* Adds new filter `snake` for templates
* Modifies `Import` to only contain name field (Template will use
appopriate casing filter to add an import statement based on `Import`)
* Adds doc comments changes
* Renames `Language#string_value()` to `name()`
* Modifies `Display` for `AbstractDataType`
Copy link
Contributor

@zslayton zslayton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few minor comments, but looks good

fn string_value() -> String;

/// Provides file name based on programming language standards
fn file_name(name: &str) -> String;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the input string is always, say, a target language type name, it would be good to communicate that expectation in the method name and/or the doc comment:

fn file_name_for_type(name: &str) -> String;

/// e.g.
/// Template<RustLanguage>::Struct -> "struct"
/// Template<JavaLanguage>::Class -> "class"
fn template_as_string(template: &Template) -> String;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about naming it template_name(...) to communicate what the resulting String represents?

src/bin/ion/commands/beta/generate/context.rs Show resolved Hide resolved
src/bin/ion/commands/beta/generate/generator.rs Outdated Show resolved Hide resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Development

Successfully merging this pull request may close these issues.

2 participants