-
Notifications
You must be signed in to change notification settings - Fork 15
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
Adds support for imports in code generation #169
Changes from all commits
64c837b
44c8178
2068065
edff0c7
a930c07
de4d075
e574f83
fc94da4
5ced8ad
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 |
---|---|---|
@@ -0,0 +1 @@ | ||
[ mango ] // expected apple, banana or strawberry, found mango |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
(apple banana) // expected list, found sexp |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// simple struct with type mismatched import field | ||
{ | ||
A: "hello", | ||
B: false, // expected field type symbol | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
[] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
[ apple, strawberry ] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// simple struct with all valid fields | ||
{ | ||
A: "hello", | ||
B: apple, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// simple struct with all valid fields | ||
{ | ||
A: "hello", | ||
// B: apple, // since `B` is an optional field, this is a valid struct | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// struct with unordered fields | ||
{ | ||
B: banana, | ||
A: "hello", | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
schema_header::{ | ||
imports: [ | ||
{ id: "utils/fruits.isl", type: fruits } | ||
] | ||
} | ||
|
||
type::{ | ||
name: sequence_with_import, | ||
type: list, | ||
element: fruits | ||
} | ||
|
||
schema_footer::{} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
type::{ | ||
name: struct_with_inline_import, | ||
type: struct, | ||
fields: { | ||
A: string, | ||
B: { id: "utils/fruits.isl", type: fruits } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
type::{ | ||
name: fruits, | ||
valid_values: [apple, banana, strawberry] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -277,30 +277,51 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { | |
schema_system: &mut SchemaSystem, | ||
) -> CodeGenResult<()> { | ||
for authority in authorities { | ||
// 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::<Result<Vec<_>, _>>()?; | ||
paths.sort_by_key(|dir| dir.path()); | ||
for schema_file in paths { | ||
let schema_file_path = schema_file.path(); | ||
let schema_id = schema_file_path.file_name().unwrap().to_str().unwrap(); | ||
|
||
let schema = schema_system.load_isl_schema(schema_id).unwrap(); | ||
|
||
self.generate(schema)?; | ||
} | ||
self.generate_code_for_directory(authority, None, schema_system)?; | ||
} | ||
Ok(()) | ||
} | ||
|
||
/// Generates code for given Ion Schema | ||
pub fn generate_code_for_schema( | ||
/// Helper method to generate code for all schema files in a directory | ||
/// `relative_path` is used to provide a relative path to the authority for a nested directory | ||
pub fn generate_code_for_directory<P: AsRef<Path>>( | ||
&mut self, | ||
directory: P, | ||
relative_path: Option<&str>, | ||
schema_system: &mut SchemaSystem, | ||
schema_id: &str, | ||
) -> CodeGenResult<()> { | ||
let schema = schema_system.load_isl_schema(schema_id).unwrap(); | ||
self.generate(schema) | ||
let paths = fs::read_dir(&directory)?.collect::<Result<Vec<_>, _>>()?; | ||
for schema_file in paths { | ||
let schema_file_path = schema_file.path(); | ||
|
||
// if this is a nested directory then load schema files from it | ||
if schema_file_path.is_dir() { | ||
self.generate_code_for_directory( | ||
&schema_file_path, | ||
Some( | ||
schema_file_path | ||
.strip_prefix(&directory) | ||
.unwrap() | ||
.to_str() | ||
.unwrap(), | ||
), | ||
schema_system, | ||
)?; | ||
} else { | ||
let schema = if let Some(path) = relative_path { | ||
let relative_path_with_schema_id = Path::new(path) | ||
.join(schema_file_path.file_name().unwrap().to_str().unwrap()); | ||
schema_system | ||
.load_isl_schema(relative_path_with_schema_id.as_path().to_str().unwrap()) | ||
} else { | ||
schema_system | ||
.load_isl_schema(schema_file_path.file_name().unwrap().to_str().unwrap()) | ||
}?; | ||
self.generate(schema)?; | ||
} | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
fn generate(&mut self, schema: IslSchema) -> CodeGenResult<()> { | ||
|
@@ -328,7 +349,6 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { | |
let isl_type_name = isl_type.name().clone().unwrap(); | ||
self.generate_abstract_data_type(&isl_type_name, isl_type)?; | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
|
@@ -597,24 +617,10 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { | |
type_name_suggestion: Option<&str>, | ||
) -> CodeGenResult<Option<FullyQualifiedTypeReference>> { | ||
Ok(match isl_type_ref { | ||
IslTypeRef::Named(name, _) => { | ||
let schema_type: IonSchemaType = name.into(); | ||
L::target_type(&schema_type) | ||
.as_ref() | ||
.map(|type_name| FullyQualifiedTypeReference { | ||
type_name: vec![NamespaceNode::Type(type_name.to_string())], | ||
parameters: vec![], | ||
}) | ||
.map(|t| { | ||
if field_presence == FieldPresence::Optional { | ||
L::target_type_as_optional(t) | ||
} else { | ||
t | ||
} | ||
}) | ||
} | ||
IslTypeRef::TypeImport(_, _) => { | ||
unimplemented!("Imports in schema are not supported yet!"); | ||
IslTypeRef::Named(name, _) => Self::target_type_for(field_presence, name), | ||
IslTypeRef::TypeImport(isl_import_type, _) => { | ||
let name = isl_import_type.type_name(); | ||
Self::target_type_for(field_presence, name) | ||
} | ||
IslTypeRef::Anonymous(type_def, _) => { | ||
let name = type_name_suggestion.map(|t| t.to_string()).ok_or( | ||
|
@@ -637,6 +643,27 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { | |
}) | ||
} | ||
|
||
/// Returns the target type based on given ISL type name and field presence | ||
fn target_type_for( | ||
field_presence: FieldPresence, | ||
name: &String, | ||
) -> Option<FullyQualifiedTypeReference> { | ||
let schema_type: IonSchemaType = name.into(); | ||
L::target_type(&schema_type) | ||
.as_ref() | ||
.map(|type_name| FullyQualifiedTypeReference { | ||
type_name: vec![NamespaceNode::Type(type_name.to_string())], | ||
parameters: vec![], | ||
}) | ||
Comment on lines
+654
to
+657
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 is only going to work if the target type is defined in the same namespace as the location from which it is referenced. If you have e.g. 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. Added a warning saying generated code uses a flattened namespace. |
||
.map(|t| { | ||
if field_presence == FieldPresence::Optional { | ||
L::target_type_as_optional(t) | ||
} else { | ||
t | ||
} | ||
}) | ||
} | ||
|
||
/// Returns error if duplicate constraints are present based `found_constraint` flag | ||
fn handle_duplicate_constraint( | ||
&mut self, | ||
|
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.
Can you explain the purpose of this particular change? Is it just a refactoring, or is there a technical reason why it was necessary?
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.
It allows traversing through sub directories of the authority. Just created separate method that I cna reuse for each directory getting traversed.