Skip to content

Commit

Permalink
feat: impl Iterator for errors
Browse files Browse the repository at this point in the history
fixes #139

ast.errors() now return an `impl ExactSizeIterator` which allows us to iterate, but also check for the `len()`. It should allow us to use `is_empty()` as well eventually, if / when rust-lang/rust#35428 lands.
  • Loading branch information
o0Ignition0o committed Nov 24, 2021
1 parent fcbf490 commit 75488cb
Show file tree
Hide file tree
Showing 13 changed files with 59 additions and 51 deletions.
59 changes: 34 additions & 25 deletions crates/apollo-parser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,31 @@
</div>

## Features
* Typed GraphQL AST as per [October 2021 specification]
* Error resilience
* lexing and parsing does not fail or `panic` if a lexical or a syntax error is found
* GraphQL lexer
* GraphQL parser

- Typed GraphQL AST as per [October 2021 specification]
- Error resilience
- lexing and parsing does not fail or `panic` if a lexical or a syntax error is found
- GraphQL lexer
- GraphQL parser

## Getting started

Add this to your `Cargo.toml` to start using `apollo-parser`:

```toml
# Just an example, change to the necessary package version.
[dependencies]
apollo_parser = "0.1.0"
```

Or using [cargo-edit]:

```bash
cargo add apollo_parser
```

## Usage

