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]
|
||||
extern crate thiserror;
|
||||
|
||||
mod pos;
|
||||
pub mod query;
|
||||
pub mod schema;
|
||||
|
||||
mod error;
|
||||
mod pos;
|
||||
mod query_parser;
|
||||
mod schema_parser;
|
||||
mod utils;
|
||||
mod value;
|
||||
|
||||
pub use error::{Error, Result};
|
||||
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};
|
||||
|
|
|
@ -6,8 +6,11 @@ int = @{ "-"? ~ ("0" | (ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*)) }
|
|||
float = @{ "-"? ~ int ~ "." ~ ASCII_DIGIT+ ~ exp? }
|
||||
exp = @{ ("E" | "e") ~ ("+" | "-")? ~ ASCII_DIGIT+ }
|
||||
|
||||
string = @{ "\"" ~ string_inner ~ "\"" }
|
||||
string_inner = @{ (!("\"" | "\\") ~ ANY)* ~ (escape ~ string_inner)? }
|
||||
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} }
|
||||
|
||||
|
@ -29,7 +32,6 @@ nonnull_type = { (list_type | name) ~ "!" }
|
|||
type_ = { nonnull_type | list_type | name }
|
||||
|
||||
// query
|
||||
|
||||
arguments = { "(" ~ pair* ~ ")" }
|
||||
directive = { "@" ~ name ~ arguments? }
|
||||
directives = { directive* }
|
||||
|
|
|
@ -1,102 +1,19 @@
|
|||
use crate::pos::Positioned;
|
||||
use crate::query::*;
|
||||
use crate::utils::{to_static_str, unquote_string, PositionCalculator};
|
||||
use crate::value::Value;
|
||||
use crate::Pos;
|
||||
use arrayvec::ArrayVec;
|
||||
use pest::error::LineColLocation;
|
||||
use crate::Result;
|
||||
use pest::iterators::Pair;
|
||||
use pest::Parser;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
use std::iter::Peekable;
|
||||
use std::ops::Deref;
|
||||
use std::str::Chars;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[grammar = "query.pest"]
|
||||
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.
|
||||
pub fn parse_query<T: Into<String>>(input: T) -> Result<Document> {
|
||||
let source = input.into();
|
||||
|
@ -134,6 +51,12 @@ pub struct ParsedValue {
|
|||
value: Value,
|
||||
}
|
||||
|
||||
impl fmt::Debug for ParsedValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ParsedValue {
|
||||
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::int => Value::Int(pair.as_str().parse().unwrap()),
|
||||
Rule::string => Value::String({
|
||||
let start_pos = pair.as_span().start_pos().line_col();
|
||||
unquote_string(
|
||||
to_static_str(pair.as_str()),
|
||||
Pos {
|
||||
line: start_pos.0,
|
||||
column: start_pos.1,
|
||||
},
|
||||
)?
|
||||
let pos = pc.step(&pair);
|
||||
unquote_string(pair.as_str(), pos)?
|
||||
}),
|
||||
Rule::name => Value::Enum(to_static_str(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();
|
||||
for pair in pair.into_inner() {
|
||||
match pair.as_rule() {
|
||||
Rule::pair => {
|
||||
map.extend(std::iter::once(parse_object_pair(pair, pc)?));
|
||||
}
|
||||
Rule::pair => map.extend(std::iter::once(parse_object_pair(pair, pc)?)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
@ -418,9 +333,7 @@ fn parse_array_value(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Va
|
|||
let mut array = Vec::new();
|
||||
for pair in pair.into_inner() {
|
||||
match pair.as_rule() {
|
||||
Rule::value => {
|
||||
array.push(parse_value2(pair, pc)?);
|
||||
}
|
||||
Rule::value => array.push(parse_value2(pair, pc)?),
|
||||
_ => 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)]
|
||||
mod tests {
|
||||
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 | 👍 | ⛔️ |
|
||||
| Rustfmt friendly(No DSL) | 👍 | ⛔️ |
|
||||
| Boilerplate | less | some |
|
||||
| Boilerplate | Less | Some |
|
||||
| Type Safety | 👍 | 👍 |
|
||||
| Query | 👍 | 👍 |
|
||||
| Mutation | 👍 | 👍 |
|
||||
| Type Safety | 👍 | 👍 |
|
||||
| Interfaces | 👍 | 👍 |
|
||||
| Union | 👍 | 👍 |
|
||||
| Dataloading | 👍 | 👍 |
|
||||
|
|
Loading…
Reference in New Issue
Block a user