Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
mavilein committed May 28, 2020
1 parent 536c606 commit 4d71317
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 11 deletions.
4 changes: 2 additions & 2 deletions libs/datamodel/core/src/ast/parser/datamodel.pest
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ type_alias = { comment_block? ~ TYPE_KEYWORD ~ non_empty_identifier ~ "=" ~ (bas
// ######################################
// Other kind of blocks
// ######################################
source_block = { comment_block? ~ DATASOURCE_KEYWORD ~ non_empty_identifier ~ BLOCK_OPEN ~ (key_value | doc_comment_and_new_line | comment_and_new_line | NEWLINE)* ~ BLOCK_CLOSE }
generator_block = { comment_block? ~ GENERATOR_KEYWORD ~ non_empty_identifier ~ BLOCK_OPEN ~ (key_value | doc_comment_and_new_line | comment_and_new_line | NEWLINE)* ~ BLOCK_CLOSE }
source_block = { comment_block? ~ DATASOURCE_KEYWORD ~ non_empty_identifier ~ BLOCK_OPEN ~ (key_value | doc_comment_and_new_line | comment_and_new_line | NEWLINE | BLOCK_LEVEL_CATCH_ALL)* ~ BLOCK_CLOSE }
generator_block = { comment_block? ~ GENERATOR_KEYWORD ~ non_empty_identifier ~ BLOCK_OPEN ~ (key_value | doc_comment_and_new_line | comment_and_new_line | NEWLINE | BLOCK_LEVEL_CATCH_ALL)* ~ BLOCK_CLOSE }
key_value = { non_empty_identifier ~ "=" ~ expression ~ NEWLINE }

// a block definition without a keyword. Is not valid. Just acts as a catch for the parser to display a nice error.
Expand Down
38 changes: 30 additions & 8 deletions libs/datamodel/core/src/ast/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,8 @@ fn parse_key_value(token: &pest::iterators::Pair<'_, Rule>) -> Argument {
}

// Source parsing
fn parse_source(token: &pest::iterators::Pair<'_, Rule>) -> SourceConfig {
fn parse_source(token: &pest::iterators::Pair<'_, Rule>) -> Result<SourceConfig, ErrorCollection> {
let mut errors = ErrorCollection::new();
let mut name: Option<Identifier> = None;
let mut properties: Vec<Argument> = vec![];
let mut comment: Option<Comment> = None;
Expand All @@ -444,16 +445,23 @@ fn parse_source(token: &pest::iterators::Pair<'_, Rule>) -> SourceConfig {
Rule::comment_block => {
comment = Some(parse_comment_block(&current))
},
Rule::BLOCK_LEVEL_CATCH_ALL => { errors.push(
DatamodelError::new_validation_error(
"This line is not a valid definition within a datasource.",
Span::from_pest(current.as_span()))
) },
_ => parsing_catch_all(&current)
};

errors.ok()?;

match name {
Some(name) => SourceConfig {
Some(name) => Ok(SourceConfig {
name,
properties,
documentation: comment,
span: Span::from_pest(token.as_span()),
},
}),
_ => panic!(
"Encountered impossible source declaration during parsing, name is missing: {:?}",
token.as_str()
Expand All @@ -462,7 +470,8 @@ fn parse_source(token: &pest::iterators::Pair<'_, Rule>) -> SourceConfig {
}

// Generator parsing
fn parse_generator(token: &pest::iterators::Pair<'_, Rule>) -> GeneratorConfig {
fn parse_generator(token: &pest::iterators::Pair<'_, Rule>) -> Result<GeneratorConfig, ErrorCollection> {
let mut errors = ErrorCollection::new();
let mut name: Option<Identifier> = None;
let mut properties: Vec<Argument> = vec![];
let mut comments: Vec<String> = Vec::new();
Expand All @@ -472,16 +481,23 @@ fn parse_generator(token: &pest::iterators::Pair<'_, Rule>) -> GeneratorConfig {
Rule::key_value => properties.push(parse_key_value(&current)),
Rule::doc_comment => comments.push(parse_doc_comment(&current)),
Rule::doc_comment_and_new_line => comments.push(parse_doc_comment(&current)),
Rule::BLOCK_LEVEL_CATCH_ALL => { errors.push(
DatamodelError::new_validation_error(
"This line is not a valid definition within a generator.",
Span::from_pest(current.as_span()))
) },
_ => parsing_catch_all(&current)
};

errors.ok()?;

match name {
Some(name) => GeneratorConfig {
Some(name) => Ok(GeneratorConfig {
name,
properties,
documentation: doc_comments_to_string(&comments),
span: Span::from_pest(token.as_span()),
},
}),
_ => panic!(
"Encountered impossible generator declaration during parsing, name is missing: {:?}",
token.as_str()
Expand Down Expand Up @@ -566,8 +582,14 @@ pub fn parse(datamodel_string: &str) -> Result<SchemaAst, ErrorCollection> {
Ok(enm) => top_level_definitions.push(Top::Enum(enm)),
Err(mut err) => errors.append(&mut err)
},
Rule::source_block => top_level_definitions.push(Top::Source(parse_source(&current))),
Rule::generator_block => top_level_definitions.push(Top::Generator(parse_generator(&current))),
Rule::source_block => match parse_source(&current) {
Ok(source) => top_level_definitions.push(Top::Source(source)),
Err(mut err) => errors.append(&mut err)
},
Rule::generator_block => match parse_generator(&current) {
Ok(generator) => top_level_definitions.push(Top::Generator(generator)),
Err(mut err) => errors.append(&mut err)
},
Rule::type_alias => top_level_definitions.push(Top::Type(parse_type(&current))),
Rule::comment_block => (),
Rule::EOI => {},
Expand Down
37 changes: 36 additions & 1 deletion libs/datamodel/core/tests/parsing/nice_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ fn optional_list_fields_must_error() {
}

#[test]
fn incomplete_last_line_must_error_nicely() {
fn invalid_lines_at_the_top_level_must_render_nicely() {
// https://github.com/prisma/vscode/issues/140
// If a user types on the very last line we did not error nicely.
// a new line fixed the problem but this is not nice.
Expand All @@ -209,6 +209,41 @@ fn incomplete_last_line_must_error_nicely() {
));
}

#[test]
fn invalid_lines_in_datasources_must_render_nicely() {
let dml = r#"
datasource mydb {
provider = "postgres"
url = "postgresql://localhost"
this is an invalid line
}
"#;

let error = parse_error(dml);

error.assert_is(DatamodelError::new_validation_error(
"This line is not a valid definition within a datasource.",
Span::new(100, 124),
));
}

#[test]
fn invalid_lines_in_generators_must_render_nicely() {
let dml = r#"
generator js {
provider = "js"
this is an invalid line
}
"#;

let error = parse_error(dml);

error.assert_is(DatamodelError::new_validation_error(
"This line is not a valid definition within a generator.",
Span::new(52, 76),
));
}

#[test]
fn invalid_field_line_must_error_nicely() {
// https://github.com/prisma/vscode/issues/140
Expand Down
38 changes: 38 additions & 0 deletions libs/datamodel/core/tests/reformat/reformat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,44 @@ model Post {
assert_reformat(input, input);
}

#[test]
fn reformatting_an_invalid_datasource_block_must_work() {
let input = r#"datasource db {
provider = "postgresql"
url = env("POSTGRESQL_URL")
test
}
"#;

let expected = r#"datasource db {
provider = "postgresql"
url = env("POSTGRESQL_URL")
test
}
"#;

assert_reformat(input, expected);
}

#[test]
fn reformatting_an_invalid_generator_block_must_work() {
let input = r#"generator js {
provider = "js"
output = "../wherever"
test
}
"#;

let expected = r#"generator js {
provider = "js"
output = "../wherever"
test
}
"#;

assert_reformat(input, expected);
}

#[test]
fn incomplete_field_definitions_in_a_model_must_not_get_removed() {
// incomplete field definitions are handled in a special way in the grammar to allow nice errors. See `nice_error.rs:nice_error_missing_type`
Expand Down

0 comments on commit 4d71317

Please sign in to comment.