`apollo-parser` is built to parse both GraphQL schemas and queries according to the latest [October 2021 specification]. It produces
a typed syntax tree that then can we walked extracting all the necessary
information. You can quick start with:
Expand All @@ -63,7 +68,7 @@ fn main() {
let ast = parser.parse();

// ast.errors() returns an errors slice encountered during lexing and parsing
assert!(ast.errors().is_empty());
assert_eq!(0, ast.errors().len());

// ast.document() get the Document, or root node, of the tree that you can
// start iterating on.
Expand All @@ -73,15 +78,17 @@ fn main() {

### Examples

Two examples outlined here:
* [Get field names in an object]
* [Get variables used in a query]
Two examples outlined here:

- [Get field names in an object]
- [Get variables used in a query]

The [examples directory] in this repository has a few more useful
implementations such as:
* [using apollo-rs with miette to display error diagnostics]
* [using apollo-rs with annotate_snippets to display error diagnostics]
* [checking for unused variables]

- [using apollo-rs with miette to display error diagnostics]
- [using apollo-rs with annotate_snippets to display error diagnostics]
- [checking for unused variables]

#### Get field names in an object

Expand All @@ -97,10 +104,10 @@ fn main() {
";
let parser = Parser::new(input);
let ast = parser.parse();
assert!(ast.errors().is_empty());
assert_eq!(0, ast.errors().len());

let doc = ast.document();

for def in doc.definitions() {
if let ast::Definition::ObjectTypeDefinition(object_type) = def {
assert_eq!(object_type.name().unwrap().text(), "ProductDimension");
Expand All @@ -112,7 +119,7 @@ fn main() {
}
```

#### Get variables used in a query
#### Get variables used in a query

```rust
use apollo_parser::{ast, Parser};
Expand All @@ -127,17 +134,17 @@ fn main() {
}
}
";

let parser = Parser::new(input);
let ast = parser.parse();
assert!(&ast.errors().is_empty());
assert_eq!(0, &ast.errors().len());

let doc = ast.document();

for def in doc.definitions() {
if let ast::Definition::OperationDefinition(op_def) = def {
assert_eq!(op_def.name().unwrap().text(), "GraphQuery");

let variable_defs = op_def.variable_definitions();
let variables: Vec<String> = variable_defs
.iter()
Expand All @@ -155,6 +162,7 @@ fn main() {
```

## License

Licensed under either of

- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0)
Expand All @@ -163,16 +171,17 @@ Licensed under either of
at your option.

#### Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.

[cargo-edit]: https://github.com/killercup/cargo-edit
[apollo-rs: spec-compliant GraphQL Tools in Rust]: https://www.apollographql.com/blog/announcement/tooling/apollo-rs-graphql-tools-in-rust/
[apollo-rs: spec-compliant graphql tools in rust]: https://www.apollographql.com/blog/announcement/tooling/apollo-rs-graphql-tools-in-rust/
[examples directory]: https://github.com/apollographql/apollo-rs/tree/main/crates/apollo-parser/examples
[Get field names in an object]: https://github.com/apollographql/apollo-rs#get-field-names-in-an-object
[Get variables used in a query]: https://github.com/apollographql/apollo-rs#get-variables-used-in-a-query
[get field names in an object]: https://github.com/apollographql/apollo-rs#get-field-names-in-an-object
[get variables used in a query]: https://github.com/apollographql/apollo-rs#get-variables-used-in-a-query
[using apollo-rs with miette to display error diagnostics]: https://github.com/apollographql/apollo-rs/blob/a7f616454a53dcb8496725ceac6c63eacddefb2c/crates/apollo-parser/examples/miette.rs
[using apollo-rs with annotate_snippets to display error diagnostics]: https://github.com/apollographql/apollo-rs/blob/a7f616454a53dcb8496725ceac6c63eacddefb2c/crates/apollo-parser/examples/annotate_snippet.rs
[checking for unused variables]: https://github.com/apollographql/apollo-rs/blob/a7f616454a53dcb8496725ceac6c63eacddefb2c/crates/apollo-parser/examples/unused_vars.rs
[October 2021 specification]: https://spec.graphql.org/October2021
[october 2021 specification]: https://spec.graphql.org/October2021
2 changes: 1 addition & 1 deletion crates/apollo-parser/examples/unused_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fn are_variables_unused() {
let parser = Parser::new(&src);
let ast = parser.parse();

assert!(&ast.errors().is_empty());
assert_eq!(0, ast.errors().len());

let doc = ast.document();

Expand Down
2 changes: 1 addition & 1 deletion crates/apollo-parser/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
//! let parser = Parser::new(schema);
//! let ast = parser.parse();
//!
//! assert!(ast.errors().is_empty());
//! assert_eq!(0, ast.errors().len());
//! let document = ast.document();
//! for definition in document.definitions() {
//! match definition {
Expand Down
2 changes: 1 addition & 1 deletion crates/apollo-parser/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::fmt;
/// let parser = Parser::new(input);
/// let ast = parser.parse();
///
/// assert!(ast.errors().is_empty());
/// assert_eq!(0, ast.errors().len());
///
/// let doc = ast.document();
/// ```
Expand Down
4 changes: 2 additions & 2 deletions crates/apollo-parser/src/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ impl Lexer {
}

/// Get a reference to the lexer's tokens.
pub(crate) fn errors(&self) -> &[Error] {
self.errors.as_slice()
pub(crate) fn errors<'e>(&'e self) -> impl ExactSizeIterator<Item = &'e Error> {
self.errors.iter()
}
}

Expand Down
6 changes: 3 additions & 3 deletions crates/apollo-parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
//! let ast = parser.parse();
//!
//! // ast.errors() returns an errors slice encountered during lexing and parsing
//! assert!(ast.errors().is_empty());
//! assert_eq!(0, ast.errors().len());
//!
//! // ast.document() get the Document, or root node, of the tree that you can
//! // start iterating on.
Expand Down Expand Up @@ -94,7 +94,7 @@
//! ";
//! let parser = Parser::new(input);
//! let ast = parser.parse();
//! assert!(ast.errors().is_empty());
//! assert_eq!(0, ast.errors().len());
//!
//! let doc = ast.document();
//!
Expand Down Expand Up @@ -125,7 +125,7 @@
//!
//! let parser = Parser::new(input);
//! let ast = parser.parse();
//! assert!(&ast.errors().is_empty());
//! assert_eq!(0, ast.errors().len());
//!
//! let doc = ast.document();
//!
Expand Down
2 changes: 1 addition & 1 deletion crates/apollo-parser/src/parser/grammar/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ type Business implements NamedEntity & ValuedEntity & CatEntity {
}";
let parser = Parser::new(input);
let ast = parser.parse();
assert!(ast.errors().is_empty());
assert_eq!(0, ast.errors().len());

let doc = ast.document();

Expand Down
4 changes: 2 additions & 2 deletions crates/apollo-parser/src/parser/grammar/selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ mod test {
";
let parser = Parser::new(input);
let ast = parser.parse();
assert!(&ast.errors().is_empty());
assert_eq!(0, ast.errors().len());

let doc = ast.document();

Expand Down Expand Up @@ -129,7 +129,7 @@ query GraphQuery($graph_id: ID!, $variant: String) {
";
let parser = Parser::new(input);
let ast = parser.parse();
assert!(&ast.errors().is_empty());
assert_eq!(0, ast.errors().len());

let doc = ast.document();

Expand Down
2 changes: 1 addition & 1 deletion crates/apollo-parser/src/parser/grammar/union_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ mod test {
let input = "union SearchResult = Photo | Person | Cat | Dog";
let parser = Parser::new(input);
let ast = parser.parse();
assert!(ast.errors().is_empty());
assert_eq!(0, ast.errors().len());

let doc = ast.document();

Expand Down
2 changes: 1 addition & 1 deletion crates/apollo-parser/src/parser/grammar/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ query GraphQuery($graph_id: ID!, $variant: String) {
";
let parser = Parser::new(input);
let ast = parser.parse();
assert!(&ast.errors().is_empty());
assert_eq!(0, ast.errors().len());

let doc = ast.document();

Expand Down
6 changes: 3 additions & 3 deletions crates/apollo-parser/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub(crate) use token_text::TokenText;
/// // Parse the query, and return a SyntaxTree.
/// let ast = parser.parse();
/// // Check that are no errors. These are not part of the AST.
/// assert!(&ast.errors().is_empty());
/// assert_eq!(0, ast.errors().len());
///
/// // Get the document root node
/// let doc = ast.document();
Expand All @@ -65,7 +65,7 @@ pub(crate) use token_text::TokenText;
/// let parser = Parser::new(core_schema);
/// let ast = parser.parse();
///
/// assert!(ast.errors().is_empty());
/// assert_eq!(0, ast.errors().len());
///
/// let document = ast.document();
/// ```
Expand All @@ -91,7 +91,7 @@ impl Parser {
tokens.push(s);
}

for e in lexer.errors().to_owned() {
for e in lexer.errors().cloned() {
errors.push(e);
}

Expand Down
6 changes: 3 additions & 3 deletions crates/apollo-parser/src/parser/syntax_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ pub struct SyntaxTree {

impl SyntaxTree {
/// Get a reference to the syntax tree's errors.
pub fn errors(&self) -> &[crate::Error] {
self.errors.as_ref()
pub fn errors(&self) -> impl ExactSizeIterator<Item = &crate::Error> {
self.errors.iter()
}

/// Return the root typed `Document` node.
Expand Down Expand Up @@ -159,7 +159,7 @@ mod test {
";
let parser = Parser::new(input);
let ast = parser.parse();
assert!(ast.errors().is_empty());
assert_eq!(0, ast.errors().len());

let doc = ast.document();

Expand Down
13 changes: 6 additions & 7 deletions crates/apollo-parser/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,25 +52,24 @@ fn parser_tests() {
});
}

fn assert_errors_are_present(errors: &[Error], path: &Path) {
fn assert_errors_are_present<'e>(errors: impl ExactSizeIterator<Item = &'e Error>, path: &Path) {
assert!(
!errors.is_empty(),
errors.len() != 0,
"There should be errors in the file {:?}",
path.display()
);
}

fn assert_errors_are_absent(errors: &[Error], path: &Path) {
assert_eq!(
errors,
&[] as &[Error],
fn assert_errors_are_absent<'e>(errors: impl ExactSizeIterator<Item = &'e Error>, path: &Path) {
assert!(
errors.len() == 0,
"There should be no errors in the file {:?}",
path.display(),
);
}

/// Concatenate tokens and erorrs.
fn dump_tokens_and_errors(tokens: &[Token], errors: &[Error]) -> String {
fn dump_tokens_and_errors<'e>(tokens: &[Token], errors: impl Iterator<Item = &'e Error>) -> String {
let mut acc = String::new();
for token in tokens {
writeln!(acc, "{:?}", token).unwrap();
Expand Down

0 comments on commit 75488cb

Please sign in to comment.