use combine::easy::{Error, Errors};
use combine::error::StreamError;
use combine::sep_by1;
use combine::{choice, eof, many, many1, optional, position};
use combine::{parser, Parser, StdParseResult};
use crate::common::{default_value, directives, parse_type, string, Text};
use crate::helpers::{ident, kind, name, punct};
use crate::schema::ast::*;
use crate::schema::error::ParseError;
use crate::tokenizer::{Kind as T, Token, TokenStream};
pub fn schema<'a, S>(
input: &mut TokenStream<'a>,
) -> StdParseResult<SchemaDefinition<'a, S>, TokenStream<'a>>
where
S: Text<'a>,
{
(
position().skip(ident("schema")),
parser(directives),
punct("{")
.with(many((kind(T::Name).skip(punct(":")), name::<'a, S>())))
.skip(punct("}")),
)
.flat_map(
|(position, directives, operations): (_, _, Vec<(Token, _)>)| {
let mut query = None;
let mut mutation = None;
let mut subscription = None;
let mut err = Errors::empty(position);
for (oper, type_name) in operations {
match oper.value {
"query" if query.is_some() => {
err.add_error(Error::unexpected_static_message(
"duplicate `query` operation",
));
}
"query" => {
query = Some(type_name);
}
"mutation" if mutation.is_some() => {
err.add_error(Error::unexpected_static_message(
"duplicate `mutation` operation",
));
}
"mutation" => {
mutation = Some(type_name);
}
"subscription" if subscription.is_some() => {
err.add_error(Error::unexpected_static_message(
"duplicate `subscription` operation",
));
}
"subscription" => {
subscription = Some(type_name);
}
_ => {
err.add_error(Error::unexpected_token(oper));
err.add_error(Error::expected_static_message("query"));
err.add_error(Error::expected_static_message("mutation"));
err.add_error(Error::expected_static_message("subscription"));
}
}
}
if !err.errors.is_empty() {
return Err(err);
}
Ok(SchemaDefinition {
position,
directives,
query,
mutation,
subscription,
})
},
)
.parse_stream(input)
.into_result()
}
pub fn scalar_type<'a, T>(
input: &mut TokenStream<'a>,
) -> StdParseResult<ScalarType<'a, T>, TokenStream<'a>>
where
T: Text<'a>,
{
(
position(),
ident("scalar").with(name::<'a, T>()),
parser(directives),
)
.map(|(position, name, directives)| ScalarType {
position,
description: None,
name,
directives,
})
.parse_stream(input)
.into_result()
}
pub fn scalar_type_extension<'a, T>(
input: &mut TokenStream<'a>,
) -> StdParseResult<ScalarTypeExtension<'a, T>, TokenStream<'a>>
where
T: Text<'a>,
{
(
position(),
ident("scalar").with(name::<'a, T>()),
parser(directives),
)
.flat_map(|(position, name, directives)| {
if directives.is_empty() {
let mut e = Errors::empty(position);
e.add_error(Error::expected_static_message(
"Scalar type extension should contain at least \
one directive.",
));
return Err(e);
}
Ok(ScalarTypeExtension {
position,
name,
directives,
})
})
.parse_stream(input)
.into_result()
}
pub fn implements_interfaces<'a, X>(
input: &mut TokenStream<'a>,
) -> StdParseResult<Vec<X::Value>, TokenStream<'a>>
where
X: Text<'a>,
{
optional(
ident("implements")
.skip(optional(punct("&")))
.with(sep_by1(name::<'a, X>(), punct("&"))),
)
.map(|opt| opt.unwrap_or_default())
.parse_stream(input)
.into_result()
}
pub fn input_value<'a, X>(
input: &mut TokenStream<'a>,
) -> StdParseResult<InputValue<'a, X>, TokenStream<'a>>
where
X: Text<'a>,
{
(
position(),
optional(parser(string)),
name::<'a, X>(),
punct(":").with(parser(parse_type)),
optional(punct("=").with(parser(default_value))),
parser(directives),
)
.map(
|(position, description, name, value_type, default_value, directives)| InputValue {
position,
description,
name,
value_type,
default_value,
directives,
},
)
.parse_stream(input)
.into_result()
}
pub fn arguments_definition<'a, T>(
input: &mut TokenStream<'a>,
) -> StdParseResult<Vec<InputValue<'a, T>>, TokenStream<'a>>
where
T: Text<'a>,
{
optional(punct("(").with(many1(parser(input_value))).skip(punct(")")))
.map(|v| v.unwrap_or_default())
.parse_stream(input)
.into_result()
}
pub fn field<'a, S>(input: &mut TokenStream<'a>) -> StdParseResult<Field<'a, S>, TokenStream<'a>>
where
S: Text<'a>,
{
(
position(),
optional(parser(string)),
name::<'a, S>(),
parser(arguments_definition),
punct(":").with(parser(parse_type)),
parser(directives),
)
.map(
|(position, description, name, arguments, field_type, directives)| Field {
position,
description,
name,
arguments,
field_type,
directives,
},
)
.parse_stream(input)
.into_result()
}
pub fn fields<'a, S>(
input: &mut TokenStream<'a>,
) -> StdParseResult<Vec<Field<'a, S>>, TokenStream<'a>>
where
S: Text<'a>,
{
optional(punct("{").with(many1(parser(field))).skip(punct("}")))
.map(|v| v.unwrap_or_default())
.parse_stream(input)
.into_result()
}
pub fn object_type<'a, S>(
input: &mut TokenStream<'a>,
) -> StdParseResult<ObjectType<'a, S>, TokenStream<'a>>
where
S: Text<'a>,
{
(
position(),
ident("type").with(name::<'a, S>()),
parser(implements_interfaces::<S>),
parser(directives),
parser(fields),
)
.map(|(position, name, interfaces, directives, fields)| {
ObjectType {
position,
name,
directives,
fields,
implements_interfaces: interfaces,
description: None, }
})
.parse_stream(input)
.into_result()
}
pub fn object_type_extension<'a, S>(
input: &mut TokenStream<'a>,
) -> StdParseResult<ObjectTypeExtension<'a, S>, TokenStream<'a>>
where
S: Text<'a>,
{
(
position(),
ident("type").with(name::<'a, S>()),
parser(implements_interfaces::<S>),
parser(directives),
parser(fields),
)
.flat_map(|(position, name, interfaces, directives, fields)| {
if interfaces.is_empty() && directives.is_empty() && fields.is_empty() {
let mut e = Errors::empty(position);
e.add_error(Error::expected_static_message(
"Object type extension should contain at least \
one interface, directive or field.",
));
return Err(e);
}
Ok(ObjectTypeExtension {
position,
name,
directives,
fields,
implements_interfaces: interfaces,
})
})
.parse_stream(input)
.into_result()
}
pub fn interface_type<'a, T>(
input: &mut TokenStream<'a>,
) -> StdParseResult<InterfaceType<'a, T>, TokenStream<'a>>
where
T: Text<'a>,
{
(
position(),
ident("interface").with(name::<'a, T>()),
parser(implements_interfaces::<T>),
parser(directives),
parser(fields),
)
.map(|(position, name, interfaces, directives, fields)| {
InterfaceType {
position,
name,
implements_interfaces: interfaces,
directives,
fields,
description: None, }
})
.parse_stream(input)
.into_result()
}
pub fn interface_type_extension<'a, T>(
input: &mut TokenStream<'a>,
) -> StdParseResult<InterfaceTypeExtension<'a, T>, TokenStream<'a>>
where
T: Text<'a>,
{
(
position(),
ident("interface").with(name::<'a, T>()),
parser(implements_interfaces::<T>),
parser(directives),
parser(fields),
)
.flat_map(|(position, name, interfaces, directives, fields)| {
if directives.is_empty() && fields.is_empty() {
let mut e = Errors::empty(position);
e.add_error(Error::expected_static_message(
"Interface type extension should contain at least \
one directive or field.",
));
return Err(e);
}
Ok(InterfaceTypeExtension {
position,
name,
implements_interfaces: interfaces,
directives,
fields,
})
})
.parse_stream(input)
.into_result()
}
pub fn union_members<'a, T>(
input: &mut TokenStream<'a>,
) -> StdParseResult<Vec<T::Value>, TokenStream<'a>>
where
T: Text<'a>,
{
optional(punct("|"))
.with(sep_by1(name::<'a, T>(), punct("|")))
.parse_stream(input)
.into_result()
}
pub fn union_type<'a, T>(
input: &mut TokenStream<'a>,
) -> StdParseResult<UnionType<'a, T>, TokenStream<'a>>
where
T: Text<'a>,
{
(
position(),
ident("union").with(name::<'a, T>()),
parser(directives),
optional(punct("=").with(parser(union_members::<T>))),
)
.map(|(position, name, directives, types)| {
UnionType {
position,
name,
directives,
types: types.unwrap_or_else(Vec::new),
description: None, }
})
.parse_stream(input)
.into_result()
}
pub fn union_type_extension<'a, T>(
input: &mut TokenStream<'a>,
) -> StdParseResult<UnionTypeExtension<'a, T>, TokenStream<'a>>
where
T: Text<'a>,
{
(
position(),
ident("union").with(name::<'a, T>()),
parser(directives),
optional(punct("=").with(parser(union_members::<T>))),
)
.flat_map(|(position, name, directives, types)| {
if directives.is_empty() && types.is_none() {
let mut e = Errors::empty(position);
e.add_error(Error::expected_static_message(
"Union type extension should contain at least \
one directive or type.",
));
return Err(e);
}
Ok(UnionTypeExtension {
position,
name,
directives,
types: types.unwrap_or_else(Vec::new),
})
})
.parse_stream(input)
.into_result()
}
pub fn enum_values<'a, T>(
input: &mut TokenStream<'a>,
) -> StdParseResult<Vec<EnumValue<'a, T>>, TokenStream<'a>>
where
T: Text<'a>,
{
punct("{")
.with(many1(
(
position(),
optional(parser(string)),
name::<'a, T>(),
parser(directives),
)
.map(|(position, description, name, directives)| EnumValue {
position,
description,
name,
directives,
}),
))
.skip(punct("}"))
.parse_stream(input)
.into_result()
}
pub fn enum_type<'a, T>(
input: &mut TokenStream<'a>,
) -> StdParseResult<EnumType<'a, T>, TokenStream<'a>>
where
T: Text<'a>,
{
(
position(),
ident("enum").with(name::<'a, T>()),
parser(directives),
optional(parser(enum_values)),
)
.map(|(position, name, directives, values)| {
EnumType {
position,
name,
directives,
values: values.unwrap_or_else(Vec::new),
description: None, }
})
.parse_stream(input)
.into_result()
}
pub fn enum_type_extension<'a, T>(
input: &mut TokenStream<'a>,
) -> StdParseResult<EnumTypeExtension<'a, T>, TokenStream<'a>>
where
T: Text<'a>,
{
(
position(),
ident("enum").with(name::<'a, T>()),
parser(directives),
optional(parser(enum_values)),
)
.flat_map(|(position, name, directives, values)| {
if directives.is_empty() && values.is_none() {
let mut e = Errors::empty(position);
e.add_error(Error::expected_static_message(
"Enum type extension should contain at least \
one directive or value.",
));
return Err(e);
}
Ok(EnumTypeExtension {
position,
name,
directives,
values: values.unwrap_or_else(Vec::new),
})
})
.parse_stream(input)
.into_result()
}
pub fn input_fields<'a, T>(
input: &mut TokenStream<'a>,
) -> StdParseResult<Vec<InputValue<'a, T>>, TokenStream<'a>>
where
T: Text<'a>,
{
optional(punct("{").with(many1(parser(input_value))).skip(punct("}")))
.map(|v| v.unwrap_or_default())
.parse_stream(input)
.into_result()
}
pub fn input_object_type<'a, T>(
input: &mut TokenStream<'a>,
) -> StdParseResult<InputObjectType<'a, T>, TokenStream<'a>>
where
T: Text<'a>,
{
(
position(),
ident("input").with(name::<'a, T>()),
parser(directives),
parser(input_fields),
)
.map(|(position, name, directives, fields)| {
InputObjectType {
position,
name,
directives,
fields,
description: None, }
})
.parse_stream(input)
.into_result()
}
pub fn input_object_type_extension<'a, T>(
input: &mut TokenStream<'a>,
) -> StdParseResult<InputObjectTypeExtension<'a, T>, TokenStream<'a>>
where
T: Text<'a>,
{
(
position(),
ident("input").with(name::<'a, T>()),
parser(directives),
parser(input_fields),
)
.flat_map(|(position, name, directives, fields)| {
if directives.is_empty() && fields.is_empty() {
let mut e = Errors::empty(position);
e.add_error(Error::expected_static_message(
"Input object type extension should contain at least \
one directive or field.",
));
return Err(e);
}
Ok(InputObjectTypeExtension {
position,
name,
directives,
fields,
})
})
.parse_stream(input)
.into_result()
}
pub fn directive_locations<'a>(
input: &mut TokenStream<'a>,
) -> StdParseResult<Vec<DirectiveLocation>, TokenStream<'a>> {
optional(optional(punct("|")).with(sep_by1(
kind(T::Name).and_then(|tok| tok.value.parse::<DirectiveLocation>()),
punct("|"),
)))
.map(|opt| opt.unwrap_or_default())
.parse_stream(input)
.into_result()
}
pub fn directive_definition<'a, T>(
input: &mut TokenStream<'a>,
) -> StdParseResult<DirectiveDefinition<'a, T>, TokenStream<'a>>
where
T: Text<'a>,
{
(
position(),
ident("directive").and(punct("@")).with(name::<'a, T>()),
parser(arguments_definition),
optional(ident("repeatable")),
ident("on").with(parser(directive_locations)),
)
.map(|(position, name, arguments, repeatable, locations)| {
DirectiveDefinition {
position,
name,
arguments,
locations,
repeatable: repeatable.is_some(),
description: None, }
})
.parse_stream(input)
.into_result()
}
pub fn described_definition<'a, T>(
input: &mut TokenStream<'a>,
) -> StdParseResult<Definition<'a, T>, TokenStream<'a>>
where
T: Text<'a>,
{
use self::TypeDefinition::*;
(
optional(parser(string)),
choice((
choice((
parser(scalar_type).map(Scalar),
parser(object_type).map(Object),
parser(interface_type).map(Interface),
parser(union_type).map(Union),
parser(enum_type).map(Enum),
parser(input_object_type).map(InputObject),
))
.map(Definition::TypeDefinition),
parser(directive_definition).map(Definition::DirectiveDefinition),
)),
)
.map(|(descr, mut def)| {
use crate::schema::ast::Definition::TypeDefinition as T;
use crate::schema::ast::Definition::*;
use crate::schema::ast::TypeDefinition::*;
match def {
T(Scalar(ref mut s)) => s.description = descr,
T(Object(ref mut o)) => o.description = descr,
T(Interface(ref mut i)) => i.description = descr,
T(Union(ref mut u)) => u.description = descr,
T(Enum(ref mut e)) => e.description = descr,
T(InputObject(ref mut o)) => o.description = descr,
DirectiveDefinition(ref mut d) => d.description = descr,
SchemaDefinition(_) => unreachable!(),
TypeExtension(_) => unreachable!(),
}
def
})
.parse_stream(input)
.into_result()
}
pub fn type_extension<'a, T>(
input: &mut TokenStream<'a>,
) -> StdParseResult<TypeExtension<'a, T>, TokenStream<'a>>
where
T: Text<'a>,
{
ident("extend")
.with(choice((
parser(scalar_type_extension).map(TypeExtension::Scalar),
parser(object_type_extension).map(TypeExtension::Object),
parser(interface_type_extension).map(TypeExtension::Interface),
parser(union_type_extension).map(TypeExtension::Union),
parser(enum_type_extension).map(TypeExtension::Enum),
parser(input_object_type_extension).map(TypeExtension::InputObject),
)))
.parse_stream(input)
.into_result()
}
pub fn definition<'a, T>(
input: &mut TokenStream<'a>,
) -> StdParseResult<Definition<'a, T>, TokenStream<'a>>
where
T: Text<'a>,
{
choice((
parser(schema).map(Definition::SchemaDefinition),
parser(type_extension).map(Definition::TypeExtension),
parser(described_definition),
))
.parse_stream(input)
.into_result()
}
pub fn parse_schema<'a, T>(s: &'a str) -> Result<Document<'a, T>, ParseError>
where
T: Text<'a>,
{
let mut tokens = TokenStream::new(s);
let (doc, _) = many1(parser(definition))
.map(|d| Document { definitions: d })
.skip(eof())
.parse_stream(&mut tokens)
.into_result()
.map_err(|e| e.into_inner().error)?;
Ok(doc)
}
#[cfg(test)]
mod test {
use super::parse_schema;
use crate::position::Pos;
use crate::schema::grammar::*;
fn ast(s: &str) -> Document<String> {
parse_schema::<String>(s).unwrap().to_owned()
}
#[test]
fn one_field() {
assert_eq!(
ast("schema { query: Query }"),
Document {
definitions: vec![Definition::SchemaDefinition(SchemaDefinition {
position: Pos { line: 1, column: 1 },
directives: vec![],
query: Some("Query".into()),
mutation: None,
subscription: None
})],
}
);
}
}