Merge branch 'schema-parser'
This commit is contained in:
commit
2764a6a3a3
35
async-graphql-parser/src/error.rs
Normal file
35
async-graphql-parser/src/error.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
use crate::Pos;
|
||||||
|
use pest::error::LineColLocation;
|
||||||
|
use pest::RuleType;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
/// Parser error
|
||||||
|
#[derive(Error, Debug, PartialEq)]
|
||||||
|
pub struct Error {
|
||||||
|
pub pos: Pos,
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: RuleType> From<pest::error::Error<R>> for Error {
|
||||||
|
fn from(err: pest::error::Error<R>) -> Self {
|
||||||
|
Error {
|
||||||
|
pos: {
|
||||||
|
let (line, column) = match err.line_col {
|
||||||
|
LineColLocation::Pos((line, column)) => (line, column),
|
||||||
|
LineColLocation::Span((line, column), _) => (line, column),
|
||||||
|
};
|
||||||
|
Pos { line, column }
|
||||||
|
},
|
||||||
|
message: err.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parser result
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
|
@ -3,11 +3,18 @@ extern crate pest_derive;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate thiserror;
|
extern crate thiserror;
|
||||||
|
|
||||||
mod pos;
|
|
||||||
pub mod query;
|
pub mod query;
|
||||||
|
pub mod schema;
|
||||||
|
|
||||||
|
mod error;
|
||||||
|
mod pos;
|
||||||
mod query_parser;
|
mod query_parser;
|
||||||
|
mod schema_parser;
|
||||||
|
mod utils;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
|
pub use error::{Error, Result};
|
||||||
pub use pos::{Pos, Positioned};
|
pub use pos::{Pos, Positioned};
|
||||||
pub use query_parser::{parse_query, parse_value, Error, ParsedValue, Result};
|
pub use query_parser::{parse_query, parse_value, ParsedValue};
|
||||||
|
pub use schema_parser::parse_schema;
|
||||||
pub use value::{UploadValue, Value};
|
pub use value::{UploadValue, Value};
|
||||||
|
|
|
@ -6,8 +6,11 @@ int = @{ "-"? ~ ("0" | (ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*)) }
|
||||||
float = @{ "-"? ~ int ~ "." ~ ASCII_DIGIT+ ~ exp? }
|
float = @{ "-"? ~ int ~ "." ~ ASCII_DIGIT+ ~ exp? }
|
||||||
exp = @{ ("E" | "e") ~ ("+" | "-")? ~ ASCII_DIGIT+ }
|
exp = @{ ("E" | "e") ~ ("+" | "-")? ~ ASCII_DIGIT+ }
|
||||||
|
|
||||||
string = @{ "\"" ~ string_inner ~ "\"" }
|
singleline_string = @{ "\"" ~ singleline_inner ~ "\"" }
|
||||||
string_inner = @{ (!("\"" | "\\") ~ ANY)* ~ (escape ~ string_inner)? }
|
singleline_inner = @{ (!("\"" | "\\") ~ ANY)* ~ (escape ~ singleline_inner)? }
|
||||||
|
multiline_inner = @{ (!("\"\"\"" | "\\") ~ ANY)* ~ (escape ~ multiline_inner)? }
|
||||||
|
multiline_string = @{ "\"\"\"" ~ multiline_inner ~ "\"\"\"" }
|
||||||
|
string = @{ multiline_string | singleline_string }
|
||||||
escape = @{ "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t" | unicode) }
|
escape = @{ "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t" | unicode) }
|
||||||
unicode = @{ "u" ~ ASCII_HEX_DIGIT{4} }
|
unicode = @{ "u" ~ ASCII_HEX_DIGIT{4} }
|
||||||
|
|
||||||
|
@ -29,7 +32,6 @@ nonnull_type = { (list_type | name) ~ "!" }
|
||||||
type_ = { nonnull_type | list_type | name }
|
type_ = { nonnull_type | list_type | name }
|
||||||
|
|
||||||
// query
|
// query
|
||||||
|
|
||||||
arguments = { "(" ~ pair* ~ ")" }
|
arguments = { "(" ~ pair* ~ ")" }
|
||||||
directive = { "@" ~ name ~ arguments? }
|
directive = { "@" ~ name ~ arguments? }
|
||||||
directives = { directive* }
|
directives = { directive* }
|
||||||
|
|
|
@ -1,102 +1,19 @@
|
||||||
use crate::pos::Positioned;
|
use crate::pos::Positioned;
|
||||||
use crate::query::*;
|
use crate::query::*;
|
||||||
|
use crate::utils::{to_static_str, unquote_string, PositionCalculator};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::Pos;
|
use crate::Result;
|
||||||
use arrayvec::ArrayVec;
|
|
||||||
use pest::error::LineColLocation;
|
|
||||||
use pest::iterators::Pair;
|
use pest::iterators::Pair;
|
||||||
use pest::Parser;
|
use pest::Parser;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::iter::Peekable;
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::str::Chars;
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[grammar = "query.pest"]
|
#[grammar = "query.pest"]
|
||||||
struct QueryParser;
|
struct QueryParser;
|
||||||
|
|
||||||
/// Parser error
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
|
||||||
pub struct Error {
|
|
||||||
pub pos: Pos,
|
|
||||||
pub message: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "{}", self.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<pest::error::Error<Rule>> for Error {
|
|
||||||
fn from(err: pest::error::Error<Rule>) -> Self {
|
|
||||||
Error {
|
|
||||||
pos: {
|
|
||||||
let (line, column) = match err.line_col {
|
|
||||||
LineColLocation::Pos((line, column)) => (line, column),
|
|
||||||
LineColLocation::Span((line, column), _) => (line, column),
|
|
||||||
};
|
|
||||||
Pos { line, column }
|
|
||||||
},
|
|
||||||
message: err.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parser result
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
|
||||||
|
|
||||||
pub(crate) struct PositionCalculator<'a> {
|
|
||||||
input: Peekable<Chars<'a>>,
|
|
||||||
pos: usize,
|
|
||||||
line: usize,
|
|
||||||
column: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> PositionCalculator<'a> {
|
|
||||||
fn new(input: &'a str) -> PositionCalculator<'a> {
|
|
||||||
Self {
|
|
||||||
input: input.chars().peekable(),
|
|
||||||
pos: 0,
|
|
||||||
line: 1,
|
|
||||||
column: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn step(&mut self, pair: &Pair<Rule>) -> Pos {
|
|
||||||
let pos = pair.as_span().start();
|
|
||||||
debug_assert!(pos >= self.pos);
|
|
||||||
for _ in 0..pos - self.pos {
|
|
||||||
match self.input.next() {
|
|
||||||
Some('\r') => {
|
|
||||||
if let Some(&'\n') = self.input.peek() {
|
|
||||||
self.input.next();
|
|
||||||
self.line += 1;
|
|
||||||
self.column = 1;
|
|
||||||
} else {
|
|
||||||
self.column += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some('\n') => {
|
|
||||||
self.line += 1;
|
|
||||||
self.column = 1;
|
|
||||||
}
|
|
||||||
Some(_) => {
|
|
||||||
self.column += 1;
|
|
||||||
}
|
|
||||||
None => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.pos = pos;
|
|
||||||
Pos {
|
|
||||||
line: self.line,
|
|
||||||
column: self.column,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse a GraphQL query.
|
/// Parse a GraphQL query.
|
||||||
pub fn parse_query<T: Into<String>>(input: T) -> Result<Document> {
|
pub fn parse_query<T: Into<String>>(input: T) -> Result<Document> {
|
||||||
let source = input.into();
|
let source = input.into();
|
||||||
|
@ -134,6 +51,12 @@ pub struct ParsedValue {
|
||||||
value: Value,
|
value: Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for ParsedValue {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Deref for ParsedValue {
|
impl Deref for ParsedValue {
|
||||||
type Target = Value;
|
type Target = Value;
|
||||||
|
|
||||||
|
@ -365,14 +288,8 @@ fn parse_value2(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Value>
|
||||||
Rule::float => Value::Float(pair.as_str().parse().unwrap()),
|
Rule::float => Value::Float(pair.as_str().parse().unwrap()),
|
||||||
Rule::int => Value::Int(pair.as_str().parse().unwrap()),
|
Rule::int => Value::Int(pair.as_str().parse().unwrap()),
|
||||||
Rule::string => Value::String({
|
Rule::string => Value::String({
|
||||||
let start_pos = pair.as_span().start_pos().line_col();
|
let pos = pc.step(&pair);
|
||||||
unquote_string(
|
unquote_string(pair.as_str(), pos)?
|
||||||
to_static_str(pair.as_str()),
|
|
||||||
Pos {
|
|
||||||
line: start_pos.0,
|
|
||||||
column: start_pos.1,
|
|
||||||
},
|
|
||||||
)?
|
|
||||||
}),
|
}),
|
||||||
Rule::name => Value::Enum(to_static_str(pair.as_str())),
|
Rule::name => Value::Enum(to_static_str(pair.as_str())),
|
||||||
Rule::boolean => Value::Boolean(match pair.as_str() {
|
Rule::boolean => Value::Boolean(match pair.as_str() {
|
||||||
|
@ -405,9 +322,7 @@ fn parse_object_value(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<V
|
||||||
let mut map = BTreeMap::new();
|
let mut map = BTreeMap::new();
|
||||||
for pair in pair.into_inner() {
|
for pair in pair.into_inner() {
|
||||||
match pair.as_rule() {
|
match pair.as_rule() {
|
||||||
Rule::pair => {
|
Rule::pair => map.extend(std::iter::once(parse_object_pair(pair, pc)?)),
|
||||||
map.extend(std::iter::once(parse_object_pair(pair, pc)?));
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -418,9 +333,7 @@ fn parse_array_value(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Va
|
||||||
let mut array = Vec::new();
|
let mut array = Vec::new();
|
||||||
for pair in pair.into_inner() {
|
for pair in pair.into_inner() {
|
||||||
match pair.as_rule() {
|
match pair.as_rule() {
|
||||||
Rule::value => {
|
Rule::value => array.push(parse_value2(pair, pc)?),
|
||||||
array.push(parse_value2(pair, pc)?);
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -647,103 +560,6 @@ fn parse_fragment_definition(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_static_str(s: &str) -> &'static str {
|
|
||||||
unsafe { (s as *const str).as_ref().unwrap() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unquote_string(s: &'static str, pos: Pos) -> Result<Cow<'static, str>> {
|
|
||||||
debug_assert!(s.starts_with('"') && s.ends_with('"'));
|
|
||||||
let s = &s[1..s.len() - 1];
|
|
||||||
|
|
||||||
if !s.contains('\\') {
|
|
||||||
return Ok(Cow::Borrowed(to_static_str(s)));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut chars = s.chars();
|
|
||||||
let mut res = String::with_capacity(s.len());
|
|
||||||
let mut temp_code_point = ArrayVec::<[u8; 4]>::new();
|
|
||||||
|
|
||||||
while let Some(c) = chars.next() {
|
|
||||||
match c {
|
|
||||||
'\\' => {
|
|
||||||
match chars.next().expect("slash cant be at the end") {
|
|
||||||
c @ '"' | c @ '\\' | c @ '/' => res.push(c),
|
|
||||||
'b' => res.push('\u{0010}'),
|
|
||||||
'f' => res.push('\u{000C}'),
|
|
||||||
'n' => res.push('\n'),
|
|
||||||
'r' => res.push('\r'),
|
|
||||||
't' => res.push('\t'),
|
|
||||||
'u' => {
|
|
||||||
temp_code_point.clear();
|
|
||||||
for _ in 0..4 {
|
|
||||||
match chars.next() {
|
|
||||||
Some(inner_c) if inner_c.is_digit(16) => {
|
|
||||||
temp_code_point.push(inner_c as u8)
|
|
||||||
}
|
|
||||||
Some(inner_c) => {
|
|
||||||
return Err(Error {
|
|
||||||
pos,
|
|
||||||
message: format!(
|
|
||||||
"{} is not a valid unicode code point",
|
|
||||||
inner_c
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
return Err(Error {
|
|
||||||
pos,
|
|
||||||
message: format!(
|
|
||||||
"{} must have 4 characters after it",
|
|
||||||
unsafe {
|
|
||||||
std::str::from_utf8_unchecked(
|
|
||||||
temp_code_point.as_slice(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert our hex string into a u32, then convert that into a char
|
|
||||||
match u32::from_str_radix(
|
|
||||||
unsafe { std::str::from_utf8_unchecked(temp_code_point.as_slice()) },
|
|
||||||
16,
|
|
||||||
)
|
|
||||||
.map(std::char::from_u32)
|
|
||||||
{
|
|
||||||
Ok(Some(unicode_char)) => res.push(unicode_char),
|
|
||||||
_ => {
|
|
||||||
return Err(Error {
|
|
||||||
pos,
|
|
||||||
message: format!(
|
|
||||||
"{} is not a valid unicode code point",
|
|
||||||
unsafe {
|
|
||||||
std::str::from_utf8_unchecked(
|
|
||||||
temp_code_point.as_slice(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c => {
|
|
||||||
return Err(Error {
|
|
||||||
pos,
|
|
||||||
message: format!("bad escaped char {:?}", c),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c => res.push(c),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Cow::Owned(res))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
72
async-graphql-parser/src/schema.pest
Normal file
72
async-graphql-parser/src/schema.pest
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
WHITESPACE = _{ " " | "," | "\t" | "\u{feff}" | NEWLINE }
|
||||||
|
COMMENT = _{ "#" ~ (!"\n" ~ ANY)* }
|
||||||
|
|
||||||
|
// value
|
||||||
|
int = @{ "-"? ~ ("0" | (ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*)) }
|
||||||
|
float = @{ "-"? ~ int ~ "." ~ ASCII_DIGIT+ ~ exp? }
|
||||||
|
exp = @{ ("E" | "e") ~ ("+" | "-")? ~ ASCII_DIGIT+ }
|
||||||
|
|
||||||
|
singleline_string = @{ "\"" ~ singleline_inner ~ "\"" }
|
||||||
|
singleline_inner = @{ (!("\"" | "\\") ~ ANY)* ~ (escape ~ singleline_inner)? }
|
||||||
|
multiline_inner = @{ (!("\"\"\"" | "\\") ~ ANY)* ~ (escape ~ multiline_inner)? }
|
||||||
|
multiline_string = @{ "\"\"\"" ~ multiline_inner ~ "\"\"\"" }
|
||||||
|
string = @{ multiline_string | singleline_string }
|
||||||
|
escape = @{ "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t" | unicode) }
|
||||||
|
unicode = @{ "u" ~ ASCII_HEX_DIGIT{4} }
|
||||||
|
|
||||||
|
boolean = { "true" | "false" }
|
||||||
|
null = { "null" }
|
||||||
|
name = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHA | ASCII_DIGIT | "_")* }
|
||||||
|
|
||||||
|
array = { "[" ~ value* ~ "]" }
|
||||||
|
|
||||||
|
pair = { name ~ ":" ~ value }
|
||||||
|
object = { "{" ~ pair* ~ "}" }
|
||||||
|
|
||||||
|
value = { object | array | float | int | string | null | boolean | name }
|
||||||
|
|
||||||
|
// type
|
||||||
|
list_type = { "[" ~ type_ ~ "]" }
|
||||||
|
nonnull_type = { (list_type | name) ~ "!" }
|
||||||
|
type_ = { nonnull_type | list_type | name }
|
||||||
|
|
||||||
|
// type system
|
||||||
|
arguments = { "(" ~ pair* ~ ")" }
|
||||||
|
directive = { "@" ~ name ~ arguments? }
|
||||||
|
directives = { directive* }
|
||||||
|
schema_definition = { extend? ~ "schema" ~ directives? ~ "{" ~ root_operation_type_definition* ~ "}" }
|
||||||
|
operation_type = @{ "query" | "mutation" | "subscription" }
|
||||||
|
root_operation_type_definition = { operation_type ~ ":" ~ name }
|
||||||
|
extend = { "extend" }
|
||||||
|
sclar_type_definition = { string? ~ extend? ~ "scalar" ~ name ~ directives? }
|
||||||
|
implements_interfaces = { "implements" ~ "&"? ~ name ~ ("&" ~ name)* }
|
||||||
|
default_value = { "=" ~ value }
|
||||||
|
input_value_definition = { string? ~ name ~ ":" ~ type_ ~ default_value? ~ directives? }
|
||||||
|
arguments_definition = { "(" ~ input_value_definition* ~ ")" }
|
||||||
|
field_definition = { string? ~ name ~ arguments_definition? ~ ":" ~ type_ ~ directives? }
|
||||||
|
fields_definition = { "{" ~ field_definition* ~ "}" }
|
||||||
|
object_type_definition = { string? ~ extend? ~ "type" ~ name ~ implements_interfaces? ~ directives? ~ fields_definition? }
|
||||||
|
interface_type_definition = { string? ~ extend? ~ "interface" ~ name ~ directives? ~ fields_definition? }
|
||||||
|
union_member_types = { "=" ~ "|"? ~ name ~ ("|" ~ name)* }
|
||||||
|
union_type_definition = { string? ~ extend? ~ "union" ~ name ~ directives? ~ union_member_types? }
|
||||||
|
enum_value_definition = { string? ~ name ~ directives? }
|
||||||
|
enum_values_definition = { "{" ~ enum_value_definition* ~ "}" }
|
||||||
|
enum_type_definition = { string? ~ extend? ~ "enum" ~ name ~ directives? ~ enum_values_definition? }
|
||||||
|
input_fields_definition = { "{" ~ input_value_definition* ~ "}" }
|
||||||
|
input_type_definition = { string? ~ extend? ~ "input" ~ name ~ directives? ~ input_fields_definition? }
|
||||||
|
executable_directive_location = @{ "QUERY" | "MUTATION" | "SUBSCRIPTION" | "FIELD" | "FRAGMENT_DEFINITION" | "FRAGMENT_SPREAD" | "INLINE_FRAGMENT" }
|
||||||
|
type_system_directive_location = @{ "SCHEMA" | "SCALAR" | "OBJECT" | "FIELD_DEFINITION" | "ARGUMENT_DEFINITION" | "INTERFACE" | "UNION" | "ENUM" | "ENUM_VALUE" | "INPUT_OBJECT" | "INPUT_FIELD_DEFINITION"}
|
||||||
|
directive_location = @{ executable_directive_location | type_system_directive_location }
|
||||||
|
directive_locations = { "|"? ~ directive_location ~ ("|" ~ directive_location)* }
|
||||||
|
directive_definition = { string? ~ "directive" ~ "@" ~ name ~ arguments_definition ~ "on" ~ directive_locations }
|
||||||
|
definition = {
|
||||||
|
schema_definition |
|
||||||
|
sclar_type_definition |
|
||||||
|
object_type_definition |
|
||||||
|
interface_type_definition |
|
||||||
|
union_type_definition |
|
||||||
|
enum_type_definition |
|
||||||
|
input_type_definition |
|
||||||
|
directive_definition
|
||||||
|
}
|
||||||
|
document = { SOI ~ definition+ ~ EOI}
|
171
async-graphql-parser/src/schema.rs
Normal file
171
async-graphql-parser/src/schema.rs
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
use crate::pos::Positioned;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub enum Value {
|
||||||
|
Null,
|
||||||
|
Int(i64),
|
||||||
|
Float(f64),
|
||||||
|
String(String),
|
||||||
|
Boolean(bool),
|
||||||
|
Enum(String),
|
||||||
|
List(Vec<Value>),
|
||||||
|
Object(BTreeMap<String, Value>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum Type {
|
||||||
|
Named(String),
|
||||||
|
List(Box<Type>),
|
||||||
|
NonNull(Box<Type>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Document {
|
||||||
|
pub definitions: Vec<Positioned<Definition>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Definition {
|
||||||
|
SchemaDefinition(Positioned<SchemaDefinition>),
|
||||||
|
TypeDefinition(Positioned<TypeDefinition>),
|
||||||
|
DirectiveDefinition(Positioned<DirectiveDefinition>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SchemaDefinition {
|
||||||
|
pub extend: bool,
|
||||||
|
pub directives: Vec<Positioned<Directive>>,
|
||||||
|
pub query: Option<Positioned<String>>,
|
||||||
|
pub mutation: Option<Positioned<String>>,
|
||||||
|
pub subscription: Option<Positioned<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TypeDefinition {
|
||||||
|
Scalar(Positioned<ScalarType>),
|
||||||
|
Object(Positioned<ObjectType>),
|
||||||
|
Interface(Positioned<InterfaceType>),
|
||||||
|
Union(Positioned<UnionType>),
|
||||||
|
Enum(Positioned<EnumType>),
|
||||||
|
InputObject(Positioned<InputObjectType>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ScalarType {
|
||||||
|
pub extend: bool,
|
||||||
|
pub description: Option<Positioned<String>>,
|
||||||
|
pub name: Positioned<String>,
|
||||||
|
pub directives: Vec<Positioned<Directive>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ObjectType {
|
||||||
|
pub extend: bool,
|
||||||
|
pub description: Option<Positioned<String>>,
|
||||||
|
pub name: Positioned<String>,
|
||||||
|
pub implements_interfaces: Vec<Positioned<String>>,
|
||||||
|
pub directives: Vec<Positioned<Directive>>,
|
||||||
|
pub fields: Vec<Positioned<Field>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Field {
|
||||||
|
pub description: Option<Positioned<String>>,
|
||||||
|
pub name: Positioned<String>,
|
||||||
|
pub arguments: Vec<Positioned<InputValue>>,
|
||||||
|
pub ty: Positioned<Type>,
|
||||||
|
pub directives: Vec<Positioned<Directive>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InputValue {
|
||||||
|
pub description: Option<Positioned<String>>,
|
||||||
|
pub name: Positioned<String>,
|
||||||
|
pub ty: Positioned<Type>,
|
||||||
|
pub default_value: Option<Positioned<Value>>,
|
||||||
|
pub directives: Vec<Positioned<Directive>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InterfaceType {
|
||||||
|
pub extend: bool,
|
||||||
|
pub description: Option<Positioned<String>>,
|
||||||
|
pub name: Positioned<String>,
|
||||||
|
pub directives: Vec<Positioned<Directive>>,
|
||||||
|
pub fields: Vec<Positioned<Field>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct UnionType {
|
||||||
|
pub extend: bool,
|
||||||
|
pub description: Option<Positioned<String>>,
|
||||||
|
pub name: Positioned<String>,
|
||||||
|
pub directives: Vec<Positioned<Directive>>,
|
||||||
|
pub members: Vec<Positioned<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct EnumType {
|
||||||
|
pub extend: bool,
|
||||||
|
pub description: Option<Positioned<String>>,
|
||||||
|
pub name: Positioned<String>,
|
||||||
|
pub directives: Vec<Positioned<Directive>>,
|
||||||
|
pub values: Vec<Positioned<EnumValue>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct EnumValue {
|
||||||
|
pub description: Option<Positioned<String>>,
|
||||||
|
pub name: Positioned<String>,
|
||||||
|
pub directives: Vec<Positioned<Directive>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InputObjectType {
|
||||||
|
pub extend: bool,
|
||||||
|
pub description: Option<Positioned<String>>,
|
||||||
|
pub name: Positioned<String>,
|
||||||
|
pub directives: Vec<Positioned<Directive>>,
|
||||||
|
pub fields: Vec<Positioned<InputValue>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum DirectiveLocation {
|
||||||
|
// executable
|
||||||
|
Query,
|
||||||
|
Mutation,
|
||||||
|
Subscription,
|
||||||
|
Field,
|
||||||
|
FragmentDefinition,
|
||||||
|
FragmentSpread,
|
||||||
|
InlineFragment,
|
||||||
|
|
||||||
|
// type_system
|
||||||
|
Schema,
|
||||||
|
Scalar,
|
||||||
|
Object,
|
||||||
|
FieldDefinition,
|
||||||
|
ArgumentDefinition,
|
||||||
|
Interface,
|
||||||
|
Union,
|
||||||
|
Enum,
|
||||||
|
EnumValue,
|
||||||
|
InputObject,
|
||||||
|
InputFieldDefinition,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DirectiveDefinition {
|
||||||
|
pub description: Option<Positioned<String>>,
|
||||||
|
pub name: Positioned<String>,
|
||||||
|
pub arguments: Vec<Positioned<InputValue>>,
|
||||||
|
pub locations: Vec<Positioned<DirectiveLocation>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Directive {
|
||||||
|
pub name: Positioned<String>,
|
||||||
|
pub arguments: Vec<(Positioned<String>, Positioned<Value>)>,
|
||||||
|
}
|
761
async-graphql-parser/src/schema_parser.rs
Normal file
761
async-graphql-parser/src/schema_parser.rs
Normal file
|
@ -0,0 +1,761 @@
|
||||||
|
use crate::schema::*;
|
||||||
|
use crate::utils::{unquote_string, PositionCalculator};
|
||||||
|
use crate::{Positioned, Result};
|
||||||
|
use pest::iterators::Pair;
|
||||||
|
use pest::Parser;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[grammar = "schema.pest"]
|
||||||
|
struct SchemaParser;
|
||||||
|
|
||||||
|
/// Parse a GraphQL schema.
|
||||||
|
pub fn parse_schema<T: AsRef<str>>(input: T) -> Result<Document> {
|
||||||
|
let document_pair: Pair<Rule> = SchemaParser::parse(Rule::document, input.as_ref())?
|
||||||
|
.next()
|
||||||
|
.unwrap();
|
||||||
|
let mut definitions = Vec::new();
|
||||||
|
let mut pc = PositionCalculator::new(input.as_ref());
|
||||||
|
|
||||||
|
for pair in document_pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::definition => {
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::schema_definition => {
|
||||||
|
definitions.push(
|
||||||
|
parse_schema_definition(pair, &mut pc)?
|
||||||
|
.pack(Definition::SchemaDefinition),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Rule::sclar_type_definition => definitions.push(
|
||||||
|
parse_scalar_type(pair, &mut pc)?
|
||||||
|
.pack(TypeDefinition::Scalar)
|
||||||
|
.pack(Definition::TypeDefinition),
|
||||||
|
),
|
||||||
|
Rule::object_type_definition => definitions.push(
|
||||||
|
parse_object_type(pair, &mut pc)?
|
||||||
|
.pack(TypeDefinition::Object)
|
||||||
|
.pack(Definition::TypeDefinition),
|
||||||
|
),
|
||||||
|
Rule::interface_type_definition => definitions.push(
|
||||||
|
parse_interface_type(pair, &mut pc)?
|
||||||
|
.pack(TypeDefinition::Interface)
|
||||||
|
.pack(Definition::TypeDefinition),
|
||||||
|
),
|
||||||
|
Rule::union_type_definition => definitions.push(
|
||||||
|
parse_union_type(pair, &mut pc)?
|
||||||
|
.pack(TypeDefinition::Union)
|
||||||
|
.pack(Definition::TypeDefinition),
|
||||||
|
),
|
||||||
|
Rule::enum_type_definition => definitions.push(
|
||||||
|
parse_enum_type(pair, &mut pc)?
|
||||||
|
.pack(TypeDefinition::Enum)
|
||||||
|
.pack(Definition::TypeDefinition),
|
||||||
|
),
|
||||||
|
Rule::input_type_definition => definitions.push(
|
||||||
|
parse_input_type(pair, &mut pc)?
|
||||||
|
.pack(TypeDefinition::InputObject)
|
||||||
|
.pack(Definition::TypeDefinition),
|
||||||
|
),
|
||||||
|
Rule::directive_definition => definitions.push(
|
||||||
|
parse_directive_definition(pair, &mut pc)?
|
||||||
|
.pack(Definition::DirectiveDefinition),
|
||||||
|
),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rule::EOI => {}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Document { definitions })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_schema_definition(
|
||||||
|
pair: Pair<Rule>,
|
||||||
|
pc: &mut PositionCalculator,
|
||||||
|
) -> Result<Positioned<SchemaDefinition>> {
|
||||||
|
let pos = pc.step(&pair);
|
||||||
|
let mut extend = false;
|
||||||
|
let mut directives = None;
|
||||||
|
let mut query = None;
|
||||||
|
let mut mutation = None;
|
||||||
|
let mut subscription = None;
|
||||||
|
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::extend => extend = true,
|
||||||
|
Rule::directives => directives = Some(parse_directives(pair, pc)?),
|
||||||
|
Rule::root_operation_type_definition => {
|
||||||
|
let mut op_name = None;
|
||||||
|
let mut ty_name = None;
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::operation_type => op_name = Some(pair.as_str().to_string()),
|
||||||
|
Rule::name => {
|
||||||
|
ty_name =
|
||||||
|
Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair)))
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match op_name.as_deref() {
|
||||||
|
Some("query") => query = ty_name,
|
||||||
|
Some("mutation") => mutation = ty_name,
|
||||||
|
Some("subscription") => subscription = ty_name,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Positioned::new(
|
||||||
|
SchemaDefinition {
|
||||||
|
extend,
|
||||||
|
directives: directives.unwrap_or_default(),
|
||||||
|
query,
|
||||||
|
mutation,
|
||||||
|
subscription,
|
||||||
|
},
|
||||||
|
pos,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_directive(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Positioned<Directive>> {
|
||||||
|
let pos = pc.step(&pair);
|
||||||
|
let mut name = None;
|
||||||
|
let mut arguments = None;
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::name => {
|
||||||
|
let pos = pc.step(&pair);
|
||||||
|
name = Some(Positioned::new(pair.as_str().to_string(), pos))
|
||||||
|
}
|
||||||
|
Rule::arguments => arguments = Some(parse_arguments(pair, pc)?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Positioned::new(
|
||||||
|
Directive {
|
||||||
|
name: name.unwrap(),
|
||||||
|
arguments: arguments.unwrap_or_default(),
|
||||||
|
},
|
||||||
|
pos,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_directives(
|
||||||
|
pair: Pair<Rule>,
|
||||||
|
pc: &mut PositionCalculator,
|
||||||
|
) -> Result<Vec<Positioned<Directive>>> {
|
||||||
|
let mut directives = Vec::new();
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::directive => directives.push(parse_directive(pair, pc)?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(directives)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_arguments(
|
||||||
|
pair: Pair<Rule>,
|
||||||
|
pc: &mut PositionCalculator,
|
||||||
|
) -> Result<Vec<(Positioned<String>, Positioned<Value>)>> {
|
||||||
|
let mut arguments = Vec::new();
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::pair => arguments.extend(std::iter::once(parse_pair(pair, pc)?)),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_pair(
|
||||||
|
pair: Pair<Rule>,
|
||||||
|
pc: &mut PositionCalculator,
|
||||||
|
) -> Result<(Positioned<String>, Positioned<Value>)> {
|
||||||
|
let mut name = None;
|
||||||
|
let mut value = None;
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::name => name = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair))),
|
||||||
|
Rule::value => {
|
||||||
|
value = {
|
||||||
|
let pos = pc.step(&pair);
|
||||||
|
Some(Positioned::new(parse_value(pair, pc)?, pos))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((name.unwrap(), value.unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_value(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Value> {
|
||||||
|
let pair = pair.into_inner().next().unwrap();
|
||||||
|
Ok(match pair.as_rule() {
|
||||||
|
Rule::object => parse_object_value(pair, pc)?,
|
||||||
|
Rule::array => parse_array_value(pair, pc)?,
|
||||||
|
Rule::float => Value::Float(pair.as_str().parse().unwrap()),
|
||||||
|
Rule::int => Value::Int(pair.as_str().parse().unwrap()),
|
||||||
|
Rule::string => Value::String({
|
||||||
|
let pos = pc.step(&pair);
|
||||||
|
unquote_string(pair.as_str(), pos).map(|s| s.to_string())?
|
||||||
|
}),
|
||||||
|
Rule::name => Value::Enum(pair.to_string()),
|
||||||
|
Rule::boolean => Value::Boolean(match pair.as_str() {
|
||||||
|
"true" => true,
|
||||||
|
"false" => false,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}),
|
||||||
|
Rule::null => Value::Null,
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_object_pair(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<(String, Value)> {
|
||||||
|
let mut name = None;
|
||||||
|
let mut value = None;
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::name => name = Some(pair.as_str().to_string()),
|
||||||
|
Rule::value => value = Some(parse_value(pair, pc)?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((name.unwrap(), value.unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_object_value(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Value> {
|
||||||
|
let mut map = BTreeMap::new();
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::pair => map.extend(std::iter::once(parse_object_pair(pair, pc)?)),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Value::Object(map))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_array_value(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Value> {
|
||||||
|
let mut array = Vec::new();
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::value => array.push(parse_value(pair, pc)?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Value::List(array))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_scalar_type(
|
||||||
|
pair: Pair<Rule>,
|
||||||
|
pc: &mut PositionCalculator,
|
||||||
|
) -> Result<Positioned<ScalarType>> {
|
||||||
|
let pos = pc.step(&pair);
|
||||||
|
let mut description = None;
|
||||||
|
let mut extend = false;
|
||||||
|
let mut name = None;
|
||||||
|
let mut directives = None;
|
||||||
|
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::string => {
|
||||||
|
description = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair)))
|
||||||
|
}
|
||||||
|
Rule::extend => extend = true,
|
||||||
|
Rule::name => name = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair))),
|
||||||
|
Rule::directives => directives = Some(parse_directives(pair, pc)?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Positioned::new(
|
||||||
|
ScalarType {
|
||||||
|
extend,
|
||||||
|
description,
|
||||||
|
name: name.unwrap(),
|
||||||
|
directives: directives.unwrap_or_default(),
|
||||||
|
},
|
||||||
|
pos,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_implements_interfaces(
|
||||||
|
pair: Pair<Rule>,
|
||||||
|
pc: &mut PositionCalculator,
|
||||||
|
) -> Result<Vec<Positioned<String>>> {
|
||||||
|
let mut interfaces = Vec::new();
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::name => {
|
||||||
|
interfaces.push(Positioned::new(pair.as_str().to_string(), pc.step(&pair)))
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(interfaces)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_type(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Type> {
|
||||||
|
let pair = pair.into_inner().next().unwrap();
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::nonnull_type => Ok(Type::NonNull(Box::new(parse_type(pair, pc)?))),
|
||||||
|
Rule::list_type => Ok(Type::List(Box::new(parse_type(pair, pc)?))),
|
||||||
|
Rule::name => Ok(Type::Named(pair.as_str().to_string())),
|
||||||
|
Rule::type_ => parse_type(pair, pc),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input_value_definition(
|
||||||
|
pair: Pair<Rule>,
|
||||||
|
pc: &mut PositionCalculator,
|
||||||
|
) -> Result<Positioned<InputValue>> {
|
||||||
|
let pos = pc.step(&pair);
|
||||||
|
let mut description = None;
|
||||||
|
let mut name = None;
|
||||||
|
let mut type_ = None;
|
||||||
|
let mut default_value = None;
|
||||||
|
let mut directives = None;
|
||||||
|
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::string => {
|
||||||
|
description = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair)))
|
||||||
|
}
|
||||||
|
Rule::name => name = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair))),
|
||||||
|
Rule::type_ => {
|
||||||
|
let pos = pc.step(&pair);
|
||||||
|
type_ = Some(Positioned::new(parse_type(pair, pc)?, pos));
|
||||||
|
}
|
||||||
|
Rule::default_value => {
|
||||||
|
let pos = pc.step(&pair);
|
||||||
|
default_value = Some(Positioned::new(
|
||||||
|
parse_value(pair.into_inner().next().unwrap(), pc)?,
|
||||||
|
pos,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Rule::directives => directives = Some(parse_directives(pair, pc)?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Positioned::new(
|
||||||
|
InputValue {
|
||||||
|
description,
|
||||||
|
name: name.unwrap(),
|
||||||
|
default_value,
|
||||||
|
ty: type_.unwrap(),
|
||||||
|
directives: directives.unwrap_or_default(),
|
||||||
|
},
|
||||||
|
pos,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_arguments_definition(
|
||||||
|
pair: Pair<Rule>,
|
||||||
|
pc: &mut PositionCalculator,
|
||||||
|
) -> Result<Vec<Positioned<InputValue>>> {
|
||||||
|
let mut arguments = Vec::new();
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::input_value_definition => arguments.push(parse_input_value_definition(pair, pc)?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_field_definition(
|
||||||
|
pair: Pair<Rule>,
|
||||||
|
pc: &mut PositionCalculator,
|
||||||
|
) -> Result<Positioned<Field>> {
|
||||||
|
let pos = pc.step(&pair);
|
||||||
|
let mut description = None;
|
||||||
|
let mut name = None;
|
||||||
|
let mut arguments = None;
|
||||||
|
let mut type_ = None;
|
||||||
|
let mut directives = None;
|
||||||
|
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::string => {
|
||||||
|
description = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair)))
|
||||||
|
}
|
||||||
|
Rule::name => name = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair))),
|
||||||
|
Rule::arguments_definition => arguments = Some(parse_arguments_definition(pair, pc)?),
|
||||||
|
Rule::type_ => {
|
||||||
|
let pos = pc.step(&pair);
|
||||||
|
type_ = Some(Positioned::new(parse_type(pair, pc)?, pos));
|
||||||
|
}
|
||||||
|
Rule::directives => directives = Some(parse_directives(pair, pc)?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Positioned::new(
|
||||||
|
Field {
|
||||||
|
description,
|
||||||
|
name: name.unwrap(),
|
||||||
|
arguments: arguments.unwrap_or_default(),
|
||||||
|
ty: type_.unwrap(),
|
||||||
|
directives: directives.unwrap_or_default(),
|
||||||
|
},
|
||||||
|
pos,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_fields_definition(
|
||||||
|
pair: Pair<Rule>,
|
||||||
|
pc: &mut PositionCalculator,
|
||||||
|
) -> Result<Vec<Positioned<Field>>> {
|
||||||
|
let mut fields = Vec::new();
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::field_definition => fields.push(parse_field_definition(pair, pc)?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_object_type(
|
||||||
|
pair: Pair<Rule>,
|
||||||
|
pc: &mut PositionCalculator,
|
||||||
|
) -> Result<Positioned<ObjectType>> {
|
||||||
|
let pos = pc.step(&pair);
|
||||||
|
let mut description = None;
|
||||||
|
let mut extend = false;
|
||||||
|
let mut name = None;
|
||||||
|
let mut implements_interfaces = None;
|
||||||
|
let mut directives = None;
|
||||||
|
let mut fields = None;
|
||||||
|
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::string => {
|
||||||
|
description = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair)))
|
||||||
|
}
|
||||||
|
Rule::extend => extend = true,
|
||||||
|
Rule::name => name = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair))),
|
||||||
|
Rule::implements_interfaces => {
|
||||||
|
implements_interfaces = Some(parse_implements_interfaces(pair, pc)?)
|
||||||
|
}
|
||||||
|
Rule::directives => directives = Some(parse_directives(pair, pc)?),
|
||||||
|
Rule::fields_definition => fields = Some(parse_fields_definition(pair, pc)?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Positioned::new(
|
||||||
|
ObjectType {
|
||||||
|
extend,
|
||||||
|
description,
|
||||||
|
name: name.unwrap(),
|
||||||
|
implements_interfaces: implements_interfaces.unwrap_or_default(),
|
||||||
|
directives: directives.unwrap_or_default(),
|
||||||
|
fields: fields.unwrap_or_default(),
|
||||||
|
},
|
||||||
|
pos,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_interface_type(
|
||||||
|
pair: Pair<Rule>,
|
||||||
|
pc: &mut PositionCalculator,
|
||||||
|
) -> Result<Positioned<InterfaceType>> {
|
||||||
|
let pos = pc.step(&pair);
|
||||||
|
let mut description = None;
|
||||||
|
let mut extend = false;
|
||||||
|
let mut name = None;
|
||||||
|
let mut directives = None;
|
||||||
|
let mut fields = None;
|
||||||
|
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::string => {
|
||||||
|
description = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair)))
|
||||||
|
}
|
||||||
|
Rule::extend => extend = true,
|
||||||
|
Rule::name => name = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair))),
|
||||||
|
Rule::directives => directives = Some(parse_directives(pair, pc)?),
|
||||||
|
Rule::fields_definition => fields = Some(parse_fields_definition(pair, pc)?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Positioned::new(
|
||||||
|
InterfaceType {
|
||||||
|
extend,
|
||||||
|
description,
|
||||||
|
name: name.unwrap(),
|
||||||
|
directives: directives.unwrap_or_default(),
|
||||||
|
fields: fields.unwrap_or_default(),
|
||||||
|
},
|
||||||
|
pos,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_union_members(
|
||||||
|
pair: Pair<Rule>,
|
||||||
|
pc: &mut PositionCalculator,
|
||||||
|
) -> Result<Vec<Positioned<String>>> {
|
||||||
|
let mut members = Vec::new();
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::name => members.push(Positioned::new(pair.as_str().to_string(), pc.step(&pair))),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(members)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_union_type(
|
||||||
|
pair: Pair<Rule>,
|
||||||
|
pc: &mut PositionCalculator,
|
||||||
|
) -> Result<Positioned<UnionType>> {
|
||||||
|
let pos = pc.step(&pair);
|
||||||
|
let mut description = None;
|
||||||
|
let mut extend = false;
|
||||||
|
let mut name = None;
|
||||||
|
let mut directives = None;
|
||||||
|
let mut members = None;
|
||||||
|
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::string => {
|
||||||
|
description = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair)))
|
||||||
|
}
|
||||||
|
Rule::extend => extend = true,
|
||||||
|
Rule::name => name = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair))),
|
||||||
|
Rule::directives => directives = Some(parse_directives(pair, pc)?),
|
||||||
|
Rule::union_member_types => members = Some(parse_union_members(pair, pc)?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Positioned::new(
|
||||||
|
UnionType {
|
||||||
|
extend,
|
||||||
|
description,
|
||||||
|
name: name.unwrap(),
|
||||||
|
directives: directives.unwrap_or_default(),
|
||||||
|
members: members.unwrap_or_default(),
|
||||||
|
},
|
||||||
|
pos,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_enum_value(
|
||||||
|
pair: Pair<Rule>,
|
||||||
|
pc: &mut PositionCalculator,
|
||||||
|
) -> Result<Positioned<EnumValue>> {
|
||||||
|
let pos = pc.step(&pair);
|
||||||
|
let mut description = None;
|
||||||
|
let mut name = None;
|
||||||
|
let mut directives = None;
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::string => {
|
||||||
|
description = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair)))
|
||||||
|
}
|
||||||
|
Rule::name => name = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair))),
|
||||||
|
Rule::directives => directives = Some(parse_directives(pair, pc)?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Positioned::new(
|
||||||
|
EnumValue {
|
||||||
|
description,
|
||||||
|
name: name.unwrap(),
|
||||||
|
directives: directives.unwrap_or_default(),
|
||||||
|
},
|
||||||
|
pos,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_enum_values(
|
||||||
|
pair: Pair<Rule>,
|
||||||
|
pc: &mut PositionCalculator,
|
||||||
|
) -> Result<Vec<Positioned<EnumValue>>> {
|
||||||
|
let mut values = Vec::new();
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::enum_value_definition => values.push(parse_enum_value(pair, pc)?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(values)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_enum_type(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Positioned<EnumType>> {
|
||||||
|
let pos = pc.step(&pair);
|
||||||
|
let mut description = None;
|
||||||
|
let mut extend = false;
|
||||||
|
let mut name = None;
|
||||||
|
let mut directives = None;
|
||||||
|
let mut values = None;
|
||||||
|
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::string => {
|
||||||
|
description = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair)))
|
||||||
|
}
|
||||||
|
Rule::extend => extend = true,
|
||||||
|
Rule::name => name = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair))),
|
||||||
|
Rule::directives => directives = Some(parse_directives(pair, pc)?),
|
||||||
|
Rule::enum_values_definition => values = Some(parse_enum_values(pair, pc)?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Positioned::new(
|
||||||
|
EnumType {
|
||||||
|
extend,
|
||||||
|
description,
|
||||||
|
name: name.unwrap(),
|
||||||
|
directives: directives.unwrap_or_default(),
|
||||||
|
values: values.unwrap_or_default(),
|
||||||
|
},
|
||||||
|
pos,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input_type(
|
||||||
|
pair: Pair<Rule>,
|
||||||
|
pc: &mut PositionCalculator,
|
||||||
|
) -> Result<Positioned<InputObjectType>> {
|
||||||
|
let pos = pc.step(&pair);
|
||||||
|
let mut description = None;
|
||||||
|
let mut extend = false;
|
||||||
|
let mut name = None;
|
||||||
|
let mut directives = None;
|
||||||
|
let mut fields = None;
|
||||||
|
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::string => {
|
||||||
|
description = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair)))
|
||||||
|
}
|
||||||
|
Rule::extend => extend = true,
|
||||||
|
Rule::name => name = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair))),
|
||||||
|
Rule::directives => directives = Some(parse_directives(pair, pc)?),
|
||||||
|
Rule::input_fields_definition => fields = Some(parse_arguments_definition(pair, pc)?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Positioned::new(
|
||||||
|
InputObjectType {
|
||||||
|
extend,
|
||||||
|
description,
|
||||||
|
name: name.unwrap(),
|
||||||
|
directives: directives.unwrap_or_default(),
|
||||||
|
fields: fields.unwrap_or_default(),
|
||||||
|
},
|
||||||
|
pos,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_directive_locations(
|
||||||
|
pair: Pair<Rule>,
|
||||||
|
pc: &mut PositionCalculator,
|
||||||
|
) -> Result<Vec<Positioned<DirectiveLocation>>> {
|
||||||
|
let mut locations = Vec::new();
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::directive_location => {
|
||||||
|
let pos = pc.step(&pair);
|
||||||
|
let loc = match pair.as_str() {
|
||||||
|
"QUERY" => DirectiveLocation::Query,
|
||||||
|
"MUTATION" => DirectiveLocation::Mutation,
|
||||||
|
"SUBSCRIPTION" => DirectiveLocation::Subscription,
|
||||||
|
"FIELD" => DirectiveLocation::Field,
|
||||||
|
"FRAGMENT_DEFINITION" => DirectiveLocation::FragmentDefinition,
|
||||||
|
"FRAGMENT_SPREAD" => DirectiveLocation::FragmentSpread,
|
||||||
|
"INLINE_FRAGMENT" => DirectiveLocation::InlineFragment,
|
||||||
|
"SCHEMA" => DirectiveLocation::Schema,
|
||||||
|
"SCALAR" => DirectiveLocation::Scalar,
|
||||||
|
"OBJECT" => DirectiveLocation::Object,
|
||||||
|
"FIELD_DEFINITION" => DirectiveLocation::FieldDefinition,
|
||||||
|
"ARGUMENT_DEFINITION" => DirectiveLocation::ArgumentDefinition,
|
||||||
|
"INTERFACE" => DirectiveLocation::Interface,
|
||||||
|
"UNION" => DirectiveLocation::Union,
|
||||||
|
"ENUM" => DirectiveLocation::Enum,
|
||||||
|
"ENUM_VALUE" => DirectiveLocation::EnumValue,
|
||||||
|
"INPUT_OBJECT" => DirectiveLocation::InputObject,
|
||||||
|
"INPUT_FIELD_DEFINITION" => DirectiveLocation::InputFieldDefinition,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
locations.push(Positioned::new(loc, pos));
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(locations)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_directive_definition(
|
||||||
|
pair: Pair<Rule>,
|
||||||
|
pc: &mut PositionCalculator,
|
||||||
|
) -> Result<Positioned<DirectiveDefinition>> {
|
||||||
|
let pos = pc.step(&pair);
|
||||||
|
let mut description = None;
|
||||||
|
let mut name = None;
|
||||||
|
let mut arguments = None;
|
||||||
|
let mut locations = None;
|
||||||
|
|
||||||
|
for pair in pair.into_inner() {
|
||||||
|
match pair.as_rule() {
|
||||||
|
Rule::string => {
|
||||||
|
description = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair)))
|
||||||
|
}
|
||||||
|
Rule::name => name = Some(Positioned::new(pair.as_str().to_string(), pc.step(&pair))),
|
||||||
|
Rule::arguments_definition => arguments = Some(parse_arguments_definition(pair, pc)?),
|
||||||
|
Rule::directive_locations => locations = Some(parse_directive_locations(pair, pc)?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Positioned::new(
|
||||||
|
DirectiveDefinition {
|
||||||
|
description,
|
||||||
|
name: name.unwrap(),
|
||||||
|
arguments: arguments.unwrap(),
|
||||||
|
locations: locations.unwrap(),
|
||||||
|
},
|
||||||
|
pos,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parser() {
|
||||||
|
for entry in fs::read_dir("tests/schemas").unwrap() {
|
||||||
|
if let Ok(entry) = entry {
|
||||||
|
SchemaParser::parse(Rule::document, &fs::read_to_string(entry.path()).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parser_ast() {
|
||||||
|
for entry in fs::read_dir("tests/schemas").unwrap() {
|
||||||
|
if let Ok(entry) = entry {
|
||||||
|
parse_schema(fs::read_to_string(entry.path()).unwrap()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
158
async-graphql-parser/src/utils.rs
Normal file
158
async-graphql-parser/src/utils.rs
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
use crate::{Error, Pos, Result};
|
||||||
|
use arrayvec::ArrayVec;
|
||||||
|
use pest::iterators::Pair;
|
||||||
|
use pest::RuleType;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::iter::Peekable;
|
||||||
|
use std::str::Chars;
|
||||||
|
|
||||||
|
pub struct PositionCalculator<'a> {
|
||||||
|
input: Peekable<Chars<'a>>,
|
||||||
|
pos: usize,
|
||||||
|
line: usize,
|
||||||
|
column: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PositionCalculator<'a> {
|
||||||
|
pub fn new(input: &'a str) -> PositionCalculator<'a> {
|
||||||
|
Self {
|
||||||
|
input: input.chars().peekable(),
|
||||||
|
pos: 0,
|
||||||
|
line: 1,
|
||||||
|
column: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn step<R: RuleType>(&mut self, pair: &Pair<R>) -> Pos {
|
||||||
|
let pos = pair.as_span().start();
|
||||||
|
debug_assert!(pos >= self.pos);
|
||||||
|
for _ in 0..pos - self.pos {
|
||||||
|
match self.input.next() {
|
||||||
|
Some('\r') => {
|
||||||
|
if let Some(&'\n') = self.input.peek() {
|
||||||
|
self.input.next();
|
||||||
|
self.line += 1;
|
||||||
|
self.column = 1;
|
||||||
|
} else {
|
||||||
|
self.column += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some('\n') => {
|
||||||
|
self.line += 1;
|
||||||
|
self.column = 1;
|
||||||
|
}
|
||||||
|
Some(_) => {
|
||||||
|
self.column += 1;
|
||||||
|
}
|
||||||
|
None => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.pos = pos;
|
||||||
|
Pos {
|
||||||
|
line: self.line,
|
||||||
|
column: self.column,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn to_static_str(s: &str) -> &'static str {
|
||||||
|
unsafe { (s as *const str).as_ref().unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unquote_string(s: &str, pos: Pos) -> Result<Cow<'static, str>> {
|
||||||
|
let s = if s.starts_with(r#"""""#) {
|
||||||
|
&s[3..s.len() - 3]
|
||||||
|
} else if s.starts_with('"') {
|
||||||
|
&s[1..s.len() - 1]
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
if !s.contains('\\') {
|
||||||
|
return Ok(Cow::Borrowed(to_static_str(s)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut chars = s.chars();
|
||||||
|
let mut res = String::with_capacity(s.len());
|
||||||
|
let mut temp_code_point = ArrayVec::<[u8; 4]>::new();
|
||||||
|
|
||||||
|
while let Some(c) = chars.next() {
|
||||||
|
match c {
|
||||||
|
'\\' => {
|
||||||
|
match chars.next().expect("slash cant be at the end") {
|
||||||
|
c @ '"' | c @ '\\' | c @ '/' => res.push(c),
|
||||||
|
'b' => res.push('\u{0010}'),
|
||||||
|
'f' => res.push('\u{000C}'),
|
||||||
|
'n' => res.push('\n'),
|
||||||
|
'r' => res.push('\r'),
|
||||||
|
't' => res.push('\t'),
|
||||||
|
'u' => {
|
||||||
|
temp_code_point.clear();
|
||||||
|
for _ in 0..4 {
|
||||||
|
match chars.next() {
|
||||||
|
Some(inner_c) if inner_c.is_digit(16) => {
|
||||||
|
temp_code_point.push(inner_c as u8)
|
||||||
|
}
|
||||||
|
Some(inner_c) => {
|
||||||
|
return Err(Error {
|
||||||
|
pos,
|
||||||
|
message: format!(
|
||||||
|
"{} is not a valid unicode code point",
|
||||||
|
inner_c
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(Error {
|
||||||
|
pos,
|
||||||
|
message: format!(
|
||||||
|
"{} must have 4 characters after it",
|
||||||
|
unsafe {
|
||||||
|
std::str::from_utf8_unchecked(
|
||||||
|
temp_code_point.as_slice(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert our hex string into a u32, then convert that into a char
|
||||||
|
match u32::from_str_radix(
|
||||||
|
unsafe { std::str::from_utf8_unchecked(temp_code_point.as_slice()) },
|
||||||
|
16,
|
||||||
|
)
|
||||||
|
.map(std::char::from_u32)
|
||||||
|
{
|
||||||
|
Ok(Some(unicode_char)) => res.push(unicode_char),
|
||||||
|
_ => {
|
||||||
|
return Err(Error {
|
||||||
|
pos,
|
||||||
|
message: format!(
|
||||||
|
"{} is not a valid unicode code point",
|
||||||
|
unsafe {
|
||||||
|
std::str::from_utf8_unchecked(
|
||||||
|
temp_code_point.as_slice(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c => {
|
||||||
|
return Err(Error {
|
||||||
|
pos,
|
||||||
|
message: format!("bad escaped char {:?}", c),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c => res.push(c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Cow::Owned(res))
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
a(s: """
|
||||||
|
a
|
||||||
|
b
|
||||||
|
c
|
||||||
|
""")
|
||||||
|
}
|
1
async-graphql-parser/tests/schemas/directive.graphql
Normal file
1
async-graphql-parser/tests/schemas/directive.graphql
Normal file
|
@ -0,0 +1 @@
|
||||||
|
directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
|
@ -0,0 +1,19 @@
|
||||||
|
"""
|
||||||
|
Directs the executor to include this field or fragment only when the `if` argument is true.
|
||||||
|
"""
|
||||||
|
directive @include(
|
||||||
|
"""
|
||||||
|
Included when true.
|
||||||
|
"""
|
||||||
|
if: Boolean!
|
||||||
|
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
||||||
|
|
||||||
|
"""
|
||||||
|
Directs the executor to skip this field or fragment when the `if` argument is true.
|
||||||
|
"""
|
||||||
|
directive @skip(
|
||||||
|
"""
|
||||||
|
Skipped when true.
|
||||||
|
"""
|
||||||
|
if: Boolean!
|
||||||
|
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
|
@ -0,0 +1,13 @@
|
||||||
|
"""
|
||||||
|
Directs the executor to include this field or fragment only when the `if` argument is true.
|
||||||
|
"""
|
||||||
|
directive @include("""
|
||||||
|
Included when true.
|
||||||
|
""" if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
||||||
|
|
||||||
|
"""
|
||||||
|
Directs the executor to skip this field or fragment when the `if` argument is true.
|
||||||
|
"""
|
||||||
|
directive @skip("""
|
||||||
|
Skipped when true.
|
||||||
|
""" if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
1
async-graphql-parser/tests/schemas/empty_union.graphql
Normal file
1
async-graphql-parser/tests/schemas/empty_union.graphql
Normal file
|
@ -0,0 +1 @@
|
||||||
|
union UndefinedUnion
|
4
async-graphql-parser/tests/schemas/enum.graphql
Normal file
4
async-graphql-parser/tests/schemas/enum.graphql
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
enum Site {
|
||||||
|
DESKTOP
|
||||||
|
MOBILE
|
||||||
|
}
|
3
async-graphql-parser/tests/schemas/extend_enum.graphql
Normal file
3
async-graphql-parser/tests/schemas/extend_enum.graphql
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
extend enum Site {
|
||||||
|
VR
|
||||||
|
}
|
3
async-graphql-parser/tests/schemas/extend_input.graphql
Normal file
3
async-graphql-parser/tests/schemas/extend_input.graphql
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
extend input InputType {
|
||||||
|
other: Float = 1.23e4
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
extend input InputType {
|
||||||
|
other: Float = 12300
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
extend interface Bar {
|
||||||
|
two(argument: InputType!): Type
|
||||||
|
}
|
3
async-graphql-parser/tests/schemas/extend_object.graphql
Normal file
3
async-graphql-parser/tests/schemas/extend_object.graphql
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
extend type Foo {
|
||||||
|
seven(argument: [String]): Type
|
||||||
|
}
|
1
async-graphql-parser/tests/schemas/extend_scalar.graphql
Normal file
1
async-graphql-parser/tests/schemas/extend_scalar.graphql
Normal file
|
@ -0,0 +1 @@
|
||||||
|
extend scalar CustomScalar @onScalar
|
3
async-graphql-parser/tests/schemas/implements.graphql
Normal file
3
async-graphql-parser/tests/schemas/implements.graphql
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
type Type1 implements IOne
|
||||||
|
|
||||||
|
type Type1 implements IOne & ITwo
|
|
@ -0,0 +1,2 @@
|
||||||
|
type Type1 implements & IOne & ITwo
|
||||||
|
type Type2 implements & IOne
|
|
@ -0,0 +1,3 @@
|
||||||
|
type Type1 implements IOne & ITwo
|
||||||
|
|
||||||
|
type Type2 implements IOne
|
4
async-graphql-parser/tests/schemas/input_type.graphql
Normal file
4
async-graphql-parser/tests/schemas/input_type.graphql
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
input InputType {
|
||||||
|
key: String!
|
||||||
|
answer: Int = 42
|
||||||
|
}
|
3
async-graphql-parser/tests/schemas/interface.graphql
Normal file
3
async-graphql-parser/tests/schemas/interface.graphql
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
interface Bar {
|
||||||
|
one: Type
|
||||||
|
}
|
117
async-graphql-parser/tests/schemas/kitchen-sink.graphql
Normal file
117
async-graphql-parser/tests/schemas/kitchen-sink.graphql
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
# Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
#
|
||||||
|
# This source code is licensed under the MIT license found in the
|
||||||
|
# LICENSE file in the root directory of this source tree.
|
||||||
|
|
||||||
|
schema {
|
||||||
|
query: QueryType
|
||||||
|
mutation: MutationType
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
This is a description
|
||||||
|
of the `Foo` type.
|
||||||
|
"""
|
||||||
|
type Foo implements Bar & Baz {
|
||||||
|
one: Type
|
||||||
|
two(argument: InputType!): Type
|
||||||
|
three(argument: InputType, other: String): Int
|
||||||
|
four(argument: String = "string"): String
|
||||||
|
five(argument: [String] = ["string", "string"]): String
|
||||||
|
six(argument: InputType = {key: "value"}): Type
|
||||||
|
seven(argument: Int = null): Type
|
||||||
|
}
|
||||||
|
|
||||||
|
type AnnotatedObject @onObject(arg: "value") {
|
||||||
|
annotatedField(arg: Type = "default" @onArg): Type @onField
|
||||||
|
}
|
||||||
|
|
||||||
|
type UndefinedType
|
||||||
|
|
||||||
|
extend type Foo {
|
||||||
|
seven(argument: [String]): Type
|
||||||
|
}
|
||||||
|
|
||||||
|
extend type Foo @onType
|
||||||
|
|
||||||
|
interface Bar {
|
||||||
|
one: Type
|
||||||
|
four(argument: String = "string"): String
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AnnotatedInterface @onInterface {
|
||||||
|
annotatedField(arg: Type @onArg): Type @onField
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UndefinedInterface
|
||||||
|
|
||||||
|
extend interface Bar {
|
||||||
|
two(argument: InputType!): Type
|
||||||
|
}
|
||||||
|
|
||||||
|
extend interface Bar @onInterface
|
||||||
|
|
||||||
|
union Feed = Story | Article | Advert
|
||||||
|
|
||||||
|
union AnnotatedUnion @onUnion = A | B
|
||||||
|
|
||||||
|
union AnnotatedUnionTwo @onUnion = | A | B
|
||||||
|
|
||||||
|
union UndefinedUnion
|
||||||
|
|
||||||
|
extend union Feed = Photo | Video
|
||||||
|
|
||||||
|
extend union Feed @onUnion
|
||||||
|
|
||||||
|
scalar CustomScalar
|
||||||
|
|
||||||
|
scalar AnnotatedScalar @onScalar
|
||||||
|
|
||||||
|
extend scalar CustomScalar @onScalar
|
||||||
|
|
||||||
|
enum Site {
|
||||||
|
DESKTOP
|
||||||
|
MOBILE
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AnnotatedEnum @onEnum {
|
||||||
|
ANNOTATED_VALUE @onEnumValue
|
||||||
|
OTHER_VALUE
|
||||||
|
}
|
||||||
|
|
||||||
|
enum UndefinedEnum
|
||||||
|
|
||||||
|
extend enum Site {
|
||||||
|
VR
|
||||||
|
}
|
||||||
|
|
||||||
|
extend enum Site @onEnum
|
||||||
|
|
||||||
|
input InputType {
|
||||||
|
key: String!
|
||||||
|
answer: Int = 42
|
||||||
|
}
|
||||||
|
|
||||||
|
input AnnotatedInput @onInputObject {
|
||||||
|
annotatedField: Type @onField
|
||||||
|
}
|
||||||
|
|
||||||
|
input UndefinedInput
|
||||||
|
|
||||||
|
extend input InputType {
|
||||||
|
other: Float = 1.23e4
|
||||||
|
}
|
||||||
|
|
||||||
|
extend input InputType @onInputObject
|
||||||
|
|
||||||
|
directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
||||||
|
|
||||||
|
directive @include(if: Boolean!)
|
||||||
|
on FIELD
|
||||||
|
| FRAGMENT_SPREAD
|
||||||
|
| INLINE_FRAGMENT
|
||||||
|
|
||||||
|
directive @include2(if: Boolean!) on
|
||||||
|
| FIELD
|
||||||
|
| FRAGMENT_SPREAD
|
||||||
|
| INLINE_FRAGMENT
|
|
@ -0,0 +1,106 @@
|
||||||
|
schema {
|
||||||
|
query: QueryType
|
||||||
|
mutation: MutationType
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
This is a description
|
||||||
|
of the `Foo` type.
|
||||||
|
"""
|
||||||
|
type Foo implements Bar & Baz {
|
||||||
|
one: Type
|
||||||
|
two(argument: InputType!): Type
|
||||||
|
three(argument: InputType, other: String): Int
|
||||||
|
four(argument: String = "string"): String
|
||||||
|
five(argument: [String] = ["string", "string"]): String
|
||||||
|
six(argument: InputType = {key: "value"}): Type
|
||||||
|
seven(argument: Int = null): Type
|
||||||
|
}
|
||||||
|
|
||||||
|
type AnnotatedObject @onObject(arg: "value") {
|
||||||
|
annotatedField(arg: Type = "default" @onArg): Type @onField
|
||||||
|
}
|
||||||
|
|
||||||
|
type UndefinedType
|
||||||
|
|
||||||
|
extend type Foo {
|
||||||
|
seven(argument: [String]): Type
|
||||||
|
}
|
||||||
|
|
||||||
|
extend type Foo @onType
|
||||||
|
|
||||||
|
interface Bar {
|
||||||
|
one: Type
|
||||||
|
four(argument: String = "string"): String
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AnnotatedInterface @onInterface {
|
||||||
|
annotatedField(arg: Type @onArg): Type @onField
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UndefinedInterface
|
||||||
|
|
||||||
|
extend interface Bar {
|
||||||
|
two(argument: InputType!): Type
|
||||||
|
}
|
||||||
|
|
||||||
|
extend interface Bar @onInterface
|
||||||
|
|
||||||
|
union Feed = Story | Article | Advert
|
||||||
|
|
||||||
|
union AnnotatedUnion @onUnion = A | B
|
||||||
|
|
||||||
|
union AnnotatedUnionTwo @onUnion = A | B
|
||||||
|
|
||||||
|
union UndefinedUnion
|
||||||
|
|
||||||
|
extend union Feed = Photo | Video
|
||||||
|
|
||||||
|
extend union Feed @onUnion
|
||||||
|
|
||||||
|
scalar CustomScalar
|
||||||
|
|
||||||
|
scalar AnnotatedScalar @onScalar
|
||||||
|
|
||||||
|
extend scalar CustomScalar @onScalar
|
||||||
|
|
||||||
|
enum Site {
|
||||||
|
DESKTOP
|
||||||
|
MOBILE
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AnnotatedEnum @onEnum {
|
||||||
|
ANNOTATED_VALUE @onEnumValue
|
||||||
|
OTHER_VALUE
|
||||||
|
}
|
||||||
|
|
||||||
|
enum UndefinedEnum
|
||||||
|
|
||||||
|
extend enum Site {
|
||||||
|
VR
|
||||||
|
}
|
||||||
|
|
||||||
|
extend enum Site @onEnum
|
||||||
|
|
||||||
|
input InputType {
|
||||||
|
key: String!
|
||||||
|
answer: Int = 42
|
||||||
|
}
|
||||||
|
|
||||||
|
input AnnotatedInput @onInputObject {
|
||||||
|
annotatedField: Type @onField
|
||||||
|
}
|
||||||
|
|
||||||
|
input UndefinedInput
|
||||||
|
|
||||||
|
extend input InputType {
|
||||||
|
other: Float = 12300
|
||||||
|
}
|
||||||
|
|
||||||
|
extend input InputType @onInputObject
|
||||||
|
|
||||||
|
directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
||||||
|
|
||||||
|
directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
||||||
|
|
||||||
|
directive @include2(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
3
async-graphql-parser/tests/schemas/minimal.graphql
Normal file
3
async-graphql-parser/tests/schemas/minimal.graphql
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
schema {
|
||||||
|
query: Query
|
||||||
|
}
|
1
async-graphql-parser/tests/schemas/minimal_type.graphql
Normal file
1
async-graphql-parser/tests/schemas/minimal_type.graphql
Normal file
|
@ -0,0 +1 @@
|
||||||
|
type UndefinedType
|
2
async-graphql-parser/tests/schemas/scalar_type.graphql
Normal file
2
async-graphql-parser/tests/schemas/scalar_type.graphql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
"This is the best scalar type"
|
||||||
|
scalar BestType @perfectness(value: 100500)
|
3
async-graphql-parser/tests/schemas/simple_object.graphql
Normal file
3
async-graphql-parser/tests/schemas/simple_object.graphql
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
type Foo {
|
||||||
|
bar: Type
|
||||||
|
}
|
1
async-graphql-parser/tests/schemas/union.graphql
Normal file
1
async-graphql-parser/tests/schemas/union.graphql
Normal file
|
@ -0,0 +1 @@
|
||||||
|
union Feed = Story | Article | Advert
|
|
@ -0,0 +1 @@
|
||||||
|
extend union Feed = Photo | Video
|
|
@ -8,10 +8,10 @@ Comparing Features of Other Rust GraphQL Implementations
|
||||||
|----------------|---------------|-----------------|
|
|----------------|---------------|-----------------|
|
||||||
| async/await | 👍 | ⛔️ |
|
| async/await | 👍 | ⛔️ |
|
||||||
| Rustfmt friendly(No DSL) | 👍 | ⛔️ |
|
| Rustfmt friendly(No DSL) | 👍 | ⛔️ |
|
||||||
| Boilerplate | less | some |
|
| Boilerplate | Less | Some |
|
||||||
|
| Type Safety | 👍 | 👍 |
|
||||||
| Query | 👍 | 👍 |
|
| Query | 👍 | 👍 |
|
||||||
| Mutation | 👍 | 👍 |
|
| Mutation | 👍 | 👍 |
|
||||||
| Type Safety | 👍 | 👍 |
|
|
||||||
| Interfaces | 👍 | 👍 |
|
| Interfaces | 👍 | 👍 |
|
||||||
| Union | 👍 | 👍 |
|
| Union | 👍 | 👍 |
|
||||||
| Dataloading | 👍 | 👍 |
|
| Dataloading | 👍 | 👍 |
|
||||||
|
|
Loading…
Reference in New Issue
Block a user