Schema parser has been implemented.

This commit is contained in:
sunli 2020-05-15 22:27:38 +08:00
parent 752f6adde8
commit 6c7fb0dff7
10 changed files with 871 additions and 40 deletions

View File

@ -30,3 +30,6 @@ impl<R: RuleType> From<pest::error::Error<R>> for Error {
}
}
}
/// Parser result
pub type Result<T> = std::result::Result<T, Error>;

View File

@ -9,10 +9,12 @@ pub mod schema;
mod error;
mod pos;
mod query_parser;
mod schema_parser;
mod utils;
mod value;
pub use error::Error;
pub use error::{Error, Result};
pub use pos::{Pos, Positioned};
pub use query_parser::{parse_query, parse_value, ParsedValue, Result};
pub use query_parser::{parse_query, parse_value, ParsedValue};
pub use schema_parser::parse_schema;
pub use value::{UploadValue, Value};

View File

@ -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} }

View File

@ -2,7 +2,7 @@ use crate::pos::Positioned;
use crate::query::*;
use crate::utils::{to_static_str, unquote_string, PositionCalculator};
use crate::value::Value;
use crate::{Error, Pos};
use crate::Result;
use pest::iterators::Pair;
use pest::Parser;
use std::borrow::Cow;
@ -14,9 +14,6 @@ use std::ops::Deref;
#[grammar = "query.pest"]
struct QueryParser;
/// Parser result
pub type Result<T> = std::result::Result<T, Error>;
/// Parse a GraphQL query.
pub fn parse_query<T: Into<String>>(input: T) -> Result<Document> {
let source = input.into();
@ -291,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() {
@ -331,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!(),
}
}
@ -344,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!(),
}
}

View File

@ -6,22 +6,24 @@ 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} }
boolean = { "true" | "false" }
null = { "null" }
name = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHA | ASCII_DIGIT | "_")* }
variable = { "$" ~ name }
array = { "[" ~ value* ~ "]" }
pair = { name ~ ":" ~ value }
object = { "{" ~ pair* ~ "}" }
value = { object | array | variable | float | int | string | null | boolean | name }
value = { object | array | float | int | string | null | boolean | name }
// type
list_type = { "[" ~ type_ ~ "]" }
@ -29,3 +31,42 @@ 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}

View File

@ -1,5 +1,18 @@
use crate::pos::Positioned;
use crate::ParsedValue;
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 {
@ -16,16 +29,13 @@ pub struct Document {
#[derive(Debug)]
pub enum Definition {
SchemaDefinition(Positioned<SchemaDefinition>),
TypeDefinition {
extend: bool,
description: Positioned<String>,
definition: Positioned<TypeDefinition>,
},
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>>,
@ -44,12 +54,16 @@ pub enum TypeDefinition {
#[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>>,
@ -70,12 +84,14 @@ pub struct InputValue {
pub description: Option<Positioned<String>>,
pub name: Positioned<String>,
pub ty: Positioned<Type>,
pub default_value: Option<ParsedValue>,
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>>,
@ -83,13 +99,17 @@ pub struct InterfaceType {
#[derive(Debug)]
pub struct UnionType {
pub extend: bool,
pub description: Option<Positioned<String>>,
pub name: Positioned<String>,
pub directives: Vec<Positioned<Directive>>,
pub types: Vec<Positioned<String>>,
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>>,
@ -104,6 +124,8 @@ pub struct EnumValue {
#[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>>,
@ -145,5 +167,5 @@ pub struct DirectiveDefinition {
#[derive(Debug)]
pub struct Directive {
pub name: Positioned<String>,
pub arguments: Vec<(Positioned<String>, Positioned<ParsedValue>)>,
pub arguments: Vec<(Positioned<String>, Positioned<Value>)>,
}

View 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();
}
}
}
}

View File

@ -60,9 +60,14 @@ pub fn to_static_str(s: &str) -> &'static str {
unsafe { (s as *const str).as_ref().unwrap() }
}
pub 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];
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)));

View File

@ -0,0 +1,7 @@
{
a(s: """
a
b
c
""")
}

View File

@ -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 | 👍 | 👍 |