-
Notifications
You must be signed in to change notification settings - Fork 16
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
Changes from 1 commit
cc6a21c
6bd7644
948c8dd
ba59f35
d614692
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,7 +29,7 @@ impl<'a> CodeGenerator<'a, RustLanguage> { | |
Self { | ||
output, | ||
anonymous_type_counter: 0, | ||
tera: Tera::new("src/bin/ion/commands/beta/generate/templates/**/*.templ").unwrap(), | ||
tera: Tera::new("src/bin/ion/commands/beta/generate/templates/rust/*.templ").unwrap(), | ||
phantom: PhantomData, | ||
} | ||
} | ||
|
@@ -40,8 +40,9 @@ impl<'a> CodeGenerator<'a, RustLanguage> { | |
let mut modules = vec![]; | ||
let mut module_context = tera::Context::new(); | ||
|
||
// Register a tera filter that can be used to convert a string to upper camel case | ||
// Register a tera filter that can be used to convert a string based on case | ||
self.tera.register_filter("upper_camel", Self::upper_camel); | ||
self.tera.register_filter("snake", Self::snake); | ||
// Register a tera filter that can be used to see if a type is built in data type or not | ||
self.tera | ||
.register_filter("is_built_in_type", Self::is_built_in_type); | ||
|
@@ -61,7 +62,7 @@ impl<'a> CodeGenerator<'a, RustLanguage> { | |
module_context: &mut Context, | ||
) -> CodeGenResult<()> { | ||
module_context.insert("modules", &modules); | ||
let rendered = self.tera.render("rust/mod.templ", module_context)?; | ||
let rendered = self.tera.render("mod.templ", module_context)?; | ||
let mut file = File::create(self.output.join("mod.rs"))?; | ||
file.write_all(rendered.as_bytes())?; | ||
Ok(()) | ||
|
@@ -73,7 +74,7 @@ impl<'a> CodeGenerator<'a, JavaLanguage> { | |
Self { | ||
output, | ||
anonymous_type_counter: 0, | ||
tera: Tera::new("src/bin/ion/commands/beta/generate/templates/**/*.templ").unwrap(), | ||
tera: Tera::new("src/bin/ion/commands/beta/generate/templates/java/*.templ").unwrap(), | ||
phantom: PhantomData, | ||
} | ||
} | ||
|
@@ -83,7 +84,7 @@ impl<'a> CodeGenerator<'a, JavaLanguage> { | |
// this will be used for Rust to create mod.rs which lists all the generated modules | ||
let mut modules = vec![]; | ||
|
||
// Register a tera filter that can be used to convert a string to upper camel case | ||
// Register a tera filter that can be used to convert a string based on case | ||
self.tera.register_filter("upper_camel", Self::upper_camel); | ||
|
||
for isl_type in schema.types() { | ||
|
@@ -114,6 +115,25 @@ impl<'a, L: Language> CodeGenerator<'a, L> { | |
)) | ||
} | ||
|
||
/// Represents a [tera] filter that converts given tera string value to [snake case]. | ||
/// Returns error if the given value is not a string. | ||
/// | ||
/// For more information: <https://docs.rs/tera/1.19.0/tera/struct.Tera.html#method.register_filter> | ||
/// | ||
/// [tera]: <https://docs.rs/tera/latest/tera/> | ||
/// [upper camel case]: <https://docs.rs/convert_case/latest/convert_case/enum.Case.html#variant.Snake> | ||
pub fn snake( | ||
value: &tera::Value, | ||
_map: &HashMap<String, tera::Value>, | ||
) -> Result<tera::Value, tera::Error> { | ||
Ok(tera::Value::String( | ||
value | ||
.as_str() | ||
.ok_or(tera::Error::msg("Required string for this filter"))? | ||
desaikd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.to_case(Case::Snake), | ||
)) | ||
} | ||
|
||
/// Represents a [tera] filter that return true if the value is a built in type, otherwise returns false. | ||
/// | ||
/// For more information: <https://docs.rs/tera/1.19.0/tera/struct.Tera.html#method.register_filter> | ||
|
@@ -124,9 +144,9 @@ impl<'a, L: Language> CodeGenerator<'a, L> { | |
_map: &HashMap<String, tera::Value>, | ||
) -> Result<tera::Value, tera::Error> { | ||
Ok(tera::Value::Bool(L::is_built_in_type( | ||
value | ||
.as_str() | ||
.ok_or(tera::Error::msg("Required string for this filter"))?, | ||
value.as_str().ok_or(tera::Error::msg( | ||
desaikd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"`is_built_in_type` called with non-String Value", | ||
))?, | ||
))) | ||
} | ||
|
||
|
@@ -135,7 +155,7 @@ impl<'a, L: Language> CodeGenerator<'a, L> { | |
modules: &mut Vec<String>, | ||
isl_type: &IslType, | ||
) -> CodeGenResult<()> { | ||
let abstract_data_type_name = isl_type | ||
let isl_type_name = isl_type | ||
.name() | ||
.clone() | ||
.unwrap_or_else(|| format!("AnonymousType{}", self.anonymous_type_counter)); | ||
|
@@ -145,11 +165,8 @@ impl<'a, L: Language> CodeGenerator<'a, L> { | |
let mut imports: Vec<Import> = vec![]; | ||
let mut code_gen_context = CodeGenContext::new(); | ||
|
||
// Set the target kind name of the abstract data type (i.e. enum/class) | ||
context.insert( | ||
"target_kind_name", | ||
&abstract_data_type_name.to_case(Case::UpperCamel), | ||
); | ||
// Set the ISL type name for the generated abstract data type | ||
context.insert("target_kind_name", &isl_type_name.to_case(Case::UpperCamel)); | ||
|
||
let constraints = isl_type.constraints(); | ||
for constraint in constraints { | ||
|
@@ -166,29 +183,16 @@ impl<'a, L: Language> CodeGenerator<'a, L> { | |
context.insert("imports", &imports); | ||
|
||
// add fields for template | ||
if code_gen_context.abstract_data_type == Some(AbstractDataType::Struct) | ||
|| code_gen_context.abstract_data_type == Some(AbstractDataType::Value) | ||
|| matches!( | ||
code_gen_context.abstract_data_type, | ||
Some(AbstractDataType::Sequence(_)) | ||
) | ||
{ | ||
if let Some(abstract_data_type) = &code_gen_context.abstract_data_type { | ||
context.insert("fields", &tera_fields); | ||
if let Some(abstract_data_type) = &code_gen_context.abstract_data_type { | ||
context.insert("abstract_data_type", abstract_data_type); | ||
} else { | ||
return invalid_abstract_data_type_error( | ||
context.insert("abstract_data_type", abstract_data_type); | ||
} else { | ||
return invalid_abstract_data_type_error( | ||
"Can not determine abstract data type, constraints are mapping not mapping to an abstract data type.", | ||
); | ||
} | ||
} | ||
|
||
self.render_generated_code( | ||
modules, | ||
&abstract_data_type_name, | ||
&mut context, | ||
&mut code_gen_context, | ||
) | ||
self.render_generated_code(modules, &isl_type_name, &mut context, &mut code_gen_context) | ||
} | ||
|
||
fn render_generated_code( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (🗺️ PR tour) This method is just extracted from previous |
||
|
@@ -205,11 +209,7 @@ impl<'a, L: Language> CodeGenerator<'a, L> { | |
let rendered = self | ||
.tera | ||
.render( | ||
&format!( | ||
"{}/{}.templ", | ||
L::string_value(), | ||
L::template_as_string(template) | ||
), | ||
&format!("{}.templ", L::template_as_string(template)), | ||
context, | ||
) | ||
.unwrap(); | ||
|
@@ -233,8 +233,7 @@ impl<'a, L: Language> CodeGenerator<'a, L> { | |
IslTypeRef::Named(name, _) => { | ||
if !L::is_built_in_type(name) { | ||
imports.push(Import { | ||
module_name: name.to_case(Case::Snake), | ||
type_name: name.to_case(Case::UpperCamel), | ||
name: name.to_string(), | ||
}); | ||
} | ||
let schema_type: IonSchemaType = name.into(); | ||
|
@@ -248,8 +247,7 @@ impl<'a, L: Language> CodeGenerator<'a, L> { | |
self.generate_abstract_data_type(modules, type_def)?; | ||
let name = format!("AnonymousType{}", self.anonymous_type_counter); | ||
imports.push(Import { | ||
module_name: name.to_case(Case::Snake), | ||
type_name: name.to_case(Case::UpperCamel), | ||
name: name.to_string(), | ||
}); | ||
name | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,25 +12,27 @@ pub struct Field { | |
pub(crate) value: String, | ||
} | ||
|
||
/// Represents an import statement in a module file. | ||
/// Represents an import in a generated code file. | ||
/// This will be used by template engine to fill import statements of a type definition. | ||
#[derive(Serialize)] | ||
pub struct Import { | ||
pub(crate) module_name: String, | ||
pub(crate) type_name: String, | ||
pub(crate) name: String, | ||
} | ||
|
||
pub trait Language { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (🗺️ PR tour) Adds a trait implementation of |
||
/// Provides a file extension based on programming language | ||
fn file_extension() -> String; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like most (all?) of these |
||
|
||
/// Returns string representation of programming language | ||
fn string_value() -> String; | ||
/// Returns string representation of programming language name | ||
fn name() -> String; | ||
|
||
/// Provides file name based on programming language standards | ||
/// Provides generated code's file name for given `name` based on programming language standards | ||
/// e.g. | ||
/// In Rust, this will return a string casing `name` to [Case::Snake]. | ||
/// In Java, this will return a string casing `name` to [Case::UpperCamel] | ||
fn file_name(name: &str) -> String; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the input There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method basically converts the given Below is a possible comment modification: /// Provides generated code's file name for given `name` based on programming language standards
/// e.g.
/// In Rust, this will return a string casing `name` to [Case::Snake].
/// In Java, this will return a string casing `name` to [Case::UpperCamel]
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; |
||
|
||
/// Maps the given ISL type name to a target type | ||
/// Maps the given ISL type to a target type name | ||
fn target_type(ion_schema_type: &IonSchemaType) -> String; | ||
|
||
/// Provides given target type as sequence | ||
|
@@ -45,13 +47,13 @@ pub trait Language { | |
/// Java field name case -> [Case::Camel] | ||
fn field_name_case() -> Case; | ||
|
||
/// Returns true if its a built in type otherwise returns false | ||
/// Returns true if the type name specified is provided by the target language implementation | ||
fn is_built_in_type(name: &str) -> bool; | ||
|
||
/// Returns the template as string based on programming language | ||
/// e.g. | ||
/// Template<RustLanguage>::Struct -> "struct" | ||
/// Template<JavaLanguage>::Class -> "class" | ||
/// In Rust, Template::Struct -> "struct" | ||
/// In Java, Template::Struct -> "class" | ||
fn template_as_string(template: &Template) -> String; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be a method on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Note: The doc comment here is outdated as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about naming it |
||
} | ||
|
||
|
@@ -62,7 +64,7 @@ impl Language for JavaLanguage { | |
"java".to_string() | ||
} | ||
|
||
fn string_value() -> String { | ||
fn name() -> String { | ||
"java".to_string() | ||
} | ||
|
||
|
@@ -115,7 +117,7 @@ impl Language for RustLanguage { | |
"rs".to_string() | ||
} | ||
|
||
fn string_value() -> String { | ||
fn name() -> String { | ||
"rust".to_string() | ||
} | ||
|
||
|
@@ -176,9 +178,7 @@ impl TryFrom<Option<&AbstractDataType>> for Template { | |
|
||
fn try_from(value: Option<&AbstractDataType>) -> Result<Self, Self::Error> { | ||
match value { | ||
Some(AbstractDataType::Value) | ||
| Some(AbstractDataType::Sequence(_)) | ||
| Some(AbstractDataType::Struct) => Ok(Template::Struct), | ||
Some(_) => Ok(Template::Struct), | ||
None => invalid_abstract_data_type_error( | ||
"Can not get a template without determining data model first.", | ||
), | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(🗺️ PR tour) Renames
DataModel
toAbstractDataType
. Also modifiesSequence
enum variant to store information about sequence data type. (#69)