From ffb1d0cb7409b6bbb512084737e77dc73b536e63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Bo=CC=88hm?= Date: Fri, 24 Jan 2020 12:48:04 +0100 Subject: [PATCH 01/14] addition of back relation fields are not invisible in printing anymore --- libs/datamodel/core/src/validator/standardise.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/datamodel/core/src/validator/standardise.rs b/libs/datamodel/core/src/validator/standardise.rs index 638b533bc557..6998931527e9 100644 --- a/libs/datamodel/core/src/validator/standardise.rs +++ b/libs/datamodel/core/src/validator/standardise.rs @@ -248,7 +248,7 @@ impl Standardiser { .find_model_mut(&missing_back_relation_field.model) .expect(STATE_ERROR); - let mut back_relation_field = dml::Field::new_generated( + let mut back_relation_field = dml::Field::new( &field_name, dml::FieldType::Relation(missing_back_relation_field.relation_info), ); From d1aeab2f0b167c9c557d31b3c7dc7676a2aff4fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Bo=CC=88hm?= Date: Fri, 24 Jan 2020 12:49:37 +0100 Subject: [PATCH 02/14] @relation serialization: only hide references argument when it is empty --- libs/datamodel/core/src/validator/directive/core/relation.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libs/datamodel/core/src/validator/directive/core/relation.rs b/libs/datamodel/core/src/validator/directive/core/relation.rs index 4c3ac62a2888..f0c870a80692 100644 --- a/libs/datamodel/core/src/validator/directive/core/relation.rs +++ b/libs/datamodel/core/src/validator/directive/core/relation.rs @@ -66,10 +66,7 @@ impl DirectiveValidator for RelationDirectiveValidator { relation_fields.sort(); all_related_ids.sort(); - if !relation_info.to_fields.is_empty() - && relation_fields != all_related_ids - && parent_model.name < related_model.name - { + if !relation_info.to_fields.is_empty() { let mut related_fields: Vec = Vec::new(); for related_field in &relation_info.to_fields { related_fields.push(ast::Expression::ConstantValue( From 2ee1029f1378139ce5408f96aa5faf285cb7ba88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Bo=CC=88hm?= Date: Fri, 24 Jan 2020 13:19:10 +0100 Subject: [PATCH 03/14] Reformatter now uses dml layer instead of ast only --- libs/datamodel/core/src/ast/reformat/mod.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libs/datamodel/core/src/ast/reformat/mod.rs b/libs/datamodel/core/src/ast/reformat/mod.rs index da8dcb9ba493..13ae9a972e33 100644 --- a/libs/datamodel/core/src/ast/reformat/mod.rs +++ b/libs/datamodel/core/src/ast/reformat/mod.rs @@ -34,9 +34,12 @@ fn comment(target: &mut dyn LineWriteable, comment_text: &str) { impl Reformatter { pub fn reformat_to(input: &str, output: &mut dyn std::io::Write, ident_width: usize) { - let mut ast = PrismaDatamodelParser::parse(Rule::datamodel, input).unwrap(); // TODO: Handle error. - let mut top_formatter = RefCell::new(Renderer::new(output, ident_width)); - Self::reformat_top(&mut top_formatter, &ast.next().unwrap()); + let dml = crate::parse_datamodel(&input).unwrap(); + // dbg!(&dml); + // let mut ast = PrismaDatamodelParser::parse(Rule::datamodel, input).unwrap(); // TODO: Handle error. + // let mut top_formatter = RefCell::new(Renderer::new(output, ident_width)); + // Self::reformat_top(&mut top_formatter, &ast.next().unwrap()); + crate::render_datamodel_to(output, &dml).unwrap(); } fn reformat_top(target: &mut RefCell, token: &Token) { From d715054ae09fc69c5e20ac944a412a07d1c2e948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Bo=CC=88hm?= Date: Fri, 24 Jan 2020 13:21:48 +0100 Subject: [PATCH 04/14] Unify comment types. There is only one comment type now. --- .../core/src/ast/parser/datamodel.pest | 4 +- libs/datamodel/core/src/ast/parser/mod.rs | 1 - libs/datamodel/core/src/ast/reformat/mod.rs | 1005 ++++++++--------- libs/datamodel/core/src/lib.rs | 5 +- 4 files changed, 505 insertions(+), 510 deletions(-) diff --git a/libs/datamodel/core/src/ast/parser/datamodel.pest b/libs/datamodel/core/src/ast/parser/datamodel.pest index a6d777d5df66..9b42640aa224 100644 --- a/libs/datamodel/core/src/ast/parser/datamodel.pest +++ b/libs/datamodel/core/src/ast/parser/datamodel.pest @@ -12,8 +12,6 @@ // Treat every whitespace the same for now. WHITESPACE = @{ SPACE_SEPARATOR | "\t" | NEWLINE } -// Comment ignores everything until end of line. -COMMENT = @{ (!"///") ~ "//" ~ (!NEWLINE ~ ANY)* ~ NEWLINE? } BLOCK_OPEN = @{ "{" } BLOCK_CLOSE = @{ "}" } MODEL_KEYWORD = @{ "model" } @@ -28,7 +26,7 @@ INTERPOLATION_END = { "}" } LEGACY_COLON = { ":" } doc_content = @{ (!NEWLINE ~ ANY)* ~ NEWLINE } -doc_comment = { "///" ~ doc_content } +doc_comment = { "//" ~ doc_content } // ###################################### // Base building blocks diff --git a/libs/datamodel/core/src/ast/parser/mod.rs b/libs/datamodel/core/src/ast/parser/mod.rs index bc5d72afd223..23d3f161e9e5 100644 --- a/libs/datamodel/core/src/ast/parser/mod.rs +++ b/libs/datamodel/core/src/ast/parser/mod.rs @@ -540,6 +540,5 @@ fn rule_to_string(rule: Rule) -> &'static str { Rule::boolean_true => "boolean true", Rule::boolean_false => "boolean false", Rule::doc_content => "documentation comment content", - Rule::COMMENT => "", } } diff --git a/libs/datamodel/core/src/ast/reformat/mod.rs b/libs/datamodel/core/src/ast/reformat/mod.rs index 13ae9a972e33..62f97ea9a1d1 100644 --- a/libs/datamodel/core/src/ast/reformat/mod.rs +++ b/libs/datamodel/core/src/ast/reformat/mod.rs @@ -42,507 +42,506 @@ impl Reformatter { crate::render_datamodel_to(output, &dml).unwrap(); } - fn reformat_top(target: &mut RefCell, token: &Token) { - let mut types_table = TableFormat::new(); - let mut types_mode = false; - - for current in token.clone().into_inner() { - match current.as_rule() { - Rule::WHITESPACE => {} - Rule::COMMENT => {} - Rule::type_declaration => { - types_mode = true; - } - _ => { - if types_mode { - types_mode = false; - // For all other ones, reset types_table. - types_table.render(target.get_mut()); - types_table = TableFormat::new(); - target.get_mut().maybe_end_line(); - } - } - }; - match current.as_rule() { - Rule::WHITESPACE => { - if types_mode { - let lines = count_lines(current.as_str()); - - if lines > 1 || (lines == 1 && types_table.line_empty()) { - // Reset the table layout on more than one newline. - types_table.render(target.get_mut()); - types_table = TableFormat::new(); - } - - newlines(&mut types_table, current.as_str(), "m"); - } else { - newlines(target.get_mut(), current.as_str(), "d") - } - } - Rule::COMMENT => { - if types_mode { - comment(&mut types_table.interleave_writer(), current.as_str()); - } else { - comment(target.get_mut(), current.as_str()); - } - } - Rule::model_declaration => Self::reformat_model(target, ¤t), - Rule::enum_declaration => Self::reformat_enum(target, ¤t), - Rule::source_block => Self::reformat_config_block(target.get_mut(), ¤t), - Rule::generator_block => Self::reformat_config_block(target.get_mut(), ¤t), - Rule::type_declaration => { - if !types_mode { - panic!("Renderer not in type mode."); - } - Self::reformat_type_declaration(&mut types_table, ¤t); - } - Rule::EOI => {} - _ => unreachable!( - "Encounterd impossible datamodel declaration during parsing: {:?}", - current.tokens() - ), - } - } - } - - fn reformat_config_block(target: &mut Renderer, token: &Token) { - let mut table = TableFormat::new(); - // Switch to skip whitespace in 'datasource xxxx {' - let mut skip_whitespace = false; - - for current in token.clone().into_inner() { - match current.as_rule() { - Rule::GENERATOR_KEYWORD => { - skip_whitespace = true; - target.write("generator "); - } - Rule::DATASOURCE_KEYWORD => { - skip_whitespace = true; - target.write("datasource "); - } - Rule::BLOCK_OPEN => { - skip_whitespace = false; - target.write(" {"); - target.maybe_end_line(); - target.indent_up(); - } - Rule::BLOCK_CLOSE => {} - Rule::identifier => target.write(current.as_str()), - Rule::key_value => Self::reformat_key_value(&mut table, ¤t), - Rule::doc_comment => comment(target, current.as_str()), - Rule::WHITESPACE => { - if !skip_whitespace { - // TODO: This is duplicate. - let lines = count_lines(current.as_str()); - - if lines > 1 || (lines == 1 && table.line_empty()) { - // Reset the table layout on more than one newline. - table.render(target); - table = TableFormat::new(); - } - - newlines(&mut table, current.as_str(), "m"); - } - } - Rule::COMMENT => comment(&mut table.interleave_writer(), current.as_str()), - _ => unreachable!( - "Encounterd impossible source declaration during parsing: {:?}", - current.tokens() - ), - }; - } - - table.render(target); - target.indent_down(); - target.write("}"); - target.maybe_end_line(); - target.maybe_end_line(); - } - - fn reformat_key_value(target: &mut TableFormat, token: &Token) { - for current in token.clone().into_inner() { - match current.as_rule() { - Rule::identifier => { - target.write(current.as_str()); - target.write("="); - } - Rule::expression => { - Self::reformat_expression(&mut target.column_locked_writer_for(2), ¤t); - } - Rule::WHITESPACE => {} - Rule::COMMENT => panic!("Comments inside config key/value not supported yet."), - _ => unreachable!( - "Encounterd impossible source property declaration during parsing: {:?}", - current.tokens() - ), - } - } - } - - fn reformat_model(target: &mut RefCell, token: &Token) { - let mut table = RefCell::new(TableFormat::new()); - // Switch to skip whitespace in 'model xxxx {' - let mut skip_whitespace = false; - - for current in token.clone().into_inner() { - match current.as_rule() { - Rule::MODEL_KEYWORD => { - skip_whitespace = true; - } - Rule::BLOCK_OPEN => { - skip_whitespace = false; - } - Rule::BLOCK_CLOSE => {} - - Rule::identifier => { - // Begin. - target.get_mut().write(&format!("model {} {{", current.as_str())); - target.get_mut().maybe_end_line(); - target.get_mut().indent_up(); - } - Rule::directive => { - // Directives reset the table. - table.get_mut().render(target.get_mut()); - table = RefCell::new(TableFormat::new()); - Self::reformat_directive(target.get_mut(), ¤t, "@@"); - } - Rule::field_declaration => Self::reformat_field(&mut table, ¤t), - // Doc comments are to be placed OUTSIDE of table block. - Rule::doc_comment => comment(target.get_mut(), current.as_str()), - Rule::WHITESPACE => { - if !skip_whitespace { - let lines = count_lines(current.as_str()); - - if lines > 1 || (lines == 1 && table.get_mut().line_empty()) { - // Reset the table layout on more than one newline. - table.get_mut().render(target.get_mut()); - table = RefCell::new(TableFormat::new()); - } - - newlines(table.get_mut(), current.as_str(), "m"); - } - } - Rule::COMMENT => comment(&mut table.get_mut().interleave_writer(), current.as_str()), - _ => unreachable!( - "Encounterd impossible model declaration during parsing: {:?}", - current.tokens() - ), - } - } - - // End. - table.get_mut().render(target.get_mut()); - target.get_mut().indent_down(); - target.get_mut().write("}"); - target.get_mut().maybe_end_line(); - target.get_mut().maybe_end_line(); - } - - // TODO: This is very similar to model reformating. - fn reformat_enum(target: &mut RefCell, token: &Token) { - let mut table = TableFormat::new(); - // Switch to skip whitespace in 'enum xxxx {' - let mut skip_whitespace = false; - - for current in token.clone().into_inner() { - match current.as_rule() { - Rule::ENUM_KEYWORD => { - skip_whitespace = true; - } - Rule::BLOCK_OPEN => { - skip_whitespace = false; - } - Rule::BLOCK_CLOSE => {} - - Rule::identifier => { - // Begin. - target.get_mut().write(&format!("enum {} {{", current.as_str())); - target.get_mut().maybe_end_line(); - target.get_mut().indent_up(); - } - Rule::directive => { - table.render(target.get_mut()); - table = TableFormat::new(); - Self::reformat_directive(target.get_mut(), ¤t, "@@"); - } - Rule::enum_field_declaration => table.write(current.as_str()), - // Doc comments are to be placed OUTSIDE of table block. - Rule::doc_comment => comment(target.get_mut(), current.as_str()), - Rule::WHITESPACE => { - if !skip_whitespace { - let lines = count_lines(current.as_str()); - - if lines > 1 || (lines == 1 && table.line_empty()) { - // Reset the table layout on more than one newline. - table.render(target.get_mut()); - table = TableFormat::new(); - } - - newlines(&mut table, current.as_str(), "m"); - } - } - Rule::COMMENT => comment(&mut table.interleave_writer(), current.as_str()), - _ => unreachable!( - "Encounterd impossible enum declaration during parsing: {:?}", - current.tokens() - ), - } - } - - // End. - table.render(target.get_mut()); - target.get_mut().indent_down(); - target.get_mut().write("}"); - target.get_mut().maybe_end_line(); - target.get_mut().maybe_end_line(); - } - - fn reformat_field(target: &mut RefCell, token: &Token) { - let mut identifier = None; - let mut directives_started = false; - - for current in token.clone().into_inner() { - match current.as_rule() { - Rule::identifier => identifier = Some(String::from(current.as_str())), - Rule::field_type => { - target - .get_mut() - .write(&identifier.clone().expect("Unknown field identifier.")); - target.get_mut().write(&Self::reformat_field_type(¤t)); - } - Rule::directive => { - directives_started = true; - Self::reformat_directive(&mut target.get_mut().column_locked_writer_for(2), ¤t, "@") - } - Rule::doc_comment => comment(&mut target.get_mut().interleave_writer(), current.as_str()), - Rule::COMMENT => { - if directives_started { - comment(&mut target.get_mut().column_locked_writer_for(2), current.as_str()); - } else { - comment(target.get_mut(), current.as_str()); - } - } - Rule::WHITESPACE => newlines(target.get_mut(), current.as_str(), "f"), - _ => unreachable!("Encounterd impossible field during parsing: {:?}", current.tokens()), - } - } - - target.get_mut().maybe_end_line(); - } - - fn reformat_type_declaration(target: &mut TableFormat, token: &Token) { - let mut identifier = None; - let mut directives_started = false; - - for current in token.clone().into_inner() { - match current.as_rule() { - Rule::TYPE_KEYWORD => {} - Rule::identifier => identifier = Some(String::from(current.as_str())), - Rule::base_type => { - target.write("type"); - target.write(&identifier.clone().expect("Unknown field identifier.")); - target.write("="); - target.write(&Self::get_identifier(¤t)); - } - Rule::directive => { - directives_started = true; - Self::reformat_directive(&mut target.column_locked_writer_for(4), ¤t, "@"); - } - Rule::doc_comment => comment(&mut target.interleave_writer(), current.as_str()), - Rule::COMMENT => { - if directives_started { - comment(&mut target.column_locked_writer_for(4), current.as_str()); - } else { - comment(&mut target.interleave_writer(), current.as_str()); - } - } - Rule::WHITESPACE => newlines(target, current.as_str(), "t"), - _ => unreachable!( - "Encounterd impossible custom type during parsing: {:?}", - current.tokens() - ), - } - } - - target.maybe_end_line(); - } - - fn reformat_field_type(token: &Token) -> String { - let mut builder = StringBuilder::new(); - - for current in token.clone().into_inner() { - builder.write(&Self::get_identifier(¤t)); - match current.as_rule() { - Rule::optional_type => builder.write("?"), - Rule::base_type => {} - Rule::list_type => builder.write("[]"), - _ => unreachable!( - "Encounterd impossible field type during parsing: {:?}", - current.tokens() - ), - } - } - - builder.to_string() - } - - fn get_identifier(token: &Token) -> String { - for current in token.clone().into_inner() { - if let Rule::identifier = current.as_rule() { - return current.as_str().to_string(); - } - } - - panic!("No identifier found.") - } - - fn reformat_directive(target: &mut dyn LineWriteable, token: &Token, owl: &str) { - for current in token.clone().into_inner() { - match current.as_rule() { - Rule::directive_name => { - // Begin - if !target.line_empty() { - target.write(" "); - } - target.write(owl); - target.write(current.as_str()); - } - Rule::WHITESPACE => {} - Rule::COMMENT => panic!("Comments inside attributes not supported yet."), - Rule::directive_arguments => Self::reformat_directive_args(target, ¤t), - _ => unreachable!("Encounterd impossible directive during parsing: {:?}", current.tokens()), - } - } - } - - fn reformat_directive_args(target: &mut dyn LineWriteable, token: &Token) { - let mut builder = StringBuilder::new(); - - for current in token.clone().into_inner() { - match current.as_rule() { - // This is a named arg. - Rule::argument => { - if !builder.line_empty() { - builder.write(", "); - } - Self::reformat_directive_arg(&mut builder, ¤t); - } - // This is a an unnamed arg. - Rule::argument_value => { - if !builder.line_empty() { - builder.write(", "); - } - Self::reformat_arg_value(&mut builder, ¤t); - } - Rule::WHITESPACE => {} - Rule::COMMENT => panic!("Comments inside attribute argument list not supported yet."), - _ => unreachable!( - "Encounterd impossible directive argument list during parsing: {:?}", - current.tokens() - ), - }; - } - - if !builder.line_empty() { - target.write("("); - target.write(&builder.to_string()); - target.write(")"); - } - } - - fn reformat_directive_arg(target: &mut dyn LineWriteable, token: &Token) { - for current in token.clone().into_inner() { - match current.as_rule() { - Rule::argument_name => { - target.write(current.as_str()); - target.write(": "); - } - Rule::argument_value => Self::reformat_arg_value(target, ¤t), - Rule::WHITESPACE => {} - Rule::COMMENT => panic!("Comments inside attribute argument not supported yet."), - _ => unreachable!( - "Encounterd impossible directive argument during parsing: {:?}", - current.tokens() - ), - }; - } - } - - fn reformat_arg_value(target: &mut dyn LineWriteable, token: &Token) { - for current in token.clone().into_inner() { - match current.as_rule() { - Rule::expression => Self::reformat_expression(target, ¤t), - Rule::WHITESPACE => {} - Rule::COMMENT => panic!("Comments inside attributes not supported yet."), - _ => unreachable!( - "Encounterd impossible argument value during parsing: {:?}", - current.tokens() - ), - }; - } - } - - /// Parses an expression, given a Pest parser token. - fn reformat_expression(target: &mut dyn LineWriteable, token: &Token) { - for current in token.clone().into_inner() { - match current.as_rule() { - Rule::numeric_literal => target.write(current.as_str()), - Rule::string_literal => target.write(current.as_str()), - Rule::boolean_literal => target.write(current.as_str()), - Rule::constant_literal => target.write(current.as_str()), - Rule::function => Self::reformat_function_expression(target, ¤t), - Rule::array_expression => Self::reformat_array_expression(target, ¤t), - Rule::WHITESPACE => {} - Rule::COMMENT => panic!("Comments inside expressions not supported yet."), - _ => unreachable!("Encounterd impossible literal during parsing: {:?}", current.tokens()), - } - } - } - - fn reformat_array_expression(target: &mut dyn LineWriteable, token: &Token) { - target.write("["); - let mut expr_count = 0; - - for current in token.clone().into_inner() { - match current.as_rule() { - Rule::expression => { - if expr_count > 0 { - target.write(", "); - } - Self::reformat_expression(target, ¤t); - expr_count += 1; - } - Rule::WHITESPACE => {} - Rule::COMMENT => panic!("Comments inside expressions not supported yet."), - _ => unreachable!("Encounterd impossible array during parsing: {:?}", current.tokens()), - } - } - - target.write("]"); - } - - fn reformat_function_expression(target: &mut dyn LineWriteable, token: &Token) { - let mut expr_count = 0; - - for current in token.clone().into_inner() { - match current.as_rule() { - Rule::identifier => { - target.write(current.as_str()); - target.write("("); - } - Rule::argument_value => { - if expr_count > 0 { - target.write(", "); - } - Self::reformat_arg_value(target, ¤t); - expr_count += 1; - } - Rule::WHITESPACE => {} - Rule::COMMENT => panic!("Comments inside expressions not supported yet."), - _ => unreachable!("Encounterd impossible function during parsing: {:?}", current.tokens()), - } - } - - target.write(")"); - } + // fn reformat_top(target: &mut RefCell, token: &Token) { + // let mut types_table = TableFormat::new(); + // let mut types_mode = false; + // + // for current in token.clone().into_inner() { + // match current.as_rule() { + // Rule::WHITESPACE => {} + // Rule::type_declaration => { + // types_mode = true; + // } + // _ => { + // if types_mode { + // types_mode = false; + // // For all other ones, reset types_table. + // types_table.render(target.get_mut()); + // types_table = TableFormat::new(); + // target.get_mut().maybe_end_line(); + // } + // } + // }; + // match current.as_rule() { + // Rule::WHITESPACE => { + // if types_mode { + // let lines = count_lines(current.as_str()); + // + // if lines > 1 || (lines == 1 && types_table.line_empty()) { + // // Reset the table layout on more than one newline. + // types_table.render(target.get_mut()); + // types_table = TableFormat::new(); + // } + // + // newlines(&mut types_table, current.as_str(), "m"); + // } else { + // newlines(target.get_mut(), current.as_str(), "d") + // } + // } + // // Rule::COMMENT => { + // // if types_mode { + // // comment(&mut types_table.interleave_writer(), current.as_str()); + // // } else { + // // comment(target.get_mut(), current.as_str()); + // // } + // // } + // Rule::model_declaration => Self::reformat_model(target, ¤t), + // Rule::enum_declaration => Self::reformat_enum(target, ¤t), + // Rule::source_block => Self::reformat_config_block(target.get_mut(), ¤t), + // Rule::generator_block => Self::reformat_config_block(target.get_mut(), ¤t), + // Rule::type_declaration => { + // if !types_mode { + // panic!("Renderer not in type mode."); + // } + // Self::reformat_type_declaration(&mut types_table, ¤t); + // } + // Rule::EOI => {} + // _ => unreachable!( + // "Encounterd impossible datamodel declaration during parsing: {:?}", + // current.tokens() + // ), + // } + // } + // } + // + // fn reformat_config_block(target: &mut Renderer, token: &Token) { + // let mut table = TableFormat::new(); + // // Switch to skip whitespace in 'datasource xxxx {' + // let mut skip_whitespace = false; + // + // for current in token.clone().into_inner() { + // match current.as_rule() { + // Rule::GENERATOR_KEYWORD => { + // skip_whitespace = true; + // target.write("generator "); + // } + // Rule::DATASOURCE_KEYWORD => { + // skip_whitespace = true; + // target.write("datasource "); + // } + // Rule::BLOCK_OPEN => { + // skip_whitespace = false; + // target.write(" {"); + // target.maybe_end_line(); + // target.indent_up(); + // } + // Rule::BLOCK_CLOSE => {} + // Rule::identifier => target.write(current.as_str()), + // Rule::key_value => Self::reformat_key_value(&mut table, ¤t), + // Rule::doc_comment => comment(target, current.as_str()), + // Rule::WHITESPACE => { + // if !skip_whitespace { + // // TODO: This is duplicate. + // let lines = count_lines(current.as_str()); + // + // if lines > 1 || (lines == 1 && table.line_empty()) { + // // Reset the table layout on more than one newline. + // table.render(target); + // table = TableFormat::new(); + // } + // + // newlines(&mut table, current.as_str(), "m"); + // } + // } + //// Rule::COMMENT => comment(&mut table.interleave_writer(), current.as_str()), + //// _ => unreachable!( + //// "Encounterd impossible source declaration during parsing: {:?}", + //// current.tokens() + //// ), + // }; + // } + // + // table.render(target); + // target.indent_down(); + // target.write("}"); + // target.maybe_end_line(); + // target.maybe_end_line(); + // } + // + // fn reformat_key_value(target: &mut TableFormat, token: &Token) { + // for current in token.clone().into_inner() { + // match current.as_rule() { + // Rule::identifier => { + // target.write(current.as_str()); + // target.write("="); + // } + // Rule::expression => { + // Self::reformat_expression(&mut target.column_locked_writer_for(2), ¤t); + // } + // Rule::WHITESPACE => {} + // Rule::COMMENT => panic!("Comments inside config key/value not supported yet."), + // _ => unreachable!( + // "Encounterd impossible source property declaration during parsing: {:?}", + // current.tokens() + // ), + // } + // } + // } + // + // fn reformat_model(target: &mut RefCell, token: &Token) { + // let mut table = RefCell::new(TableFormat::new()); + // // Switch to skip whitespace in 'model xxxx {' + // let mut skip_whitespace = false; + // + // for current in token.clone().into_inner() { + // match current.as_rule() { + // Rule::MODEL_KEYWORD => { + // skip_whitespace = true; + // } + // Rule::BLOCK_OPEN => { + // skip_whitespace = false; + // } + // Rule::BLOCK_CLOSE => {} + // + // Rule::identifier => { + // // Begin. + // target.get_mut().write(&format!("model {} {{", current.as_str())); + // target.get_mut().maybe_end_line(); + // target.get_mut().indent_up(); + // } + // Rule::directive => { + // // Directives reset the table. + // table.get_mut().render(target.get_mut()); + // table = RefCell::new(TableFormat::new()); + // Self::reformat_directive(target.get_mut(), ¤t, "@@"); + // } + // Rule::field_declaration => Self::reformat_field(&mut table, ¤t), + // // Doc comments are to be placed OUTSIDE of table block. + // Rule::doc_comment => comment(target.get_mut(), current.as_str()), + // Rule::WHITESPACE => { + // if !skip_whitespace { + // let lines = count_lines(current.as_str()); + // + // if lines > 1 || (lines == 1 && table.get_mut().line_empty()) { + // // Reset the table layout on more than one newline. + // table.get_mut().render(target.get_mut()); + // table = RefCell::new(TableFormat::new()); + // } + // + // newlines(table.get_mut(), current.as_str(), "m"); + // } + // } + // Rule::COMMENT => comment(&mut table.get_mut().interleave_writer(), current.as_str()), + // _ => unreachable!( + // "Encounterd impossible model declaration during parsing: {:?}", + // current.tokens() + // ), + // } + // } + // + // // End. + // table.get_mut().render(target.get_mut()); + // target.get_mut().indent_down(); + // target.get_mut().write("}"); + // target.get_mut().maybe_end_line(); + // target.get_mut().maybe_end_line(); + // } + // + // // TODO: This is very similar to model reformating. + // fn reformat_enum(target: &mut RefCell, token: &Token) { + // let mut table = TableFormat::new(); + // // Switch to skip whitespace in 'enum xxxx {' + // let mut skip_whitespace = false; + // + // for current in token.clone().into_inner() { + // match current.as_rule() { + // Rule::ENUM_KEYWORD => { + // skip_whitespace = true; + // } + // Rule::BLOCK_OPEN => { + // skip_whitespace = false; + // } + // Rule::BLOCK_CLOSE => {} + // + // Rule::identifier => { + // // Begin. + // target.get_mut().write(&format!("enum {} {{", current.as_str())); + // target.get_mut().maybe_end_line(); + // target.get_mut().indent_up(); + // } + // Rule::directive => { + // table.render(target.get_mut()); + // table = TableFormat::new(); + // Self::reformat_directive(target.get_mut(), ¤t, "@@"); + // } + // Rule::enum_field_declaration => table.write(current.as_str()), + // // Doc comments are to be placed OUTSIDE of table block. + // Rule::doc_comment => comment(target.get_mut(), current.as_str()), + // Rule::WHITESPACE => { + // if !skip_whitespace { + // let lines = count_lines(current.as_str()); + // + // if lines > 1 || (lines == 1 && table.line_empty()) { + // // Reset the table layout on more than one newline. + // table.render(target.get_mut()); + // table = TableFormat::new(); + // } + // + // newlines(&mut table, current.as_str(), "m"); + // } + // } + // Rule::COMMENT => comment(&mut table.interleave_writer(), current.as_str()), + // _ => unreachable!( + // "Encounterd impossible enum declaration during parsing: {:?}", + // current.tokens() + // ), + // } + // } + // + // // End. + // table.render(target.get_mut()); + // target.get_mut().indent_down(); + // target.get_mut().write("}"); + // target.get_mut().maybe_end_line(); + // target.get_mut().maybe_end_line(); + // } + // + // fn reformat_field(target: &mut RefCell, token: &Token) { + // let mut identifier = None; + // let mut directives_started = false; + // + // for current in token.clone().into_inner() { + // match current.as_rule() { + // Rule::identifier => identifier = Some(String::from(current.as_str())), + // Rule::field_type => { + // target + // .get_mut() + // .write(&identifier.clone().expect("Unknown field identifier.")); + // target.get_mut().write(&Self::reformat_field_type(¤t)); + // } + // Rule::directive => { + // directives_started = true; + // Self::reformat_directive(&mut target.get_mut().column_locked_writer_for(2), ¤t, "@") + // } + // Rule::doc_comment => comment(&mut target.get_mut().interleave_writer(), current.as_str()), + // Rule::COMMENT => { + // if directives_started { + // comment(&mut target.get_mut().column_locked_writer_for(2), current.as_str()); + // } else { + // comment(target.get_mut(), current.as_str()); + // } + // } + // Rule::WHITESPACE => newlines(target.get_mut(), current.as_str(), "f"), + // _ => unreachable!("Encounterd impossible field during parsing: {:?}", current.tokens()), + // } + // } + // + // target.get_mut().maybe_end_line(); + // } + // + // fn reformat_type_declaration(target: &mut TableFormat, token: &Token) { + // let mut identifier = None; + // let mut directives_started = false; + // + // for current in token.clone().into_inner() { + // match current.as_rule() { + // Rule::TYPE_KEYWORD => {} + // Rule::identifier => identifier = Some(String::from(current.as_str())), + // Rule::base_type => { + // target.write("type"); + // target.write(&identifier.clone().expect("Unknown field identifier.")); + // target.write("="); + // target.write(&Self::get_identifier(¤t)); + // } + // Rule::directive => { + // directives_started = true; + // Self::reformat_directive(&mut target.column_locked_writer_for(4), ¤t, "@"); + // } + // Rule::doc_comment => comment(&mut target.interleave_writer(), current.as_str()), + // Rule::COMMENT => { + // if directives_started { + // comment(&mut target.column_locked_writer_for(4), current.as_str()); + // } else { + // comment(&mut target.interleave_writer(), current.as_str()); + // } + // } + // Rule::WHITESPACE => newlines(target, current.as_str(), "t"), + // _ => unreachable!( + // "Encounterd impossible custom type during parsing: {:?}", + // current.tokens() + // ), + // } + // } + // + // target.maybe_end_line(); + // } + // + // fn reformat_field_type(token: &Token) -> String { + // let mut builder = StringBuilder::new(); + // + // for current in token.clone().into_inner() { + // builder.write(&Self::get_identifier(¤t)); + // match current.as_rule() { + // Rule::optional_type => builder.write("?"), + // Rule::base_type => {} + // Rule::list_type => builder.write("[]"), + // _ => unreachable!( + // "Encounterd impossible field type during parsing: {:?}", + // current.tokens() + // ), + // } + // } + // + // builder.to_string() + // } + // + // fn get_identifier(token: &Token) -> String { + // for current in token.clone().into_inner() { + // if let Rule::identifier = current.as_rule() { + // return current.as_str().to_string(); + // } + // } + // + // panic!("No identifier found.") + // } + // + // fn reformat_directive(target: &mut dyn LineWriteable, token: &Token, owl: &str) { + // for current in token.clone().into_inner() { + // match current.as_rule() { + // Rule::directive_name => { + // // Begin + // if !target.line_empty() { + // target.write(" "); + // } + // target.write(owl); + // target.write(current.as_str()); + // } + // Rule::WHITESPACE => {} + // Rule::COMMENT => panic!("Comments inside attributes not supported yet."), + // Rule::directive_arguments => Self::reformat_directive_args(target, ¤t), + // _ => unreachable!("Encounterd impossible directive during parsing: {:?}", current.tokens()), + // } + // } + // } + // + // fn reformat_directive_args(target: &mut dyn LineWriteable, token: &Token) { + // let mut builder = StringBuilder::new(); + // + // for current in token.clone().into_inner() { + // match current.as_rule() { + // // This is a named arg. + // Rule::argument => { + // if !builder.line_empty() { + // builder.write(", "); + // } + // Self::reformat_directive_arg(&mut builder, ¤t); + // } + // // This is a an unnamed arg. + // Rule::argument_value => { + // if !builder.line_empty() { + // builder.write(", "); + // } + // Self::reformat_arg_value(&mut builder, ¤t); + // } + // Rule::WHITESPACE => {} + // Rule::COMMENT => panic!("Comments inside attribute argument list not supported yet."), + // _ => unreachable!( + // "Encounterd impossible directive argument list during parsing: {:?}", + // current.tokens() + // ), + // }; + // } + // + // if !builder.line_empty() { + // target.write("("); + // target.write(&builder.to_string()); + // target.write(")"); + // } + // } + // + // fn reformat_directive_arg(target: &mut dyn LineWriteable, token: &Token) { + // for current in token.clone().into_inner() { + // match current.as_rule() { + // Rule::argument_name => { + // target.write(current.as_str()); + // target.write(": "); + // } + // Rule::argument_value => Self::reformat_arg_value(target, ¤t), + // Rule::WHITESPACE => {} + // Rule::COMMENT => panic!("Comments inside attribute argument not supported yet."), + // _ => unreachable!( + // "Encounterd impossible directive argument during parsing: {:?}", + // current.tokens() + // ), + // }; + // } + // } + // + // fn reformat_arg_value(target: &mut dyn LineWriteable, token: &Token) { + // for current in token.clone().into_inner() { + // match current.as_rule() { + // Rule::expression => Self::reformat_expression(target, ¤t), + // Rule::WHITESPACE => {} + // Rule::COMMENT => panic!("Comments inside attributes not supported yet."), + // _ => unreachable!( + // "Encounterd impossible argument value during parsing: {:?}", + // current.tokens() + // ), + // }; + // } + // } + // + // /// Parses an expression, given a Pest parser token. + // fn reformat_expression(target: &mut dyn LineWriteable, token: &Token) { + // for current in token.clone().into_inner() { + // match current.as_rule() { + // Rule::numeric_literal => target.write(current.as_str()), + // Rule::string_literal => target.write(current.as_str()), + // Rule::boolean_literal => target.write(current.as_str()), + // Rule::constant_literal => target.write(current.as_str()), + // Rule::function => Self::reformat_function_expression(target, ¤t), + // Rule::array_expression => Self::reformat_array_expression(target, ¤t), + // Rule::WHITESPACE => {} + // Rule::COMMENT => panic!("Comments inside expressions not supported yet."), + // _ => unreachable!("Encounterd impossible literal during parsing: {:?}", current.tokens()), + // } + // } + // } + // + // fn reformat_array_expression(target: &mut dyn LineWriteable, token: &Token) { + // target.write("["); + // let mut expr_count = 0; + // + // for current in token.clone().into_inner() { + // match current.as_rule() { + // Rule::expression => { + // if expr_count > 0 { + // target.write(", "); + // } + // Self::reformat_expression(target, ¤t); + // expr_count += 1; + // } + // Rule::WHITESPACE => {} + // Rule::COMMENT => panic!("Comments inside expressions not supported yet."), + // _ => unreachable!("Encounterd impossible array during parsing: {:?}", current.tokens()), + // } + // } + // + // target.write("]"); + // } + // + // fn reformat_function_expression(target: &mut dyn LineWriteable, token: &Token) { + // let mut expr_count = 0; + // + // for current in token.clone().into_inner() { + // match current.as_rule() { + // Rule::identifier => { + // target.write(current.as_str()); + // target.write("("); + // } + // Rule::argument_value => { + // if expr_count > 0 { + // target.write(", "); + // } + // Self::reformat_arg_value(target, ¤t); + // expr_count += 1; + // } + // Rule::WHITESPACE => {} + // Rule::COMMENT => panic!("Comments inside expressions not supported yet."), + // _ => unreachable!("Encounterd impossible function during parsing: {:?}", current.tokens()), + // } + // } + // + // target.write(")"); + // } } diff --git a/libs/datamodel/core/src/lib.rs b/libs/datamodel/core/src/lib.rs index 773a238bfae1..11d384bee3af 100644 --- a/libs/datamodel/core/src/lib.rs +++ b/libs/datamodel/core/src/lib.rs @@ -8,7 +8,6 @@ macro_rules! match_children ( for $current in $token.clone().into_inner() { match $current.as_rule() { Rule::WHITESPACE => { }, - Rule::COMMENT => { }, Rule::BLOCK_OPEN => { }, Rule::BLOCK_CLOSE => { }, $( @@ -30,8 +29,8 @@ macro_rules! match_first ( .filter(|rule| rule.as_rule() != Rule::BLOCK_CLOSE && rule.as_rule() != Rule::BLOCK_OPEN && - rule.as_rule() != Rule::WHITESPACE && - rule.as_rule() != Rule::COMMENT) + rule.as_rule() != Rule::WHITESPACE + ) .next().unwrap(); match $current.as_rule() { $( From 85462f205343b1f1c560d46b37ec61ab7f3a7a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Bo=CC=88hm?= Date: Fri, 24 Jan 2020 13:23:12 +0100 Subject: [PATCH 05/14] remove obsolete code --- libs/datamodel/core/src/ast/reformat/mod.rs | 545 +----------------- .../core/src/validator/standardise.rs | 1 - 2 files changed, 4 insertions(+), 542 deletions(-) diff --git a/libs/datamodel/core/src/ast/reformat/mod.rs b/libs/datamodel/core/src/ast/reformat/mod.rs index 62f97ea9a1d1..d91a448b1242 100644 --- a/libs/datamodel/core/src/ast/reformat/mod.rs +++ b/libs/datamodel/core/src/ast/reformat/mod.rs @@ -1,547 +1,10 @@ -use super::{parser::*, renderer::*}; -use pest::Parser; - -// We have to use RefCell as rust cannot -// do multiple mutable borrows inside a match statement. -use std::cell::RefCell; - -type Token<'a> = pest::iterators::Pair<'a, Rule>; +use crate::{parse_datamodel, render_datamodel_to}; pub struct Reformatter {} -fn count_lines(text: &str) -> usize { - bytecount::count(text.as_bytes(), b'\n') -} - -fn newlines(target: &mut dyn LineWriteable, text: &str, _identifier: &str) { - for _i in 0..count_lines(text) { - // target.write(&format!("{}{}", i, identifier)); - target.end_line(); - } -} - -fn comment(target: &mut dyn LineWriteable, comment_text: &str) { - let trimmed = &comment_text[0..comment_text.len() - 1]; // slice away line break. - - if !target.line_empty() { - // Prefix with whitespace seperator. - target.write(&format!(" {}", trimmed)); - } else { - target.write(trimmed); - } - target.end_line(); -} - impl Reformatter { - pub fn reformat_to(input: &str, output: &mut dyn std::io::Write, ident_width: usize) { - let dml = crate::parse_datamodel(&input).unwrap(); - // dbg!(&dml); - // let mut ast = PrismaDatamodelParser::parse(Rule::datamodel, input).unwrap(); // TODO: Handle error. - // let mut top_formatter = RefCell::new(Renderer::new(output, ident_width)); - // Self::reformat_top(&mut top_formatter, &ast.next().unwrap()); - crate::render_datamodel_to(output, &dml).unwrap(); + pub fn reformat_to(input: &str, output: &mut dyn std::io::Write, _ident_width: usize) { + let dml = parse_datamodel(&input).unwrap(); + render_datamodel_to(output, &dml).unwrap(); } - - // fn reformat_top(target: &mut RefCell, token: &Token) { - // let mut types_table = TableFormat::new(); - // let mut types_mode = false; - // - // for current in token.clone().into_inner() { - // match current.as_rule() { - // Rule::WHITESPACE => {} - // Rule::type_declaration => { - // types_mode = true; - // } - // _ => { - // if types_mode { - // types_mode = false; - // // For all other ones, reset types_table. - // types_table.render(target.get_mut()); - // types_table = TableFormat::new(); - // target.get_mut().maybe_end_line(); - // } - // } - // }; - // match current.as_rule() { - // Rule::WHITESPACE => { - // if types_mode { - // let lines = count_lines(current.as_str()); - // - // if lines > 1 || (lines == 1 && types_table.line_empty()) { - // // Reset the table layout on more than one newline. - // types_table.render(target.get_mut()); - // types_table = TableFormat::new(); - // } - // - // newlines(&mut types_table, current.as_str(), "m"); - // } else { - // newlines(target.get_mut(), current.as_str(), "d") - // } - // } - // // Rule::COMMENT => { - // // if types_mode { - // // comment(&mut types_table.interleave_writer(), current.as_str()); - // // } else { - // // comment(target.get_mut(), current.as_str()); - // // } - // // } - // Rule::model_declaration => Self::reformat_model(target, ¤t), - // Rule::enum_declaration => Self::reformat_enum(target, ¤t), - // Rule::source_block => Self::reformat_config_block(target.get_mut(), ¤t), - // Rule::generator_block => Self::reformat_config_block(target.get_mut(), ¤t), - // Rule::type_declaration => { - // if !types_mode { - // panic!("Renderer not in type mode."); - // } - // Self::reformat_type_declaration(&mut types_table, ¤t); - // } - // Rule::EOI => {} - // _ => unreachable!( - // "Encounterd impossible datamodel declaration during parsing: {:?}", - // current.tokens() - // ), - // } - // } - // } - // - // fn reformat_config_block(target: &mut Renderer, token: &Token) { - // let mut table = TableFormat::new(); - // // Switch to skip whitespace in 'datasource xxxx {' - // let mut skip_whitespace = false; - // - // for current in token.clone().into_inner() { - // match current.as_rule() { - // Rule::GENERATOR_KEYWORD => { - // skip_whitespace = true; - // target.write("generator "); - // } - // Rule::DATASOURCE_KEYWORD => { - // skip_whitespace = true; - // target.write("datasource "); - // } - // Rule::BLOCK_OPEN => { - // skip_whitespace = false; - // target.write(" {"); - // target.maybe_end_line(); - // target.indent_up(); - // } - // Rule::BLOCK_CLOSE => {} - // Rule::identifier => target.write(current.as_str()), - // Rule::key_value => Self::reformat_key_value(&mut table, ¤t), - // Rule::doc_comment => comment(target, current.as_str()), - // Rule::WHITESPACE => { - // if !skip_whitespace { - // // TODO: This is duplicate. - // let lines = count_lines(current.as_str()); - // - // if lines > 1 || (lines == 1 && table.line_empty()) { - // // Reset the table layout on more than one newline. - // table.render(target); - // table = TableFormat::new(); - // } - // - // newlines(&mut table, current.as_str(), "m"); - // } - // } - //// Rule::COMMENT => comment(&mut table.interleave_writer(), current.as_str()), - //// _ => unreachable!( - //// "Encounterd impossible source declaration during parsing: {:?}", - //// current.tokens() - //// ), - // }; - // } - // - // table.render(target); - // target.indent_down(); - // target.write("}"); - // target.maybe_end_line(); - // target.maybe_end_line(); - // } - // - // fn reformat_key_value(target: &mut TableFormat, token: &Token) { - // for current in token.clone().into_inner() { - // match current.as_rule() { - // Rule::identifier => { - // target.write(current.as_str()); - // target.write("="); - // } - // Rule::expression => { - // Self::reformat_expression(&mut target.column_locked_writer_for(2), ¤t); - // } - // Rule::WHITESPACE => {} - // Rule::COMMENT => panic!("Comments inside config key/value not supported yet."), - // _ => unreachable!( - // "Encounterd impossible source property declaration during parsing: {:?}", - // current.tokens() - // ), - // } - // } - // } - // - // fn reformat_model(target: &mut RefCell, token: &Token) { - // let mut table = RefCell::new(TableFormat::new()); - // // Switch to skip whitespace in 'model xxxx {' - // let mut skip_whitespace = false; - // - // for current in token.clone().into_inner() { - // match current.as_rule() { - // Rule::MODEL_KEYWORD => { - // skip_whitespace = true; - // } - // Rule::BLOCK_OPEN => { - // skip_whitespace = false; - // } - // Rule::BLOCK_CLOSE => {} - // - // Rule::identifier => { - // // Begin. - // target.get_mut().write(&format!("model {} {{", current.as_str())); - // target.get_mut().maybe_end_line(); - // target.get_mut().indent_up(); - // } - // Rule::directive => { - // // Directives reset the table. - // table.get_mut().render(target.get_mut()); - // table = RefCell::new(TableFormat::new()); - // Self::reformat_directive(target.get_mut(), ¤t, "@@"); - // } - // Rule::field_declaration => Self::reformat_field(&mut table, ¤t), - // // Doc comments are to be placed OUTSIDE of table block. - // Rule::doc_comment => comment(target.get_mut(), current.as_str()), - // Rule::WHITESPACE => { - // if !skip_whitespace { - // let lines = count_lines(current.as_str()); - // - // if lines > 1 || (lines == 1 && table.get_mut().line_empty()) { - // // Reset the table layout on more than one newline. - // table.get_mut().render(target.get_mut()); - // table = RefCell::new(TableFormat::new()); - // } - // - // newlines(table.get_mut(), current.as_str(), "m"); - // } - // } - // Rule::COMMENT => comment(&mut table.get_mut().interleave_writer(), current.as_str()), - // _ => unreachable!( - // "Encounterd impossible model declaration during parsing: {:?}", - // current.tokens() - // ), - // } - // } - // - // // End. - // table.get_mut().render(target.get_mut()); - // target.get_mut().indent_down(); - // target.get_mut().write("}"); - // target.get_mut().maybe_end_line(); - // target.get_mut().maybe_end_line(); - // } - // - // // TODO: This is very similar to model reformating. - // fn reformat_enum(target: &mut RefCell, token: &Token) { - // let mut table = TableFormat::new(); - // // Switch to skip whitespace in 'enum xxxx {' - // let mut skip_whitespace = false; - // - // for current in token.clone().into_inner() { - // match current.as_rule() { - // Rule::ENUM_KEYWORD => { - // skip_whitespace = true; - // } - // Rule::BLOCK_OPEN => { - // skip_whitespace = false; - // } - // Rule::BLOCK_CLOSE => {} - // - // Rule::identifier => { - // // Begin. - // target.get_mut().write(&format!("enum {} {{", current.as_str())); - // target.get_mut().maybe_end_line(); - // target.get_mut().indent_up(); - // } - // Rule::directive => { - // table.render(target.get_mut()); - // table = TableFormat::new(); - // Self::reformat_directive(target.get_mut(), ¤t, "@@"); - // } - // Rule::enum_field_declaration => table.write(current.as_str()), - // // Doc comments are to be placed OUTSIDE of table block. - // Rule::doc_comment => comment(target.get_mut(), current.as_str()), - // Rule::WHITESPACE => { - // if !skip_whitespace { - // let lines = count_lines(current.as_str()); - // - // if lines > 1 || (lines == 1 && table.line_empty()) { - // // Reset the table layout on more than one newline. - // table.render(target.get_mut()); - // table = TableFormat::new(); - // } - // - // newlines(&mut table, current.as_str(), "m"); - // } - // } - // Rule::COMMENT => comment(&mut table.interleave_writer(), current.as_str()), - // _ => unreachable!( - // "Encounterd impossible enum declaration during parsing: {:?}", - // current.tokens() - // ), - // } - // } - // - // // End. - // table.render(target.get_mut()); - // target.get_mut().indent_down(); - // target.get_mut().write("}"); - // target.get_mut().maybe_end_line(); - // target.get_mut().maybe_end_line(); - // } - // - // fn reformat_field(target: &mut RefCell, token: &Token) { - // let mut identifier = None; - // let mut directives_started = false; - // - // for current in token.clone().into_inner() { - // match current.as_rule() { - // Rule::identifier => identifier = Some(String::from(current.as_str())), - // Rule::field_type => { - // target - // .get_mut() - // .write(&identifier.clone().expect("Unknown field identifier.")); - // target.get_mut().write(&Self::reformat_field_type(¤t)); - // } - // Rule::directive => { - // directives_started = true; - // Self::reformat_directive(&mut target.get_mut().column_locked_writer_for(2), ¤t, "@") - // } - // Rule::doc_comment => comment(&mut target.get_mut().interleave_writer(), current.as_str()), - // Rule::COMMENT => { - // if directives_started { - // comment(&mut target.get_mut().column_locked_writer_for(2), current.as_str()); - // } else { - // comment(target.get_mut(), current.as_str()); - // } - // } - // Rule::WHITESPACE => newlines(target.get_mut(), current.as_str(), "f"), - // _ => unreachable!("Encounterd impossible field during parsing: {:?}", current.tokens()), - // } - // } - // - // target.get_mut().maybe_end_line(); - // } - // - // fn reformat_type_declaration(target: &mut TableFormat, token: &Token) { - // let mut identifier = None; - // let mut directives_started = false; - // - // for current in token.clone().into_inner() { - // match current.as_rule() { - // Rule::TYPE_KEYWORD => {} - // Rule::identifier => identifier = Some(String::from(current.as_str())), - // Rule::base_type => { - // target.write("type"); - // target.write(&identifier.clone().expect("Unknown field identifier.")); - // target.write("="); - // target.write(&Self::get_identifier(¤t)); - // } - // Rule::directive => { - // directives_started = true; - // Self::reformat_directive(&mut target.column_locked_writer_for(4), ¤t, "@"); - // } - // Rule::doc_comment => comment(&mut target.interleave_writer(), current.as_str()), - // Rule::COMMENT => { - // if directives_started { - // comment(&mut target.column_locked_writer_for(4), current.as_str()); - // } else { - // comment(&mut target.interleave_writer(), current.as_str()); - // } - // } - // Rule::WHITESPACE => newlines(target, current.as_str(), "t"), - // _ => unreachable!( - // "Encounterd impossible custom type during parsing: {:?}", - // current.tokens() - // ), - // } - // } - // - // target.maybe_end_line(); - // } - // - // fn reformat_field_type(token: &Token) -> String { - // let mut builder = StringBuilder::new(); - // - // for current in token.clone().into_inner() { - // builder.write(&Self::get_identifier(¤t)); - // match current.as_rule() { - // Rule::optional_type => builder.write("?"), - // Rule::base_type => {} - // Rule::list_type => builder.write("[]"), - // _ => unreachable!( - // "Encounterd impossible field type during parsing: {:?}", - // current.tokens() - // ), - // } - // } - // - // builder.to_string() - // } - // - // fn get_identifier(token: &Token) -> String { - // for current in token.clone().into_inner() { - // if let Rule::identifier = current.as_rule() { - // return current.as_str().to_string(); - // } - // } - // - // panic!("No identifier found.") - // } - // - // fn reformat_directive(target: &mut dyn LineWriteable, token: &Token, owl: &str) { - // for current in token.clone().into_inner() { - // match current.as_rule() { - // Rule::directive_name => { - // // Begin - // if !target.line_empty() { - // target.write(" "); - // } - // target.write(owl); - // target.write(current.as_str()); - // } - // Rule::WHITESPACE => {} - // Rule::COMMENT => panic!("Comments inside attributes not supported yet."), - // Rule::directive_arguments => Self::reformat_directive_args(target, ¤t), - // _ => unreachable!("Encounterd impossible directive during parsing: {:?}", current.tokens()), - // } - // } - // } - // - // fn reformat_directive_args(target: &mut dyn LineWriteable, token: &Token) { - // let mut builder = StringBuilder::new(); - // - // for current in token.clone().into_inner() { - // match current.as_rule() { - // // This is a named arg. - // Rule::argument => { - // if !builder.line_empty() { - // builder.write(", "); - // } - // Self::reformat_directive_arg(&mut builder, ¤t); - // } - // // This is a an unnamed arg. - // Rule::argument_value => { - // if !builder.line_empty() { - // builder.write(", "); - // } - // Self::reformat_arg_value(&mut builder, ¤t); - // } - // Rule::WHITESPACE => {} - // Rule::COMMENT => panic!("Comments inside attribute argument list not supported yet."), - // _ => unreachable!( - // "Encounterd impossible directive argument list during parsing: {:?}", - // current.tokens() - // ), - // }; - // } - // - // if !builder.line_empty() { - // target.write("("); - // target.write(&builder.to_string()); - // target.write(")"); - // } - // } - // - // fn reformat_directive_arg(target: &mut dyn LineWriteable, token: &Token) { - // for current in token.clone().into_inner() { - // match current.as_rule() { - // Rule::argument_name => { - // target.write(current.as_str()); - // target.write(": "); - // } - // Rule::argument_value => Self::reformat_arg_value(target, ¤t), - // Rule::WHITESPACE => {} - // Rule::COMMENT => panic!("Comments inside attribute argument not supported yet."), - // _ => unreachable!( - // "Encounterd impossible directive argument during parsing: {:?}", - // current.tokens() - // ), - // }; - // } - // } - // - // fn reformat_arg_value(target: &mut dyn LineWriteable, token: &Token) { - // for current in token.clone().into_inner() { - // match current.as_rule() { - // Rule::expression => Self::reformat_expression(target, ¤t), - // Rule::WHITESPACE => {} - // Rule::COMMENT => panic!("Comments inside attributes not supported yet."), - // _ => unreachable!( - // "Encounterd impossible argument value during parsing: {:?}", - // current.tokens() - // ), - // }; - // } - // } - // - // /// Parses an expression, given a Pest parser token. - // fn reformat_expression(target: &mut dyn LineWriteable, token: &Token) { - // for current in token.clone().into_inner() { - // match current.as_rule() { - // Rule::numeric_literal => target.write(current.as_str()), - // Rule::string_literal => target.write(current.as_str()), - // Rule::boolean_literal => target.write(current.as_str()), - // Rule::constant_literal => target.write(current.as_str()), - // Rule::function => Self::reformat_function_expression(target, ¤t), - // Rule::array_expression => Self::reformat_array_expression(target, ¤t), - // Rule::WHITESPACE => {} - // Rule::COMMENT => panic!("Comments inside expressions not supported yet."), - // _ => unreachable!("Encounterd impossible literal during parsing: {:?}", current.tokens()), - // } - // } - // } - // - // fn reformat_array_expression(target: &mut dyn LineWriteable, token: &Token) { - // target.write("["); - // let mut expr_count = 0; - // - // for current in token.clone().into_inner() { - // match current.as_rule() { - // Rule::expression => { - // if expr_count > 0 { - // target.write(", "); - // } - // Self::reformat_expression(target, ¤t); - // expr_count += 1; - // } - // Rule::WHITESPACE => {} - // Rule::COMMENT => panic!("Comments inside expressions not supported yet."), - // _ => unreachable!("Encounterd impossible array during parsing: {:?}", current.tokens()), - // } - // } - // - // target.write("]"); - // } - // - // fn reformat_function_expression(target: &mut dyn LineWriteable, token: &Token) { - // let mut expr_count = 0; - // - // for current in token.clone().into_inner() { - // match current.as_rule() { - // Rule::identifier => { - // target.write(current.as_str()); - // target.write("("); - // } - // Rule::argument_value => { - // if expr_count > 0 { - // target.write(", "); - // } - // Self::reformat_arg_value(target, ¤t); - // expr_count += 1; - // } - // Rule::WHITESPACE => {} - // Rule::COMMENT => panic!("Comments inside expressions not supported yet."), - // _ => unreachable!("Encounterd impossible function during parsing: {:?}", current.tokens()), - // } - // } - // - // target.write(")"); - // } } diff --git a/libs/datamodel/core/src/validator/standardise.rs b/libs/datamodel/core/src/validator/standardise.rs index 6998931527e9..a74e8dd1a0bc 100644 --- a/libs/datamodel/core/src/validator/standardise.rs +++ b/libs/datamodel/core/src/validator/standardise.rs @@ -31,7 +31,6 @@ impl Standardiser { /// For any relations which are missing to_fields, sets them to the @id fields /// of the foreign model. fn set_relation_to_field_to_id_if_missing(&self, schema: &mut dml::Datamodel) { - // TODO: This is such a bad solution. :( let schema_copy = schema.clone(); // Iterate and mutate models. From c4e01502f20735d1933dc27569bb32c9205140d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Bo=CC=88hm?= Date: Fri, 24 Jan 2020 13:28:07 +0100 Subject: [PATCH 06/14] fix comment rendering --- libs/datamodel/core/src/ast/renderer/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/datamodel/core/src/ast/renderer/mod.rs b/libs/datamodel/core/src/ast/renderer/mod.rs index 9cdb98ece367..ba93d0bf8785 100644 --- a/libs/datamodel/core/src/ast/renderer/mod.rs +++ b/libs/datamodel/core/src/ast/renderer/mod.rs @@ -80,7 +80,7 @@ impl<'a> Renderer<'a> { fn render_documentation(target: &mut dyn LineWriteable, obj: &dyn ast::WithDocumentation) { if let Some(doc) = &obj.documentation() { for line in doc.text.split('\n') { - target.write("/// "); + target.write("// "); target.write(line); target.end_line(); } From 15c8317dd0412fe1509801d4347e79a272f4aecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Bo=CC=88hm?= Date: Fri, 24 Jan 2020 16:38:04 +0100 Subject: [PATCH 07/14] improve grammer definition & fix lots of tests cases --- .../core/src/ast/parser/datamodel.pest | 15 ++++++----- libs/datamodel/core/src/ast/parser/mod.rs | 3 +++ libs/datamodel/core/tests/base/basic.rs | 6 ++--- libs/datamodel/core/tests/base/comments.rs | 4 +++ .../tests/directives/relations_consistency.rs | 4 +-- .../tests/end_to_end/parser_renderer_ast.rs | 12 ++++----- .../tests/end_to_end/parser_renderer_dml.rs | 26 +++++++++---------- .../tests/end_to_end/parser_renderer_dmmf.rs | 18 ++++++------- .../core/tests/parsing/nice_errors.rs | 6 ++--- .../datamodel/core/tests/reformat/reformat.rs | 19 ++++++++++---- .../core/tests/renderer/simplification.rs | 4 ++- 11 files changed, 67 insertions(+), 50 deletions(-) diff --git a/libs/datamodel/core/src/ast/parser/datamodel.pest b/libs/datamodel/core/src/ast/parser/datamodel.pest index 9b42640aa224..ba37cae94b41 100644 --- a/libs/datamodel/core/src/ast/parser/datamodel.pest +++ b/libs/datamodel/core/src/ast/parser/datamodel.pest @@ -11,8 +11,9 @@ // ###################################### // Treat every whitespace the same for now. -WHITESPACE = @{ SPACE_SEPARATOR | "\t" | NEWLINE } -BLOCK_OPEN = @{ "{" } +WHITESPACE = @{ SPACE_SEPARATOR | "\t" } +UNTIL_END_OF_LINE = @{ WHITESPACE* ~ NEWLINE } +BLOCK_OPEN = @{ "{" ~ UNTIL_END_OF_LINE } BLOCK_CLOSE = @{ "}" } MODEL_KEYWORD = @{ "model" } TYPE_KEYWORD = @{ "type" } @@ -91,7 +92,7 @@ field_type = { list_type | optional_type | legacy_required_type | legacy_list_ty // TODO: We want to force a line break after a field declaration. // For this, we have to rely on pests new '-' operator. // Progress tracked here: https://github.com/pest-parser/pest/issues/271 -field_declaration = { doc_comment* ~ identifier ~ LEGACY_COLON? ~ (field_type ~ ( "@" ~ directive )+ | field_type) } +field_declaration = { doc_comment* ~ identifier ~ LEGACY_COLON? ~ (field_type ~ ( "@" ~ directive )+ | field_type) ~ doc_comment? } // ###################################### // Custom type declarations @@ -101,18 +102,18 @@ type_declaration = { doc_comment* ~ TYPE_KEYWORD ~ identifier ~ "=" ~ (base_type // ###################################### // Model declarations // ###################################### -model_declaration = { doc_comment* ~ (MODEL_KEYWORD | TYPE_KEYWORD) ~ identifier ~ BLOCK_OPEN ~ ( field_declaration | ( "@@" ~ directive ) )* ~ BLOCK_CLOSE } +model_declaration = { (UNTIL_END_OF_LINE | doc_comment)* ~ (MODEL_KEYWORD | TYPE_KEYWORD) ~ identifier ~ BLOCK_OPEN ~ ( field_declaration | ( "@@" ~ directive ) | NEWLINE )* ~ BLOCK_CLOSE } // ###################################### // Enum declarations // ###################################### enum_field_declaration = @{ identifier } -enum_declaration = { doc_comment* ~ ENUM_KEYWORD ~ identifier ~ BLOCK_OPEN ~ (enum_field_declaration | ( "@@" ~ directive ) )* ~ BLOCK_CLOSE } +enum_declaration = { doc_comment* ~ ENUM_KEYWORD ~ identifier ~ BLOCK_OPEN ~ ((enum_field_declaration | ( "@@" ~ directive )) ~ NEWLINE)* ~ BLOCK_CLOSE } // ###################################### // Source block // ###################################### -key_value = { identifier ~ "=" ~ expression } +key_value = { identifier ~ "=" ~ expression ~ NEWLINE } source_block = { doc_comment* ~ DATASOURCE_KEYWORD ~ identifier ~ BLOCK_OPEN ~ key_value* ~ BLOCK_CLOSE } // ###################################### @@ -123,7 +124,7 @@ generator_block = { doc_comment* ~ GENERATOR_KEYWORD ~ identifier ~ BLOCK_OPEN ~ // ###################################### // Datamodel // ###################################### -datamodel = { SOI ~ (model_declaration | enum_declaration | source_block | generator_block | type_declaration )* ~ EOI } +datamodel = { SOI ~ NEWLINE* ~ ((model_declaration | enum_declaration | source_block | generator_block | type_declaration) ~ NEWLINE*)* ~ EOI } // ###################################### // String Interpolation diff --git a/libs/datamodel/core/src/ast/parser/mod.rs b/libs/datamodel/core/src/ast/parser/mod.rs index 23d3f161e9e5..b879d1114c1d 100644 --- a/libs/datamodel/core/src/ast/parser/mod.rs +++ b/libs/datamodel/core/src/ast/parser/mod.rs @@ -255,6 +255,7 @@ fn parse_model(token: &pest::iterators::Pair<'_, Rule>) -> Result comments.push(parse_doc_comment(¤t)), + Rule::UNTIL_END_OF_LINE => {}, _ => unreachable!("Encountered impossible model declaration during parsing: {:?}", current.tokens()) } @@ -444,6 +445,7 @@ pub fn parse(datamodel_string: &str) -> Result { Rule::source_block => models.push(Top::Source(parse_source(¤t))), Rule::generator_block => models.push(Top::Generator(parse_generator(¤t))), Rule::type_declaration => models.push(Top::Type(parse_type(¤t))), + Rule::doc_comment => {}, Rule::EOI => {}, _ => panic!("Encountered impossible datamodel declaration during parsing: {:?}", current.tokens()) } @@ -520,6 +522,7 @@ fn rule_to_string(rule: Rule) -> &'static str { Rule::DATASOURCE_KEYWORD => "\"datasource\" keyword", Rule::INTERPOLATION_START => "string interpolation start", Rule::INTERPOLATION_END => "string interpolation end", + Rule::UNTIL_END_OF_LINE => "until end of line", // Those are top level things and will never surface. Rule::datamodel => "datamodel declaration", diff --git a/libs/datamodel/core/tests/base/basic.rs b/libs/datamodel/core/tests/base/basic.rs index 94de7a25e1ec..3594fa0452be 100644 --- a/libs/datamodel/core/tests/base/basic.rs +++ b/libs/datamodel/core/tests/base/basic.rs @@ -50,11 +50,11 @@ fn parse_basic_enum() { #[test] fn parse_comments() { let dml = r#" - /// The user model. + // The user model. model User { id Int @id - /// The first name. - /// Can be multi-line. + // The first name. + // Can be multi-line. firstName String lastName String } diff --git a/libs/datamodel/core/tests/base/comments.rs b/libs/datamodel/core/tests/base/comments.rs index c78941c12e9c..ce0d662ea860 100644 --- a/libs/datamodel/core/tests/base/comments.rs +++ b/libs/datamodel/core/tests/base/comments.rs @@ -1,7 +1,9 @@ use crate::common::*; use datamodel::common::ScalarType; +// TODO: figure out if this is a feature we want (the weird definition of `firstName`). I don't think so. #[test] +#[ignore] fn parse_comments_without_crasing_or_loosing_info() { let dml = r#" // This is a comment @@ -26,7 +28,9 @@ fn parse_comments_without_crasing_or_loosing_info() { .assert_base_type(&ScalarType::String); } +// TODO: figure out if this is a feature we want. I don't think so. #[test] +#[ignore] fn accept_a_comment_at_the_end() { let dml = r#" model User { diff --git a/libs/datamodel/core/tests/directives/relations_consistency.rs b/libs/datamodel/core/tests/directives/relations_consistency.rs index 0ba3919554d4..55ead0151127 100644 --- a/libs/datamodel/core/tests/directives/relations_consistency.rs +++ b/libs/datamodel/core/tests/directives/relations_consistency.rs @@ -297,7 +297,7 @@ fn should_add_back_relations_for_more_complex_cases() { .assert_relation_to("User") .assert_relation_to_fields(&["id"]) .assert_relation_name("PostToUser") - .assert_is_generated(true) + .assert_is_generated(false) .assert_arity(&datamodel::dml::FieldArity::Optional); // Backward @@ -319,7 +319,7 @@ fn should_add_back_relations_for_more_complex_cases() { .assert_relation_to("Post") .assert_relation_to_fields(&["post_id"]) .assert_relation_name("CommentToPost") - .assert_is_generated(true) + .assert_is_generated(false) .assert_arity(&datamodel::dml::FieldArity::Optional); // Backward diff --git a/libs/datamodel/core/tests/end_to_end/parser_renderer_ast.rs b/libs/datamodel/core/tests/end_to_end/parser_renderer_ast.rs index 1c62b6430d57..482942b27a7e 100644 --- a/libs/datamodel/core/tests/end_to_end/parser_renderer_ast.rs +++ b/libs/datamodel/core/tests/end_to_end/parser_renderer_ast.rs @@ -172,30 +172,30 @@ fn test_parser_renderer_sources_and_comments_via_ast() { assert_eq!(rendered, DATAMODEL_WITH_SOURCE_AND_COMMENTS); } -const DATAMODEL_WITH_TABS: &str = r#"/// Super cool postgres source. +const DATAMODEL_WITH_TABS: &str = r#"// Super cool postgres source. datasource\tpg1\t{ \tprovider\t=\t\t"postgres" \turl\t=\t"https://localhost/postgres1" } \t -///\tMy author\tmodel. +//\tMy author\tmodel. model\tAuthor\t{ \tid\tInt\t@id -\t/// Name of the author. +\t// Name of the author. \t\tname\tString? \tcreatedAt\tDateTime\t@default(now()) }"#; -const DATAMODEL_WITH_SPACES: &str = r#"/// Super cool postgres source. +const DATAMODEL_WITH_SPACES: &str = r#"// Super cool postgres source. datasource pg1 { provider = "postgres" url = "https://localhost/postgres1" } -/// My author\tmodel. +// My author\tmodel. model Author { id Int @id - /// Name of the author. + // Name of the author. name String? createdAt DateTime @default(now()) }"#; diff --git a/libs/datamodel/core/tests/end_to_end/parser_renderer_dml.rs b/libs/datamodel/core/tests/end_to_end/parser_renderer_dml.rs index 2c01fbab62ba..9af31f733459 100644 --- a/libs/datamodel/core/tests/end_to_end/parser_renderer_dml.rs +++ b/libs/datamodel/core/tests/end_to_end/parser_renderer_dml.rs @@ -17,7 +17,7 @@ const DATAMODEL_STRING: &str = r#"model User { model Profile { id Int @id - user User + user User @relation(references: [id]) bio String @@map("profile") @@ -29,7 +29,7 @@ model Post { updatedAt DateTime title String @default("Default-Title") wasLiked Boolean @default(false) - author User @relation("author") + author User @relation("author", references: [id]) published Boolean @default(false) categories PostToCategory[] @@ -48,15 +48,15 @@ model Category { model PostToCategory { id Int @id - post Post - category Category + post Post @relation(references: [title, createdAt]) + category Category @relation(references: [id]) @@map("post_to_category") } model A { id Int @id - b B + b B @relation(references: [id]) } model B { @@ -80,26 +80,24 @@ fn test_parser_renderer_via_dml() { assert_eq!(DATAMODEL_STRING, rendered); } -// TODO: Test that N:M relation names are correctly handled as soon as we -// get relation table support. const MANY_TO_MANY_DATAMODEL: &str = r#"model Blog { id Int @id name String viewCount Int posts Post[] - authors Author[] @relation("AuthorToBlogs") + authors Author[] @relation("AuthorToBlogs", references: [id]) } model Author { id Int @id name String? - authors Blog[] @relation("AuthorToBlogs") + authors Blog[] @relation("AuthorToBlogs", references: [id]) } model Post { id Int @id title String - blog Blog + blog Blog @relation(references: [id]) }"#; #[test] @@ -112,14 +110,14 @@ fn test_parser_renderer_many_to_many_via_dml() { assert_eq!(rendered, MANY_TO_MANY_DATAMODEL); } -const DATAMODEL_STRING_WITH_COMMENTS: &str = r#"/// Cool user model +const DATAMODEL_STRING_WITH_COMMENTS: &str = r#"// Cool user model model User { id Int @id - /// Created at field + // Created at field createdAt DateTime email String @unique - /// Name field. - /// Multi line comment. + // Name field. + // Multi line comment. name String? @@map("user") diff --git a/libs/datamodel/core/tests/end_to_end/parser_renderer_dmmf.rs b/libs/datamodel/core/tests/end_to_end/parser_renderer_dmmf.rs index 7a842afe82f0..0e4fae6bd56d 100644 --- a/libs/datamodel/core/tests/end_to_end/parser_renderer_dmmf.rs +++ b/libs/datamodel/core/tests/end_to_end/parser_renderer_dmmf.rs @@ -15,7 +15,7 @@ const DATAMODEL_STRING: &str = r#"model User { model Profile { id Int @id - user User + user User @relation(references: [id]) bio String @@map("profile") @@ -27,7 +27,7 @@ model Post { updatedAt DateTime title String @default("Default-Title") wasLiked Boolean @default(false) - author User @relation("author") + author User @relation("author", references: [id]) published Boolean @default(false) categories PostToCategory[] @@ -46,15 +46,15 @@ model Category { model PostToCategory { id Int @id - post Post - category Category + post Post @relation(references: [title, createdAt]) + category Category @relation(references: [id]) @@map("post_to_category") } model A { id Int @id - b B + b B @relation(references: [id]) } model B { @@ -117,16 +117,16 @@ fn test_dmmf_roundtrip_with_sources() { assert_eq!(DATAMODEL_WITH_SOURCE, rendered); } -const DATAMODEL_WITH_SOURCE_AND_COMMENTS: &str = r#"/// Super cool postgres source. +const DATAMODEL_WITH_SOURCE_AND_COMMENTS: &str = r#"// Super cool postgres source. datasource pg1 { provider = "postgresql" url = "https://localhost/postgres1" } -/// My author model. +// My author model. model Author { id Int @id - /// Name of the author. + // Name of the author. name String? createdAt DateTime @default(now()) }"#; @@ -249,7 +249,7 @@ const DML_WITHOUT_RELATION_NAME: &str = r#"model User { model Post { id Int @id - user User + user User @relation(references: [id]) }"#; #[test] diff --git a/libs/datamodel/core/tests/parsing/nice_errors.rs b/libs/datamodel/core/tests/parsing/nice_errors.rs index cb951527251c..5a303ea94db1 100644 --- a/libs/datamodel/core/tests/parsing/nice_errors.rs +++ b/libs/datamodel/core/tests/parsing/nice_errors.rs @@ -78,7 +78,7 @@ fn nice_error_missing_type() { let error = parse_error(dml); - error.assert_is(DatamodelError::new_parser_error(&vec!["field type"], Span::new(54, 54))); + error.assert_is(DatamodelError::new_parser_error(&vec!["field type"], Span::new(49, 49))); } #[test] @@ -91,7 +91,7 @@ fn nice_error_missing_directive_name() { let error = parse_error(dml); - error.assert_is(DatamodelError::new_parser_error(&vec!["directive"], Span::new(43, 43))); + error.assert_is(DatamodelError::new_parser_error(&vec!["directive"], Span::new(38, 38))); } // TODO: This case is not nice because the "{ }" belong to the declaration. @@ -106,7 +106,7 @@ fn nice_error_missing_braces() { error.assert_is(DatamodelError::new_parser_error( &vec!["Start of block (\"{\")"], - Span::new(25, 25), + Span::new(16, 16), )); } diff --git a/libs/datamodel/core/tests/reformat/reformat.rs b/libs/datamodel/core/tests/reformat/reformat.rs index 20ac25a88adc..97f2acac4c97 100644 --- a/libs/datamodel/core/tests/reformat/reformat.rs +++ b/libs/datamodel/core/tests/reformat/reformat.rs @@ -5,11 +5,12 @@ use std::str; #[test] fn test_reformat_model() { let input = r#" - model User { id Int @id } + model User { + id Int @id + } "#; - let expected = r#" -model User { + let expected = r#"model User { id Int @id }"#; @@ -22,12 +23,16 @@ model User { #[test] fn test_reformat_config() { let input = r#" - datasource pg { provider = "postgres" } + datasource pg { + provider = "postgres" + url = "postgres://" + } "#; let expected = r#" datasource pg { provider = "postgres" + url = "postgres://" }"#; let mut buf = Vec::new(); @@ -39,12 +44,16 @@ datasource pg { #[test] fn test_reformat_tabs() { let input = r#" - datasource pg {\tprovider\t=\t"postgres"\t} + datasource pg { + provider\t=\t"postgres" + url = "postgres://" + } "#; let expected = r#" datasource pg { provider = "postgres" + url = "postgres://" }"#; let mut buf = Vec::new(); diff --git a/libs/datamodel/core/tests/renderer/simplification.rs b/libs/datamodel/core/tests/renderer/simplification.rs index f55c89018d9e..a4feaf6fc5ad 100644 --- a/libs/datamodel/core/tests/renderer/simplification.rs +++ b/libs/datamodel/core/tests/renderer/simplification.rs @@ -14,7 +14,7 @@ fn test_exclude_default_relation_names_from_rendering() { let expected = r#"model Todo { id Int @id - user User + user User @relation(references: [id]) } model User { @@ -30,7 +30,9 @@ model User { assert_eq!(rendered, expected); } +// TODO: this is probably obsolete #[test] +#[ignore] fn test_exclude_to_fields_id() { let input = r#" model Todo { From 8f764cb51590e1abe9065e8929ba45c93845a9cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Bo=CC=88hm?= Date: Wed, 29 Jan 2020 11:28:16 +0100 Subject: [PATCH 08/14] reformat now preserves datasources, generators and type aliases --- libs/datamodel/core/src/ast/reformat/mod.rs | 20 +++++++++++++++++-- libs/datamodel/core/src/lib.rs | 2 +- libs/datamodel/core/src/validator/lower.rs | 2 +- .../tests/end_to_end/parser_renderer_ast.rs | 6 +++--- .../datamodel/core/tests/reformat/reformat.rs | 10 ++++------ 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/libs/datamodel/core/src/ast/reformat/mod.rs b/libs/datamodel/core/src/ast/reformat/mod.rs index d91a448b1242..8db3dbfa50a1 100644 --- a/libs/datamodel/core/src/ast/reformat/mod.rs +++ b/libs/datamodel/core/src/ast/reformat/mod.rs @@ -1,10 +1,26 @@ -use crate::{parse_datamodel, render_datamodel_to}; +use crate::{ast, parse_datamodel, parse_schema_ast, render_schema_ast_to, validator::LowerDmlToAst}; pub struct Reformatter {} impl Reformatter { pub fn reformat_to(input: &str, output: &mut dyn std::io::Write, _ident_width: usize) { + // the AST contains the datasources, generators, type aliases that are missing in the dml + // it also contains all the original positions within the file + let mut ast = parse_schema_ast(&input).unwrap(); let dml = parse_datamodel(&input).unwrap(); - render_datamodel_to(output, &dml).unwrap(); + + for top in ast.tops.iter_mut() { + match top { + ast::Top::Model(model) => { + let lowerer = LowerDmlToAst::new(); + let dml_model = dml.find_model(&model.name.name).unwrap(); + let new_model = lowerer.lower_model(&dml_model, &dml).unwrap(); + std::mem::replace(top, ast::Top::Model(new_model)); + } + _ => {} + } + } + + render_schema_ast_to(output, &ast, 2); } } diff --git a/libs/datamodel/core/src/lib.rs b/libs/datamodel/core/src/lib.rs index 11d384bee3af..5972829745e6 100644 --- a/libs/datamodel/core/src/lib.rs +++ b/libs/datamodel/core/src/lib.rs @@ -197,7 +197,7 @@ pub fn render_datamodel_and_config_to( } /// Renders as a string into the stream. -fn render_schema_ast_to(stream: &mut dyn std::io::Write, schema: &ast::SchemaAst, ident_width: usize) { +pub(crate) fn render_schema_ast_to(stream: &mut dyn std::io::Write, schema: &ast::SchemaAst, ident_width: usize) { let mut renderer = ast::renderer::Renderer::new(stream, ident_width); renderer.render(schema); } diff --git a/libs/datamodel/core/src/validator/lower.rs b/libs/datamodel/core/src/validator/lower.rs index 95e27702b962..49a295155510 100644 --- a/libs/datamodel/core/src/validator/lower.rs +++ b/libs/datamodel/core/src/validator/lower.rs @@ -49,7 +49,7 @@ impl LowerDmlToAst { Ok(ast::SchemaAst { tops: tops }) } - fn lower_model(&self, model: &dml::Model, datamodel: &dml::Datamodel) -> Result { + pub fn lower_model(&self, model: &dml::Model, datamodel: &dml::Datamodel) -> Result { let mut errors = ErrorCollection::new(); let mut fields: Vec = Vec::new(); diff --git a/libs/datamodel/core/tests/end_to_end/parser_renderer_ast.rs b/libs/datamodel/core/tests/end_to_end/parser_renderer_ast.rs index 482942b27a7e..ec4f629cebf5 100644 --- a/libs/datamodel/core/tests/end_to_end/parser_renderer_ast.rs +++ b/libs/datamodel/core/tests/end_to_end/parser_renderer_ast.rs @@ -148,16 +148,16 @@ fn test_parser_renderer_sources_via_ast() { assert_eq!(rendered, DATAMODEL_WITH_SOURCE); } -const DATAMODEL_WITH_SOURCE_AND_COMMENTS: &str = r#"/// Super cool postgres source. +const DATAMODEL_WITH_SOURCE_AND_COMMENTS: &str = r#"// Super cool postgres source. datasource pg1 { provider = "postgres" url = "https://localhost/postgres1" } -/// My author model. +// My author model. model Author { id Int @id - /// Name of the author. + // Name of the author. name String? createdAt DateTime @default(now()) }"#; diff --git a/libs/datamodel/core/tests/reformat/reformat.rs b/libs/datamodel/core/tests/reformat/reformat.rs index 97f2acac4c97..461b5f048171 100644 --- a/libs/datamodel/core/tests/reformat/reformat.rs +++ b/libs/datamodel/core/tests/reformat/reformat.rs @@ -29,10 +29,9 @@ fn test_reformat_config() { } "#; - let expected = r#" -datasource pg { + let expected = r#"datasource pg { provider = "postgres" - url = "postgres://" + url = "postgres://" }"#; let mut buf = Vec::new(); @@ -50,10 +49,9 @@ fn test_reformat_tabs() { } "#; - let expected = r#" -datasource pg { + let expected = r#"datasource pg { provider = "postgres" - url = "postgres://" + url = "postgres://" }"#; let mut buf = Vec::new(); From a63315fa5fd89ca7c809a4c6075c1441a42777e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Bo=CC=88hm?= Date: Wed, 29 Jan 2020 12:08:30 +0100 Subject: [PATCH 09/14] retain information on which type alias a field type was based on --- libs/datamodel/core/src/dml/field.rs | 3 ++- libs/datamodel/core/src/json/dmmf/from_dmmf.rs | 4 ++-- libs/datamodel/core/src/json/dmmf/to_dmmf.rs | 4 ++-- .../core/src/validator/directive/core/default.rs | 2 +- .../core/src/validator/directive/core/id.rs | 4 ++-- .../core/src/validator/directive/core/updated_at.rs | 4 +++- libs/datamodel/core/src/validator/lift.rs | 12 +++++++----- libs/datamodel/core/src/validator/lower.rs | 4 +++- libs/datamodel/core/tests/common.rs | 2 +- 9 files changed, 23 insertions(+), 16 deletions(-) diff --git a/libs/datamodel/core/src/dml/field.rs b/libs/datamodel/core/src/dml/field.rs index 5f7da233ff75..c23145fe27be 100644 --- a/libs/datamodel/core/src/dml/field.rs +++ b/libs/datamodel/core/src/dml/field.rs @@ -25,7 +25,8 @@ pub enum FieldType { /// Connector specific field type. ConnectorSpecific(ScalarFieldType), /// Base (built-in scalar) type. - Base(ScalarType), + /// The option is Some(x) if the scalar type is based upon a type alias. + Base(ScalarType, Option), } impl FieldType { diff --git a/libs/datamodel/core/src/json/dmmf/from_dmmf.rs b/libs/datamodel/core/src/json/dmmf/from_dmmf.rs index 54e7ce1bbb72..15146167cdcc 100644 --- a/libs/datamodel/core/src/json/dmmf/from_dmmf.rs +++ b/libs/datamodel/core/src/json/dmmf/from_dmmf.rs @@ -86,7 +86,7 @@ fn default_value_from_serde( ) -> Option { match (container, field_type) { // Scalar. - (Some(value), dml::FieldType::Base(scalar_type)) => match (value, scalar_type) { + (Some(value), dml::FieldType::Base(scalar_type, _)) => match (value, scalar_type) { // Function. (serde_json::Value::Object(_), _) => { let func = serde_json::from_value::(value.clone()).expect("Failed to parse function JSON"); @@ -163,7 +163,7 @@ fn get_field_type(field: &Field) -> dml::FieldType { on_delete: get_on_delete_strategy(&field.relation_on_delete), }), "enum" => dml::FieldType::Enum(field.field_type.clone()), - "scalar" => dml::FieldType::Base(type_from_string(&field.field_type)), + "scalar" => dml::FieldType::Base(type_from_string(&field.field_type), None), _ => panic!(format!("Unknown field kind {}.", &field.kind)), } } diff --git a/libs/datamodel/core/src/json/dmmf/to_dmmf.rs b/libs/datamodel/core/src/json/dmmf/to_dmmf.rs index 34918db9f258..6db712dab20a 100644 --- a/libs/datamodel/core/src/json/dmmf/to_dmmf.rs +++ b/libs/datamodel/core/src/json/dmmf/to_dmmf.rs @@ -78,7 +78,7 @@ fn get_field_kind(field: &dml::Field) -> String { match field.field_type { dml::FieldType::Relation(_) => String::from("object"), dml::FieldType::Enum(_) => String::from("enum"), - dml::FieldType::Base(_) => String::from("scalar"), + dml::FieldType::Base(_, _) => String::from("scalar"), _ => unimplemented!("DMMF does not support field type {:?}", field.field_type), } } @@ -87,7 +87,7 @@ fn get_field_type(field: &dml::Field) -> String { match &field.field_type { dml::FieldType::Relation(relation_info) => relation_info.to.clone(), dml::FieldType::Enum(t) => t.clone(), - dml::FieldType::Base(t) => type_to_string(t), + dml::FieldType::Base(t, _) => type_to_string(t), dml::FieldType::ConnectorSpecific(sft) => type_to_string(&sft.prisma_type()), } } diff --git a/libs/datamodel/core/src/validator/directive/core/default.rs b/libs/datamodel/core/src/validator/directive/core/default.rs index d1d6b0ee20cb..c81683de6c0b 100644 --- a/libs/datamodel/core/src/validator/directive/core/default.rs +++ b/libs/datamodel/core/src/validator/directive/core/default.rs @@ -17,7 +17,7 @@ impl DirectiveValidator for DefaultDirectiveValidator { return self.new_directive_validation_error("Cannot set a default value on list field.", args.span()); } - if let dml::FieldType::Base(scalar_type) = field.field_type { + if let dml::FieldType::Base(scalar_type, _) = field.field_type { let dv = args .default_arg("value")? .as_default_value(scalar_type) diff --git a/libs/datamodel/core/src/validator/directive/core/id.rs b/libs/datamodel/core/src/validator/directive/core/id.rs index a03b8cbdfdea..65644f62745c 100644 --- a/libs/datamodel/core/src/validator/directive/core/id.rs +++ b/libs/datamodel/core/src/validator/directive/core/id.rs @@ -13,10 +13,10 @@ impl DirectiveValidator for IdDirectiveValidator { // TODO In which form is this still required or needs to change? Default values are handling the id strategy now. fn validate_and_apply(&self, args: &mut Args, obj: &mut dml::Field) -> Result<(), DatamodelError> { let strategy = match (&obj.field_type, &obj.default_value) { - (dml::FieldType::Base(dml::ScalarType::Int), Some(dml::DefaultValue::Expression(_))) => { + (dml::FieldType::Base(dml::ScalarType::Int, _), Some(dml::DefaultValue::Expression(_))) => { dml::IdStrategy::Auto } - (dml::FieldType::Base(dml::ScalarType::String), Some(dml::DefaultValue::Expression(_))) => { + (dml::FieldType::Base(dml::ScalarType::String, _), Some(dml::DefaultValue::Expression(_))) => { dml::IdStrategy::Auto } _ => dml::IdStrategy::None, diff --git a/libs/datamodel/core/src/validator/directive/core/updated_at.rs b/libs/datamodel/core/src/validator/directive/core/updated_at.rs index 864d7ec54240..263cc04f8e9a 100644 --- a/libs/datamodel/core/src/validator/directive/core/updated_at.rs +++ b/libs/datamodel/core/src/validator/directive/core/updated_at.rs @@ -11,7 +11,9 @@ impl DirectiveValidator for UpdatedAtDirectiveValidator { } fn validate_and_apply(&self, args: &mut Args, obj: &mut dml::Field) -> Result<(), DatamodelError> { - if obj.field_type != dml::FieldType::Base(dml::ScalarType::DateTime) { + if let dml::FieldType::Base(dml::ScalarType::DateTime, _) = obj.field_type { + // everything good + } else { return self.new_directive_validation_error( "Fields that are marked with @updatedAt must be of type DateTime.", args.span(), diff --git a/libs/datamodel/core/src/validator/lift.rs b/libs/datamodel/core/src/validator/lift.rs index 792e3b22b128..6586e5fa2d59 100644 --- a/libs/datamodel/core/src/validator/lift.rs +++ b/libs/datamodel/core/src/validator/lift.rs @@ -108,7 +108,7 @@ impl LiftAstToDml { fn lift_field(&self, ast_field: &ast::Field, ast_schema: &ast::SchemaAst) -> Result { let mut errors = ErrorCollection::new(); // If we cannot parse the field type, we exit right away. - let (field_type, extra_attributes) = self.lift_field_type(&ast_field, ast_schema, &mut Vec::new())?; + let (field_type, extra_attributes) = self.lift_field_type(&ast_field, None, ast_schema, &mut Vec::new())?; let mut field = dml::Field::new(&ast_field.name.name, field_type.clone()); field.documentation = ast_field.documentation.clone().map(|comment| comment.text); @@ -117,7 +117,7 @@ impl LiftAstToDml { if let Some(value) = &ast_field.default_value { let validator = ValueValidator::new(value); - if let dml::FieldType::Base(base_type) = &field_type { + if let dml::FieldType::Base(base_type, _) = &field_type { match validator.as_default_value(*base_type) { Ok(dv) => field.default_value = Some(dv), Err(err) => errors.push(err), @@ -158,6 +158,7 @@ impl LiftAstToDml { fn lift_field_type( &self, ast_field: &ast::Field, + custom_type_name: Option, ast_schema: &ast::SchemaAst, checked_types: &mut Vec, ) -> Result<(dml::FieldType, Vec), DatamodelError> { @@ -177,10 +178,10 @@ impl LiftAstToDml { let field_type = dml::FieldType::ConnectorSpecific(x); Ok((field_type, vec![])) } else { - Ok((dml::FieldType::Base(scalar_type), vec![])) + Ok((dml::FieldType::Base(scalar_type, custom_type_name), vec![])) } } else { - Ok((dml::FieldType::Base(scalar_type), vec![])) + Ok((dml::FieldType::Base(scalar_type, custom_type_name), vec![])) } } else if ast_schema.find_model(type_name).is_some() { Ok((dml::FieldType::Relation(dml::RelationInfo::new(type_name)), vec![])) @@ -213,7 +214,8 @@ impl LiftAstToDml { if let Some(custom_type) = ast_schema.find_type_alias(&type_name) { checked_types.push(custom_type.name.name.clone()); - let (field_type, mut attrs) = self.lift_field_type(custom_type, ast_schema, checked_types)?; + let (field_type, mut attrs) = + self.lift_field_type(custom_type, Some(type_name.to_owned()), ast_schema, checked_types)?; if let dml::FieldType::Relation(_) = field_type { return Err(DatamodelError::new_validation_error( diff --git a/libs/datamodel/core/src/validator/lower.rs b/libs/datamodel/core/src/validator/lower.rs index 49a295155510..d1d3611ecfc8 100644 --- a/libs/datamodel/core/src/validator/lower.rs +++ b/libs/datamodel/core/src/validator/lower.rs @@ -154,7 +154,9 @@ impl LowerDmlToAst { datamodel: &dml::Datamodel, ) -> ast::Identifier { match field_type { - dml::FieldType::Base(tpe) => ast::Identifier::new(&tpe.to_string()), + dml::FieldType::Base(tpe, custom_type_name) => { + ast::Identifier::new(&custom_type_name.as_ref().unwrap_or(&tpe.to_string())) + } dml::FieldType::Enum(tpe) => ast::Identifier::new(&tpe.to_string()), dml::FieldType::Relation(rel) => { let related_model = datamodel.find_model(&rel.to).expect(STATE_ERROR); diff --git a/libs/datamodel/core/tests/common.rs b/libs/datamodel/core/tests/common.rs index 7818e43d19ab..60424845fa8b 100644 --- a/libs/datamodel/core/tests/common.rs +++ b/libs/datamodel/core/tests/common.rs @@ -50,7 +50,7 @@ pub trait ErrorAsserts { impl FieldAsserts for dml::Field { fn assert_base_type(&self, t: &ScalarType) -> &Self { - if let dml::FieldType::Base(base_type) = &self.field_type { + if let dml::FieldType::Base(base_type, _) = &self.field_type { assert_eq!(base_type, t); } else { panic!("Scalar expected, but found {:?}", self.field_type); From 2cad1d73f9ed64f48c08f93de0248294db4670ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Bo=CC=88hm?= Date: Wed, 29 Jan 2020 13:44:39 +0100 Subject: [PATCH 10/14] get last test to pass --- libs/datamodel/core/src/ast/parser/datamodel.pest | 5 +++-- libs/datamodel/core/src/ast/parser/mod.rs | 7 ++++++- libs/datamodel/core/tests/parsing/nice_errors.rs | 8 ++++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/libs/datamodel/core/src/ast/parser/datamodel.pest b/libs/datamodel/core/src/ast/parser/datamodel.pest index ba37cae94b41..cdaca28fa797 100644 --- a/libs/datamodel/core/src/ast/parser/datamodel.pest +++ b/libs/datamodel/core/src/ast/parser/datamodel.pest @@ -75,6 +75,7 @@ directive_arguments = { "(" ~ (((argument | argument_value) ~ ("," ~ argument)*) directive_name = @{ (identifier ~ ".")? ~ identifier } // A directive either has one unnamed argument or any number of named arguments or no argument. directive = { (directive_name ~ directive_arguments | directive_name) } +block_level_directive = { "@@" ~ directive ~ NEWLINE } // ###################################### // Field declarations @@ -107,8 +108,8 @@ model_declaration = { (UNTIL_END_OF_LINE | doc_comment)* ~ (MODEL_KEYWORD | TYPE // ###################################### // Enum declarations // ###################################### -enum_field_declaration = @{ identifier } -enum_declaration = { doc_comment* ~ ENUM_KEYWORD ~ identifier ~ BLOCK_OPEN ~ ((enum_field_declaration | ( "@@" ~ directive )) ~ NEWLINE)* ~ BLOCK_CLOSE } +enum_field_declaration = ${ identifier ~ NEWLINE } +enum_declaration = { doc_comment* ~ ENUM_KEYWORD ~ identifier ~ BLOCK_OPEN ~ (enum_field_declaration | block_level_directive)* ~ BLOCK_CLOSE } // ###################################### // Source block diff --git a/libs/datamodel/core/src/ast/parser/mod.rs b/libs/datamodel/core/src/ast/parser/mod.rs index b879d1114c1d..e3bd40f504c0 100644 --- a/libs/datamodel/core/src/ast/parser/mod.rs +++ b/libs/datamodel/core/src/ast/parser/mod.rs @@ -287,7 +287,11 @@ fn parse_enum(token: &pest::iterators::Pair<'_, Rule>) -> Enum { Rule::ENUM_KEYWORD => { }, Rule::identifier => name = Some(current.to_id()), Rule::directive => directives.push(parse_directive(¤t)), - Rule::enum_field_declaration => values.push(EnumValue { name: current.as_str().to_string(), span: Span::from_pest(current.as_span()) }), + Rule::enum_field_declaration => { + let name_token = current.into_inner().next().unwrap(); + let enum_value_name = name_token.as_str().to_string(); + values.push(EnumValue { name: enum_value_name.as_str().to_string(), span: Span::from_pest(name_token.as_span()) }) + }, Rule::doc_comment => comments.push(parse_doc_comment(¤t)), _ => unreachable!("Encountered impossible enum declaration during parsing: {:?}", current.tokens()) } @@ -486,6 +490,7 @@ fn rule_to_string(rule: Rule) -> &'static str { Rule::source_block => "source definition", Rule::generator_block => "generator definition", Rule::enum_field_declaration => "enum field declaration", + Rule::block_level_directive => "block level directive", Rule::EOI => "end of input", Rule::identifier => "alphanumeric identifier", Rule::numeric_literal => "numeric literal", diff --git a/libs/datamodel/core/tests/parsing/nice_errors.rs b/libs/datamodel/core/tests/parsing/nice_errors.rs index 5a303ea94db1..dacf9af72a70 100644 --- a/libs/datamodel/core/tests/parsing/nice_errors.rs +++ b/libs/datamodel/core/tests/parsing/nice_errors.rs @@ -62,8 +62,12 @@ fn nice_error_on_incorrect_enum_field() { let error = parse_error(dml); error.assert_is(DatamodelError::new_parser_error( - &vec!["End of block (\"}\")", "enum field declaration"], - Span::new(26, 26), + &vec![ + "End of block (\"}\")", + "block level directive", + "enum field declaration", + ], + Span::new(25, 25), )); } From b42b20a7873f9f893ab6205603993d9fb4088918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Bo=CC=88hm?= Date: Wed, 29 Jan 2020 13:45:03 +0100 Subject: [PATCH 11/14] we don't want to deal with until_end_of_line tokens --- libs/datamodel/core/src/ast/parser/datamodel.pest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/datamodel/core/src/ast/parser/datamodel.pest b/libs/datamodel/core/src/ast/parser/datamodel.pest index cdaca28fa797..096c935b36d4 100644 --- a/libs/datamodel/core/src/ast/parser/datamodel.pest +++ b/libs/datamodel/core/src/ast/parser/datamodel.pest @@ -12,7 +12,7 @@ // Treat every whitespace the same for now. WHITESPACE = @{ SPACE_SEPARATOR | "\t" } -UNTIL_END_OF_LINE = @{ WHITESPACE* ~ NEWLINE } +UNTIL_END_OF_LINE = _{ WHITESPACE* ~ NEWLINE } BLOCK_OPEN = @{ "{" ~ UNTIL_END_OF_LINE } BLOCK_CLOSE = @{ "}" } MODEL_KEYWORD = @{ "model" } From 4067754c0e263bc3761532da74a3a8ed2c9fd7f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Bo=CC=88hm?= Date: Wed, 18 Mar 2020 15:11:12 +0100 Subject: [PATCH 12/14] fix enum value directives --- libs/datamodel/core/src/ast/parser/datamodel.pest | 4 ++-- libs/datamodel/core/tests/base/duplicates.rs | 2 +- libs/datamodel/core/tests/parsing/nice_errors.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/datamodel/core/src/ast/parser/datamodel.pest b/libs/datamodel/core/src/ast/parser/datamodel.pest index 12599454db2e..bfee9d7cc827 100644 --- a/libs/datamodel/core/src/ast/parser/datamodel.pest +++ b/libs/datamodel/core/src/ast/parser/datamodel.pest @@ -109,8 +109,8 @@ model_declaration = { (UNTIL_END_OF_LINE | doc_comment)* ~ (MODEL_KEYWORD | TYPE // ###################################### // Enum declarations // ###################################### -enum_field_declaration = ${ identifier ~ ( "@" ~ directive )* ~ NEWLINE } -enum_declaration = { doc_comment* ~ ENUM_KEYWORD ~ identifier ~ BLOCK_OPEN ~ (enum_field_declaration | block_level_directive)* ~ BLOCK_CLOSE } +enum_field_declaration = { (identifier ~ ( "@" ~ directive )+ | identifier) } +enum_declaration = { doc_comment* ~ ENUM_KEYWORD ~ identifier ~ BLOCK_OPEN ~ (enum_field_declaration | block_level_directive | NEWLINE)* ~ BLOCK_CLOSE } // ###################################### // Source block diff --git a/libs/datamodel/core/tests/base/duplicates.rs b/libs/datamodel/core/tests/base/duplicates.rs index 8aa7cc4b5ef1..5a5e61e1575d 100644 --- a/libs/datamodel/core/tests/base/duplicates.rs +++ b/libs/datamodel/core/tests/base/duplicates.rs @@ -115,7 +115,7 @@ fn fail_on_duplicate_enum_value() { errors.assert_is(DatamodelError::new_duplicate_enum_value_error( "Role", "Moderator", - Span::new(57, 67), + Span::new(57, 66), )); } diff --git a/libs/datamodel/core/tests/parsing/nice_errors.rs b/libs/datamodel/core/tests/parsing/nice_errors.rs index 32ae5ce0e715..94357cad28f0 100644 --- a/libs/datamodel/core/tests/parsing/nice_errors.rs +++ b/libs/datamodel/core/tests/parsing/nice_errors.rs @@ -69,7 +69,7 @@ fn nice_error_on_incorrect_enum_field() { "block level directive", "enum field declaration", ], - Span::new(25, 25), + Span::new(26, 26), )); } From e30e71ce26f278fc8adc9f612adbeb07c914f2a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Bo=CC=88hm?= Date: Wed, 18 Mar 2020 17:25:27 +0100 Subject: [PATCH 13/14] fix block level directives for enums --- libs/datamodel/core/src/ast/parser/mod.rs | 39 +++++----- .../migration/datamodel_differ/top_level.rs | 72 +++++++++++++------ 2 files changed, 70 insertions(+), 41 deletions(-) diff --git a/libs/datamodel/core/src/ast/parser/mod.rs b/libs/datamodel/core/src/ast/parser/mod.rs index 86846dae3b03..5964ed4e5e4d 100644 --- a/libs/datamodel/core/src/ast/parser/mod.rs +++ b/libs/datamodel/core/src/ast/parser/mod.rs @@ -111,24 +111,25 @@ fn doc_comments_to_string(comments: &[String]) -> Option { // Directive parsing -fn parse_directive_arg(token: &pest::iterators::Pair<'_, Rule>) -> Argument { +fn parse_directive(token: &pest::iterators::Pair<'_, Rule>) -> Directive { let mut name: Option = None; - let mut argument: Option = None; + let mut arguments: Vec = vec![]; match_children! { token, current, - Rule::argument_name => name = Some(current.to_id()), - Rule::argument_value => argument = Some(parse_arg_value(¤t)), - _ => unreachable!("Encountered impossible directive argument during parsing: {:?}", current.tokens()) + Rule::directive => return parse_directive(¤t), + Rule::directive_name => name = Some(current.to_id()), + Rule::directive_arguments => parse_directive_args(¤t, &mut arguments), + _ => unreachable!("Encountered impossible directive during parsing: {:?} \n {:?}", token, current.tokens()) }; - match (name, argument) { - (Some(name), Some(value)) => Argument { + match name { + Some(name) => Directive { name, - value, + arguments, span: Span::from_pest(token.as_span()), }, _ => panic!( - "Encountered impossible directive arg during parsing: {:?}", + "Encountered impossible type during parsing: {:?}", token.as_str() ), } @@ -148,24 +149,24 @@ fn parse_directive_args(token: &pest::iterators::Pair<'_, Rule>, arguments: &mut } } -fn parse_directive(token: &pest::iterators::Pair<'_, Rule>) -> Directive { +fn parse_directive_arg(token: &pest::iterators::Pair<'_, Rule>) -> Argument { let mut name: Option = None; - let mut arguments: Vec = vec![]; + let mut argument: Option = None; match_children! { token, current, - Rule::directive_name => name = Some(current.to_id()), - Rule::directive_arguments => parse_directive_args(¤t, &mut arguments), - _ => unreachable!("Encountered impossible directive during parsing: {:?}", current.tokens()) + Rule::argument_name => name = Some(current.to_id()), + Rule::argument_value => argument = Some(parse_arg_value(¤t)), + _ => unreachable!("Encountered impossible directive argument during parsing: {:?}", current.tokens()) }; - match name { - Some(name) => Directive { + match (name, argument) { + (Some(name), Some(value)) => Argument { name, - arguments, + value, span: Span::from_pest(token.as_span()), }, _ => panic!( - "Encountered impossible type during parsing: {:?}", + "Encountered impossible directive arg during parsing: {:?}", token.as_str() ), } @@ -301,7 +302,7 @@ fn parse_enum(token: &pest::iterators::Pair<'_, Rule>) -> Result { }, Rule::identifier => name = Some(current.to_id()), - Rule::directive => directives.push(parse_directive(¤t)), + Rule::block_level_directive => directives.push(parse_directive(¤t)), Rule::enum_field_declaration => { match parse_enum_value(¤t) { Ok(enum_value) => values.push(enum_value), diff --git a/migration-engine/core/src/migration/datamodel_differ/top_level.rs b/migration-engine/core/src/migration/datamodel_differ/top_level.rs index 4c4c03533a7b..51249872ebe6 100644 --- a/migration-engine/core/src/migration/datamodel_differ/top_level.rs +++ b/migration-engine/core/src/migration/datamodel_differ/top_level.rs @@ -97,29 +97,37 @@ impl<'a> TopDiffer<'a> { pub(crate) fn created_type_aliases(&self) -> impl Iterator { self.next_type_aliases().filter(move |next_type_alias| { self.previous_type_aliases() - .find(|previous_type_alias| type_aliases_match(previous_type_alias, next_type_alias)) + .find(|previous_type_alias| { + type_aliases_match(previous_type_alias, next_type_alias) + }) .is_none() }) } /// Iterator over the custom types present in `previous` but not `next`. pub(crate) fn deleted_type_aliases(&self) -> impl Iterator { - self.previous_type_aliases().filter(move |previous_type_alias| { - self.next_type_aliases() - .find(|next_type_alias| type_aliases_match(previous_type_alias, next_type_alias)) - .is_none() - }) + self.previous_type_aliases() + .filter(move |previous_type_alias| { + self.next_type_aliases() + .find(|next_type_alias| { + type_aliases_match(previous_type_alias, next_type_alias) + }) + .is_none() + }) } pub(crate) fn type_alias_pairs(&self) -> impl Iterator> { - self.previous_type_aliases().filter_map(move |previous_type_alias| { - self.next_type_aliases() - .find(|next_type_alias| type_aliases_match(previous_type_alias, next_type_alias)) - .map(|next_type_alias| FieldDiffer { - previous: previous_type_alias, - next: next_type_alias, - }) - }) + self.previous_type_aliases() + .filter_map(move |previous_type_alias| { + self.next_type_aliases() + .find(|next_type_alias| { + type_aliases_match(previous_type_alias, next_type_alias) + }) + .map(|next_type_alias| FieldDiffer { + previous: previous_type_alias, + next: next_type_alias, + }) + }) } fn previous_sources(&self) -> impl Iterator { @@ -210,9 +218,13 @@ mod tests { author User } - enum Stays { A } + enum Stays { + A + } - enum ToBeDeleted { B } + enum ToBeDeleted { + B + } "#; let previous = parse(previous).unwrap(); let next = r#" @@ -225,9 +237,13 @@ mod tests { id Int @id } - enum Stays { A } + enum Stays { + A + } - enum NewEnum { B } + enum NewEnum { + B + } "#; let next = parse(next).unwrap(); @@ -236,10 +252,16 @@ mod tests { next: &next, }; - let created_models: Vec<&str> = differ.created_models().map(|model| model.name.name.as_str()).collect(); + let created_models: Vec<&str> = differ + .created_models() + .map(|model| model.name.name.as_str()) + .collect(); assert_eq!(created_models, &["Author"]); - let deleted_models: Vec<&str> = differ.deleted_models().map(|model| model.name.name.as_str()).collect(); + let deleted_models: Vec<&str> = differ + .deleted_models() + .map(|model| model.name.name.as_str()) + .collect(); assert_eq!(deleted_models, &["User"]); let model_pairs: Vec<(&str, &str)> = differ @@ -253,10 +275,16 @@ mod tests { .collect(); assert_eq!(model_pairs, &[("Blog", "Blog")]); - let created_enums: Vec<&str> = differ.created_enums().map(|enm| enm.name.name.as_str()).collect(); + let created_enums: Vec<&str> = differ + .created_enums() + .map(|enm| enm.name.name.as_str()) + .collect(); assert_eq!(created_enums, &["NewEnum"]); - let deleted_enums: Vec<&str> = differ.deleted_enums().map(|enm| enm.name.name.as_str()).collect(); + let deleted_enums: Vec<&str> = differ + .deleted_enums() + .map(|enm| enm.name.name.as_str()) + .collect(); assert_eq!(deleted_enums, &["ToBeDeleted"]); let enum_pairs: Vec<(&str, &str)> = differ From 979b25d000a564e3f52ed4c4d31672dff401ddb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Bo=CC=88hm?= Date: Thu, 19 Mar 2020 15:10:33 +0100 Subject: [PATCH 14/14] fix introspection tests --- .../mysql/relations_mysql.rs | 10 ++++--- .../mysql/tables_mysql.rs | 3 ++- .../postgres/relations_postgres.rs | 26 ++++++++++++++----- .../relations_with_compound_fk_postgres.rs | 4 +-- .../remapping_database_names_postgres.rs | 2 +- .../postgres/tables_postgres.rs | 3 ++- .../sqlite/relations_sqlite.rs | 6 ++--- .../sqlite/tables_sqlite.rs | 8 ++++-- 8 files changed, 41 insertions(+), 21 deletions(-) diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/relations_mysql.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/relations_mysql.rs index 338f80fe74c2..e7e8df7e9434 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/relations_mysql.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/relations_mysql.rs @@ -64,7 +64,9 @@ async fn introspecting_two_one_to_one_relations_between_the_same_models_should_w .execute_with_schema( |migration| { migration.change_table("User", |t| { - t.inject_custom("ADD CONSTRAINT `post_fk` FOREIGN KEY(`post_id`) REFERENCES `Post`(`id`)"); + t.inject_custom( + "ADD CONSTRAINT `post_fk` FOREIGN KEY(`post_id`) REFERENCES `Post`(`id`)", + ); }); }, api.db_name(), @@ -74,13 +76,13 @@ async fn introspecting_two_one_to_one_relations_between_the_same_models_should_w let dm = r#" model Post { id Int @id @default(autoincrement()) - user_id User @relation("Post_user_idToUser") + user_id User @relation("Post_user_idToUser", references: [id]) user User? @relation("PostToUser_post_id") } model User { id Int @id @default(autoincrement()) - post_id Post @relation("PostToUser_post_id") + post_id Post @relation("PostToUser_post_id", references: [id]) post Post? @relation("Post_user_idToUser") } "#; @@ -520,7 +522,7 @@ async fn introspecting_id_fields_with_foreign_key_should_ignore_the_relation(api let dm = r#" model Post { test String - /// This used to be part of a relation to User + // This used to be part of a relation to User user_id Int @id } diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/tables_mysql.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/tables_mysql.rs index 37e8ff394bca..bdba5b857976 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/tables_mysql.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/tables_mysql.rs @@ -1,5 +1,6 @@ use crate::*; use barrel::types; +use pretty_assertions::assert_eq; use test_harness::*; #[test_each_connector(tags("mysql"))] @@ -281,7 +282,7 @@ async fn introspecting_a_table_without_uniques_should_comment_it_out(api: &TestA }) .await; - let dm = "/// The underlying table does not contain a unique identifier and can therefore currently not be handled.\n// model Post {\n // id Int\n // user_id User\n// }\n\nmodel User {\n id Int @default(autoincrement()) @id\n}"; + let dm = "// The underlying table does not contain a unique identifier and can therefore currently not be handled.\n// model Post {\n // id Int\n // user_id User @relation(references: [id])\n// }\n\nmodel User {\n id Int @default(autoincrement()) @id\n}"; let result = dbg!(api.introspect().await); assert_eq!(&result, dm); diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/relations_postgres.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/relations_postgres.rs index fe3e71f6f5f2..dda4d8bc7e83 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/relations_postgres.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/relations_postgres.rs @@ -12,7 +12,10 @@ async fn introspecting_a_one_to_one_req_relation_should_work(api: &TestApi) { }); migration.create_table("Post", |t| { t.add_column("id", types::primary()); - t.add_column("user_id", types::foreign("User", "id").nullable(false).unique(true)); + t.add_column( + "user_id", + types::foreign("User", "id").nullable(false).unique(true), + ); }); }) .await; @@ -42,10 +45,16 @@ async fn introspecting_two_one_to_one_relations_between_the_same_models_should_w }); migration.create_table("Post", |t| { t.add_column("id", types::primary()); - t.add_column("user_id", types::foreign("User", "id").unique(true).nullable(false)); + t.add_column( + "user_id", + types::foreign("User", "id").unique(true).nullable(false), + ); }); migration.change_table("User", |t| { - t.add_column("post_id", types::foreign("Post", "id").unique(true).nullable(false)); + t.add_column( + "post_id", + types::foreign("Post", "id").unique(true).nullable(false), + ); }); }) .await; @@ -53,13 +62,13 @@ async fn introspecting_two_one_to_one_relations_between_the_same_models_should_w let dm = r#" model Post { id Int @id @default(autoincrement()) - user_id User @relation("Post_user_idToUser") + user_id User @relation("Post_user_idToUser", references: [id]) user User? @relation("PostToUser_post_id") } model User { id Int @id @default(autoincrement()) - post_id Post @relation("PostToUser_post_id") + post_id Post @relation("PostToUser_post_id", references: [id]) post Post? @relation("Post_user_idToUser") } "#; @@ -77,7 +86,10 @@ async fn introspecting_a_one_to_one_relation_should_work(api: &TestApi) { }); migration.create_table("Post", |t| { t.add_column("id", types::primary()); - t.add_column("user_id", types::foreign("User", "id").unique(true).nullable(true)); + t.add_column( + "user_id", + types::foreign("User", "id").unique(true).nullable(true), + ); }); }) .await; @@ -500,7 +512,7 @@ async fn introspecting_id_fields_with_foreign_key_should_ignore_the_relation(api let dm = r#" model Post { test String - /// This used to be part of a relation to User + // This used to be part of a relation to User user_id Int @id } diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/relations_with_compound_fk_postgres.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/relations_with_compound_fk_postgres.rs index 4f57e431346d..fee921d9838b 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/relations_with_compound_fk_postgres.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/relations_with_compound_fk_postgres.rs @@ -390,9 +390,9 @@ async fn compound_fk_pk(api: &TestApi) { model b { dummy Int - /// This used to be part of a relation to a + // This used to be part of a relation to a one Int - /// This used to be part of a relation to a + // This used to be part of a relation to a two Int @@id([dummy, one, two]) diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/remapping_database_names_postgres.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/remapping_database_names_postgres.rs index 34fa516c1f89..7f6ab8b3ebb8 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/remapping_database_names_postgres.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/remapping_database_names_postgres.rs @@ -317,7 +317,7 @@ async fn remapping_field_names_to_empty_should_comment_them_out(api: &TestApi) { }) .await; - let dm = "model User {\n /// This field was commented out because of an invalid name. Please provide a valid one that matches [a-zA-Z][a-zA-Z0-9_]*\n // 1 String @map(\"1\")\n last Int @default(autoincrement()) @id\n}"; + let dm = "model User {\n // This field was commented out because of an invalid name. Please provide a valid one that matches [a-zA-Z][a-zA-Z0-9_]*\n // 1 String @map(\"1\")\n last Int @default(autoincrement()) @id\n}"; let result = dbg!(api.introspect().await); assert_eq!(&result, dm); diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/tables_postgres.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/tables_postgres.rs index 560176f3d9b9..8d0c4ce38711 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/tables_postgres.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/tables_postgres.rs @@ -1,5 +1,6 @@ use crate::*; use barrel::types; +use pretty_assertions::assert_eq; use test_harness::*; #[test_each_connector(tags("postgres"))] @@ -273,7 +274,7 @@ async fn introspecting_a_table_without_uniques_should_comment_it_out(api: &TestA }) .await; - let dm = "/// The underlying table does not contain a unique identifier and can therefore currently not be handled.\n// model Post {\n // id Int\n // user_id User\n// }\n\nmodel User {\n id Int @default(autoincrement()) @id\n}"; + let dm = "// The underlying table does not contain a unique identifier and can therefore currently not be handled.\n// model Post {\n // id Int\n // user_id User @relation(references: [id])\n// }\n\nmodel User {\n id Int @default(autoincrement()) @id\n}"; let result = dbg!(api.introspect().await); assert_eq!(&result, dm); diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/relations_sqlite.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/relations_sqlite.rs index 4f2c1c449b14..5d64fb751f36 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/relations_sqlite.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/relations_sqlite.rs @@ -60,13 +60,13 @@ async fn introspecting_two_one_to_one_relations_between_the_same_models_should_w let dm = r#" model User { id Int @id @default(autoincrement()) - post_id Post @relation("PostToUser_post_id") + post_id Post @relation("PostToUser_post_id", references: [id]) post Post? @relation("Post_user_idToUser") } model Post { id Int @id @default(autoincrement()) - user_id User @relation("Post_user_idToUser") + user_id User @relation("Post_user_idToUser", references: [id]) user User? @relation("PostToUser_post_id") } "#; @@ -477,7 +477,7 @@ async fn introspecting_id_fields_with_foreign_key_should_ignore_the_relation(api model Post { test String - /// This used to be part of a relation to User + // This used to be part of a relation to User user_id Int @id @default(autoincrement()) } "#; diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/tables_sqlite.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/tables_sqlite.rs index 62a2b55b5304..2c484acb72fe 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/tables_sqlite.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/tables_sqlite.rs @@ -1,5 +1,6 @@ use crate::*; use barrel::types; +use pretty_assertions::assert_eq; use test_harness::*; #[test_each_connector(tags("sqlite"))] @@ -90,7 +91,10 @@ async fn introspecting_a_table_with_multi_column_unique_index_must_work(api: &Te t.add_column("id", types::primary()); t.add_column("firstname", types::text()); t.add_column("lastname", types::text()); - t.add_index("test", types::index(vec!["firstname", "lastname"]).unique(true)); + t.add_index( + "test", + types::index(vec!["firstname", "lastname"]).unique(true), + ); }); }) .await; @@ -273,7 +277,7 @@ async fn introspecting_a_table_without_uniques_should_comment_it_out(api: &TestA }) .await; - let dm = "model User {\n id Int @default(autoincrement()) @id\n}\n\n/// The underlying table does not contain a unique identifier and can therefore currently not be handled.\n// model Post {\n // id Int\n // user_id User\n// }"; + let dm = "model User {\n id Int @default(autoincrement()) @id\n}\n\n// The underlying table does not contain a unique identifier and can therefore currently not be handled.\n// model Post {\n // id Int\n // user_id User @relation(references: [id])\n// }"; let result = dbg!(api.introspect().await); assert_eq!(&result, dm);