1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
//! <div align="center">
//!   <h1><code>apollo-parser</code></h1>
//!
//!   <p>
//!     <strong>A parser for the GraphQL language.</strong>
//!   </p>
//!   <p>
//!     <a href="https://crates.io/crates/apollo-parser">
//!         <img src="https://img.shields.io/crates/v/apollo-parser.svg?style=flat-square" alt="Crates.io" />
//!     </a>
//!     <a href="https://crates.io/crates/apollo-parser">
//!         <img src="https://img.shields.io/crates/d/apollo-parser.svg?style=flat-square" alt="Download" />
//!     </a>
//!     <a href="https://docs.rs/apollo-parser/">
//!         <img src="https://img.shields.io/static/v1?label=docs&message=apollo-parser&color=blue&style=flat-square" alt="docs.rs docs" />
//!     </a>
//!   </p>
//! </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
//! * The AST produced is lossless, meaning all ignored tokens like whitespace
//! and commas are kept in the tree
//! * 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.2.1"
//! ```
//!
//! 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 be walked, extracting all the necessary
//! information. You can quick start with:
//!
//! ```rust
//! use apollo_parser::Parser;
//!
//! let input = "union SearchResult = Photo | Person | Cat | Dog";
//! let parser = Parser::new(input);
//! let ast = parser.parse();
//! ```
//!
//! `apollo-parser` is built to be error-resilient. This means we don't abort parsing (or lexing) if an error occurs. That means `parser.parse()` will always produce an AST, and it will be accompanied by any errors that are encountered:
//!
//! ```rust
//! use apollo_parser::Parser;
//!
//! let input = "union SearchResult = Photo | Person | Cat | Dog";
//! let parser = Parser::new(input);
//! let ast = parser.parse();
//!
//! // ast.errors() returns an errors slice encountered during lexing and parsing
//! assert_eq!(0, ast.errors().len());
//!
//! // ast.document() get the Document, or root node, of the tree that you can
//! // start iterating on.
//! let doc = ast.document();
//! ```
//!
//! ### Examples
//!
//! 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]
//!
//! #### Get field names in an object
//!
//! ```rust
//! use apollo_parser::{ast, Parser};
//!
//! let input = "
//! type ProductDimension {
//!   size: String
//!   weight: Float @tag(name: \"hi from inventory value type field\")
//! }
//! ";
//! let parser = Parser::new(input);
//! let ast = parser.parse();
//! 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");
//!         for field_def in object_type.fields_definition().unwrap().field_definitions() {
//!             println!("{}", field_def.name().unwrap().text()); // size weight
//!         }
//!     }
//! }
//! ```
//!
//! #### Get variables used in a query
//!
//! ```rust
//! use apollo_parser::{ast, Parser};
//!
//! let input = "
//! query GraphQuery($graph_id: ID!, $variant: String) {
//!   service(id: $graph_id) {
//!     schema(tag: $variant) {
//!       document
//!     }
//!   }
//! }
//! ";
//!
//! let parser = Parser::new(input);
//! let ast = parser.parse();
//! 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()
//!             .map(|v| v.variable_definitions())
//!             .flatten()
//!             .filter_map(|v| Some(v.variable()?.text().to_string()))
//!             .collect();
//!         assert_eq!(
//!             variables.as_slice(),
//!             ["graph_id".to_string(), "variant".to_string()]
//!         );
//!     }
//! }
//! ```
//! ## License
//! Licensed under either of
//!
//! - Apache License, Version 2.0 ([LICENSE-APACHE] or <https://www.apache.org/licenses/LICENSE-2.0>)
//! - MIT license ([LICENSE-MIT] or <https://opensource.org/licenses/MIT>)
//!
//! at your option.
//!
//! [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/
//! [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
//! [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
//! [LICENSE-APACHE]: https://github.com/apollographql/apollo-rs/blob/main/crates/apollo-parser/LICENSE-APACHE
//! [LICENSE-MIT]:https://github.com/apollographql/apollo-rs/blob/main/crates/apollo-parser/LICENSE-MIT

mod lexer;
#[cfg(test)]
mod tests;

pub mod ast;
mod error;
mod parser;

#[cfg(test)]
pub(crate) use crate::lexer::Lexer;
pub(crate) use crate::lexer::{Token, TokenKind};
pub(crate) use crate::parser::{
    SyntaxElement, SyntaxKind, SyntaxNode, SyntaxNodeChildren, SyntaxToken, TokenText,
};

pub use crate::error::Error;
pub use crate::parser::{Parser, SyntaxTree};