Add support for GraphQL Schema Language (#676)

Co-authored-by: Alexander Lyon <arlyon@me.com>
This commit is contained in:
Christian Legnitto 2020-06-05 17:43:11 -10:00 committed by GitHub
parent 40ad17c540
commit 9167654a73
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 592 additions and 5 deletions

View file

@ -48,14 +48,15 @@ see the [actix][actix_examples], [hyper][hyper_examples], [rocket][rocket_exampl
Juniper supports the full GraphQL query language according to the Juniper supports the full GraphQL query language according to the
[specification][graphql_spec], including interfaces, unions, schema [specification][graphql_spec], including interfaces, unions, schema
introspection, and validations. introspection, and validations. It can also output the schema in the [GraphQL Schema Language][schema_language].
It does not, however, support the schema language. Consider using [juniper-from-schema][] for generating code from a schema file.
As an exception to other GraphQL libraries for other languages, Juniper builds As an exception to other GraphQL libraries for other languages, Juniper builds
non-null types by default. A field of type `Vec<Episode>` will be converted into non-null types by default. A field of type `Vec<Episode>` will be converted into
`[Episode!]!`. The corresponding Rust type for e.g. `[Episode]` would be `[Episode!]!`. The corresponding Rust type for e.g. `[Episode]` would be
`Option<Vec<Option<Episode>>>`. `Option<Vec<Option<Episode>>>`.
Juniper follows a [code-first approach][schema_approach] to defining GraphQL schemas. If you would like to use a [schema-first approach][schema_approach] instead, consider [juniper-from-schema][] for generating code from a schema file.
## Integrations ## Integrations
### Data types ### Data types
@ -91,6 +92,8 @@ Juniper has not reached 1.0 yet, thus some API instability should be expected.
[playground]: https://github.com/prisma/graphql-playground [playground]: https://github.com/prisma/graphql-playground
[iron]: http://ironframework.io [iron]: http://ironframework.io
[graphql_spec]: http://facebook.github.io/graphql [graphql_spec]: http://facebook.github.io/graphql
[schema_language]: https://graphql.org/learn/schema/#type-language
[schema_approach]: https://blog.logrocket.com/code-first-vs-schema-first-development-graphql/
[test_schema_rs]: https://github.com/graphql-rust/juniper/blob/master/juniper/src/tests/schema.rs [test_schema_rs]: https://github.com/graphql-rust/juniper/blob/master/juniper/src/tests/schema.rs
[tokio]: https://github.com/tokio-rs/tokio [tokio]: https://github.com/tokio-rs/tokio
[actix_examples]: https://github.com/graphql-rust/juniper/tree/master/juniper_actix/examples [actix_examples]: https://github.com/graphql-rust/juniper/tree/master/juniper_actix/examples

View file

@ -2,6 +2,8 @@
This page will give you a short introduction to the concepts in Juniper. This page will give you a short introduction to the concepts in Juniper.
Juniper follows a [code-first approach][schema_approach] to defining GraphQL schemas. If you would like to use a [schema-first approach][schema_approach] instead, consider [juniper-from-schema][] for generating code from a schema file.
## Installation ## Installation
!FILENAME Cargo.toml !FILENAME Cargo.toml
@ -193,6 +195,8 @@ fn main() {
} }
``` ```
[juniper-from-schema]: https://github.com/davidpdrsn/juniper-from-schema
[schema_approach]: https://blog.logrocket.com/code-first-vs-schema-first-development-graphql/
[hyper]: servers/hyper.md [hyper]: servers/hyper.md
[warp]: servers/warp.md [warp]: servers/warp.md
[rocket]: servers/rocket.md [rocket]: servers/rocket.md

View file

@ -1,5 +1,7 @@
# Schemas # Schemas
Juniper follows a [code-first approach][schema_approach] to defining GraphQL schemas. If you would like to use a [schema-first approach][schema_approach] instead, consider [juniper-from-schema][] for generating code from a schema file.
A schema consists of three types: a query object, a mutation object, and a subscription object. A schema consists of three types: a query object, a mutation object, and a subscription object.
These three define the root query fields, mutations and subscriptions of the schema, respectively. These three define the root query fields, mutations and subscriptions of the schema, respectively.
@ -60,6 +62,55 @@ impl Mutations {
# fn main() { } # fn main() { }
``` ```
# Outputting schemas in the [GraphQL Schema Language][schema_language]
Many tools in the GraphQL ecosystem require the schema to be defined in the [GraphQL Schema Language][schema_language]. You can generate a [GraphQL Schema Language][schema_language] representation of your schema defined in Rust using the `schema-language` feature (on by default):
```rust
# // Only needed due to 2018 edition because the macro is not accessible.
# #[macro_use] extern crate juniper;
use juniper::{FieldResult, EmptyMutation, EmptySubscription, RootNode};
struct Query;
#[juniper::graphql_object]
impl Query {
fn hello(&self) -> FieldResult<&str> {
Ok("hello world")
}
}
fn main() {
// Define our schema in Rust.
let schema = RootNode::new(
Query,
EmptyMutation::<()>::new(),
EmptySubscription::<()>::new(),
);
// Convert the Rust schema into the GraphQL Schema Language.
let result = schema.as_schema_language();
let expected = "\
type Query {
hello: String!
}
schema {
query: Query
}
";
assert_eq!(result, expected);
}
```
Note the `schema-language` feature may be turned off if you do not need this functionality to reduce dependencies and speed up
compile times.
[schema_language]: https://graphql.org/learn/schema/#type-language
[juniper-from-schema]: https://github.com/davidpdrsn/juniper-from-schema
[schema_approach]: https://blog.logrocket.com/code-first-vs-schema-first-development-graphql/
[section]: ../advanced/subscriptions.md [section]: ../advanced/subscriptions.md
[EmptyMutation]: https://docs.rs/juniper/0.14.2/juniper/struct.EmptyMutation.html [EmptyMutation]: https://docs.rs/juniper/0.14.2/juniper/struct.EmptyMutation.html
<!--TODO: Fix This URL when the EmptySubscription become available in the Documentation --> <!--TODO: Fix This URL when the EmptySubscription become available in the Documentation -->

View file

@ -2,6 +2,9 @@
## Features ## Features
- Added support for outputting the Rust schema in the [GraphQL Schema Language](https://graphql.org/learn/schema/#type-language). ([#676](https://github.com/graphql-rust/juniper/pull/676))
- This is controlled by the `schema-language` feature and is on by default. It may be turned off if you do not need this functionality to reduce dependencies and speed up compile times.
- Normalization for the subscriptions_endpoint_url in the `graphiql_source`. - Normalization for the subscriptions_endpoint_url in the `graphiql_source`.
(See [#628](https://github.com/graphql-rust/juniper/pull/628) for more details) (See [#628](https://github.com/graphql-rust/juniper/pull/628) for more details)

View file

@ -25,11 +25,14 @@ path = "benches/bench.rs"
[features] [features]
expose-test-schema = ["serde_json"] expose-test-schema = ["serde_json"]
schema-language = ["graphql-parser-integration"]
graphql-parser-integration = ["graphql-parser"]
default = [ default = [
"bson", "bson",
"chrono", "chrono",
"url", "url",
"uuid", "uuid",
"schema-language",
] ]
scalar-naivetime = [] scalar-naivetime = []
@ -46,6 +49,7 @@ serde_json = { version="1.0.2", optional = true }
static_assertions = "1.1" static_assertions = "1.1"
url = { version = "2", optional = true } url = { version = "2", optional = true }
uuid = { version = "0.8", optional = true } uuid = { version = "0.8", optional = true }
graphql-parser = {version = "0.3.0", optional = true }
[dev-dependencies] [dev-dependencies]
bencher = "0.1.2" bencher = "0.1.2"

View file

@ -169,6 +169,14 @@ pub struct Field<'a, S> {
pub deprecation_status: DeprecationStatus, pub deprecation_status: DeprecationStatus,
} }
impl<'a, S> Field<'a, S> {
/// Returns true if the type is built-in to GraphQL.
pub fn is_builtin(&self) -> bool {
// "used exclusively by GraphQLs introspection system"
self.name.starts_with("__")
}
}
/// Metadata for an argument to a field /// Metadata for an argument to a field
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Argument<'a, S> { pub struct Argument<'a, S> {
@ -182,6 +190,14 @@ pub struct Argument<'a, S> {
pub default_value: Option<InputValue<S>>, pub default_value: Option<InputValue<S>>,
} }
impl<'a, S> Argument<'a, S> {
/// Returns true if the type is built-in to GraphQL.
pub fn is_builtin(&self) -> bool {
// "used exclusively by GraphQLs introspection system"
self.name.starts_with("__")
}
}
/// Metadata for a single value in an enum /// Metadata for a single value in an enum
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct EnumValue { pub struct EnumValue {
@ -368,6 +384,22 @@ impl<'a, S> MetaType<'a, S> {
} }
} }
/// Returns true if the type is built-in to GraphQL.
pub fn is_builtin(&self) -> bool {
if let Some(name) = self.name() {
// "used exclusively by GraphQLs introspection system"
{
name.starts_with("__") ||
// <https://facebook.github.io/graphql/draft/#sec-Scalars>
name == "Boolean" || name == "String" || name == "Int" || name == "Float" || name == "ID" ||
// Our custom empty markers
name == "_EmptyMutation" || name == "_EmptySubscription"
}
} else {
false
}
}
pub(crate) fn fields<'b>(&self, schema: &'b SchemaType<S>) -> Option<Vec<&'b Field<'b, S>>> { pub(crate) fn fields<'b>(&self, schema: &'b SchemaType<S>) -> Option<Vec<&'b Field<'b, S>>> {
schema schema
.lookup_type(&self.as_type()) .lookup_type(&self.as_type())

View file

@ -3,3 +3,4 @@
pub mod meta; pub mod meta;
pub mod model; pub mod model;
pub mod schema; pub mod schema;
pub mod translate;

View file

@ -1,6 +1,8 @@
use std::fmt; use std::fmt;
use fnv::FnvHashMap; use fnv::FnvHashMap;
#[cfg(feature = "graphql-parser-integration")]
use graphql_parser::schema::Document;
use juniper_codegen::GraphQLEnumInternal as GraphQLEnum; use juniper_codegen::GraphQLEnumInternal as GraphQLEnum;
@ -12,6 +14,9 @@ use crate::{
value::{DefaultScalarValue, ScalarValue}, value::{DefaultScalarValue, ScalarValue},
}; };
#[cfg(feature = "graphql-parser-integration")]
use crate::schema::translate::{graphql_parser::GraphQLParserTranslator, SchemaTranslator};
/// Root query node of a schema /// Root query node of a schema
/// ///
/// This brings the mutation, subscription and query types together, /// This brings the mutation, subscription and query types together,
@ -46,9 +51,9 @@ pub struct RootNode<
#[derive(Debug)] #[derive(Debug)]
pub struct SchemaType<'a, S> { pub struct SchemaType<'a, S> {
pub(crate) types: FnvHashMap<Name, MetaType<'a, S>>, pub(crate) types: FnvHashMap<Name, MetaType<'a, S>>,
query_type_name: String, pub(crate) query_type_name: String,
mutation_type_name: Option<String>, pub(crate) mutation_type_name: Option<String>,
subscription_type_name: Option<String>, pub(crate) subscription_type_name: Option<String>,
directives: FnvHashMap<String, DirectiveType<'a, S>>, directives: FnvHashMap<String, DirectiveType<'a, S>>,
} }
@ -102,6 +107,22 @@ where
) -> Self { ) -> Self {
RootNode::new_with_info(query_obj, mutation_obj, subscription_obj, (), (), ()) RootNode::new_with_info(query_obj, mutation_obj, subscription_obj, (), (), ())
} }
#[cfg(feature = "schema-language")]
/// The schema definition as a `String` in the
/// [GraphQL Schema Language](https://graphql.org/learn/schema/#type-language)
/// format.
pub fn as_schema_language(&self) -> String {
let doc = self.as_parser_document();
format!("{}", doc)
}
#[cfg(feature = "graphql-parser-integration")]
/// The schema definition as a [`graphql_parser`](https://crates.io/crates/graphql-parser)
/// [`Document`](https://docs.rs/graphql-parser/latest/graphql_parser/schema/struct.Document.html).
pub fn as_parser_document(&'a self) -> Document<'a, &'a str> {
GraphQLParserTranslator::translate_schema(&self.schema)
}
} }
impl<'a, S, QueryT, MutationT, SubscriptionT> RootNode<'a, QueryT, MutationT, SubscriptionT, S> impl<'a, S, QueryT, MutationT, SubscriptionT> RootNode<'a, QueryT, MutationT, SubscriptionT, S>
@ -534,3 +555,157 @@ impl<'a, S> fmt::Display for TypeType<'a, S> {
} }
} }
} }
#[cfg(test)]
mod test {
#[cfg(feature = "graphql-parser-integration")]
mod graphql_parser_integration {
use crate as juniper;
use crate::{EmptyMutation, EmptySubscription};
#[test]
fn graphql_parser_doc() {
struct Query;
#[juniper::graphql_object]
impl Query {
fn blah() -> bool {
true
}
};
let schema = crate::RootNode::new(
Query,
EmptyMutation::<()>::new(),
EmptySubscription::<()>::new(),
);
let ast = graphql_parser::parse_schema::<&str>(
r#"
type Query {
blah: Boolean!
}
schema {
query: Query
}
"#,
)
.unwrap();
assert_eq!(
format!("{}", ast),
format!("{}", schema.as_parser_document()),
);
}
}
#[cfg(feature = "schema-language")]
mod schema_language {
use crate as juniper;
use crate::{
EmptyMutation, EmptySubscription, GraphQLEnum, GraphQLInputObject, GraphQLObject,
GraphQLUnionInternal as GraphQLUnion,
};
#[test]
fn schema_language() {
#[derive(GraphQLObject, Default)]
struct Cake {
fresh: bool,
};
#[derive(GraphQLObject, Default)]
struct IceCream {
cold: bool,
};
#[derive(GraphQLUnion)]
enum GlutenFree {
Cake(Cake),
IceCream(IceCream),
}
#[derive(GraphQLEnum)]
enum Fruit {
Apple,
Orange,
}
#[derive(GraphQLInputObject)]
struct Coordinate {
latitude: f64,
longitude: f64,
}
struct Query;
#[juniper::graphql_object]
impl Query {
fn blah() -> bool {
true
}
/// This is whatever's description.
fn whatever() -> String {
"foo".to_string()
}
fn arr(stuff: Vec<Coordinate>) -> Option<&str> {
if stuff.is_empty() {
None
} else {
Some("stuff")
}
}
fn fruit() -> Fruit {
Fruit::Apple
}
fn gluten_free(flavor: String) -> GlutenFree {
if flavor == "savory" {
GlutenFree::Cake(Cake::default())
} else {
GlutenFree::IceCream(IceCream::default())
}
}
#[deprecated]
fn old() -> i32 {
42
}
#[deprecated(note = "This field is deprecated, use another.")]
fn really_old() -> f64 {
42.0
}
};
let schema = crate::RootNode::new(
Query,
EmptyMutation::<()>::new(),
EmptySubscription::<()>::new(),
);
let ast = graphql_parser::parse_schema::<&str>(
r#"
union GlutenFree = Cake | IceCream
enum Fruit {
APPLE
ORANGE
}
type Cake {
fresh: Boolean!
}
type IceCream {
cold: Boolean!
}
type Query {
blah: Boolean!
"This is whatever's description."
whatever: String!
arr(stuff: [Coordinate!]!): String
fruit: Fruit!
glutenFree(flavor: String!): GlutenFree!
old: Int! @deprecated
reallyOld: Float! @deprecated(reason: "This field is deprecated, use another.")
}
input Coordinate {
latitude: Float!
longitude: Float!
}
schema {
query: Query
}
"#,
)
.unwrap();
assert_eq!(format!("{}", ast), schema.as_schema_language());
}
}
}

View file

@ -0,0 +1,306 @@
use std::boxed::Box;
use std::collections::BTreeMap;
use graphql_parser::query::{
Directive as ExternalDirective, Number as ExternalNumber, Type as ExternalType,
};
use graphql_parser::schema::{Definition, Document, SchemaDefinition, Text};
use graphql_parser::schema::{
EnumType as ExternalEnum, EnumValue as ExternalEnumValue, Field as ExternalField,
InputObjectType as ExternalInputObjectType, InputValue as ExternalInputValue,
InterfaceType as ExternalInterfaceType, ObjectType as ExternalObjectType,
ScalarType as ExternalScalarType, TypeDefinition as ExternalTypeDefinition,
UnionType as ExternalUnionType, Value as ExternalValue,
};
use graphql_parser::Pos;
use crate::ast::{InputValue, Type};
use crate::schema::meta::DeprecationStatus;
use crate::schema::meta::{Argument, EnumValue, Field, MetaType};
use crate::schema::model::SchemaType;
use crate::schema::translate::SchemaTranslator;
use crate::value::ScalarValue;
pub struct GraphQLParserTranslator;
impl<'a, S: 'a, T> From<&'a SchemaType<'a, S>> for Document<'a, T>
where
S: ScalarValue,
T: Text<'a> + Default,
{
fn from(input: &'a SchemaType<'a, S>) -> Document<'a, T> {
GraphQLParserTranslator::translate_schema(input)
}
}
impl<'a, T> SchemaTranslator<'a, graphql_parser::schema::Document<'a, T>>
for GraphQLParserTranslator
where
T: Text<'a> + Default,
{
fn translate_schema<S: 'a>(input: &'a SchemaType<S>) -> graphql_parser::schema::Document<'a, T>
where
S: ScalarValue,
{
let mut doc = Document::default();
// Translate type defs.
let mut types = input
.types
.iter()
.filter(|(_, meta)| !meta.is_builtin())
.map(|(_, meta)| GraphQLParserTranslator::translate_meta(meta))
.map(Definition::TypeDefinition)
.collect();
doc.definitions.append(&mut types);
doc.definitions
.push(Definition::SchemaDefinition(SchemaDefinition {
position: Pos::default(),
directives: vec![],
query: Some(From::from(input.query_type_name.as_str())),
mutation: input
.mutation_type_name
.as_ref()
.map(|s| From::from(s.as_str())),
subscription: input
.subscription_type_name
.as_ref()
.map(|s| From::from(s.as_str())),
}));
doc
}
}
impl GraphQLParserTranslator {
fn translate_argument<'a, S, T>(input: &'a Argument<S>) -> ExternalInputValue<'a, T>
where
S: ScalarValue,
T: Text<'a>,
{
ExternalInputValue {
position: Pos::default(),
description: input.description.as_ref().map(From::from),
name: From::from(input.name.as_str()),
value_type: GraphQLParserTranslator::translate_type(&input.arg_type),
default_value: input
.default_value
.as_ref()
.map(|x| GraphQLParserTranslator::translate_value(x)),
directives: vec![],
}
}
fn translate_value<'a, S: 'a, T>(input: &'a InputValue<S>) -> ExternalValue<'a, T>
where
S: ScalarValue,
T: Text<'a>,
{
match input {
InputValue::Null => ExternalValue::Null,
InputValue::Scalar(x) => {
if let Some(v) = x.as_string() {
ExternalValue::String(v)
} else if let Some(v) = x.as_int() {
ExternalValue::Int(ExternalNumber::from(v))
} else if let Some(v) = x.as_float() {
ExternalValue::Float(v)
} else if let Some(v) = x.as_boolean() {
ExternalValue::Boolean(v)
} else {
panic!("unknown argument type")
}
}
InputValue::Enum(x) => ExternalValue::Enum(From::from(x.as_str())),
InputValue::Variable(x) => ExternalValue::Variable(From::from(x.as_str())),
InputValue::List(x) => ExternalValue::List(
x.iter()
.map(|s| GraphQLParserTranslator::translate_value(&s.item))
.collect(),
),
InputValue::Object(x) => {
let mut fields = BTreeMap::new();
x.iter().for_each(|(name_span, value_span)| {
fields.insert(
From::from(name_span.item.as_str()),
GraphQLParserTranslator::translate_value(&value_span.item),
);
});
ExternalValue::Object(fields)
}
}
}
fn translate_type<'a, T>(input: &'a Type<'a>) -> ExternalType<'a, T>
where
T: Text<'a>,
{
match input {
Type::Named(x) => ExternalType::NamedType(From::from(x.as_ref())),
Type::List(x) => {
ExternalType::ListType(GraphQLParserTranslator::translate_type(x).into())
}
Type::NonNullNamed(x) => {
ExternalType::NonNullType(Box::new(ExternalType::NamedType(From::from(x.as_ref()))))
}
Type::NonNullList(x) => ExternalType::NonNullType(Box::new(ExternalType::ListType(
Box::new(GraphQLParserTranslator::translate_type(x)),
))),
}
}
fn translate_meta<'a, S, T>(input: &'a MetaType<S>) -> ExternalTypeDefinition<'a, T>
where
S: ScalarValue,
T: Text<'a>,
{
match input {
MetaType::Scalar(x) => ExternalTypeDefinition::Scalar(ExternalScalarType {
position: Pos::default(),
description: x.description.as_ref().map(From::from),
name: From::from(x.name.as_ref()),
directives: vec![],
}),
MetaType::Enum(x) => ExternalTypeDefinition::Enum(ExternalEnum {
position: Pos::default(),
description: x.description.as_ref().map(|s| From::from(s.as_str())),
name: From::from(x.name.as_ref()),
directives: vec![],
values: x
.values
.iter()
.map(GraphQLParserTranslator::translate_enum_value)
.collect(),
}),
MetaType::Union(x) => ExternalTypeDefinition::Union(ExternalUnionType {
position: Pos::default(),
description: x.description.as_ref().map(|s| From::from(s.as_str())),
name: From::from(x.name.as_ref()),
directives: vec![],
types: x
.of_type_names
.iter()
.map(|s| From::from(s.as_str()))
.collect(),
}),
MetaType::Interface(x) => ExternalTypeDefinition::Interface(ExternalInterfaceType {
position: Pos::default(),
description: x.description.as_ref().map(|s| From::from(s.as_str())),
name: From::from(x.name.as_ref()),
directives: vec![],
fields: x
.fields
.iter()
.filter(|x| !x.is_builtin())
.map(GraphQLParserTranslator::translate_field)
.collect(),
}),
MetaType::InputObject(x) => {
ExternalTypeDefinition::InputObject(ExternalInputObjectType {
position: Pos::default(),
description: x.description.as_ref().map(|s| From::from(s.as_str())),
name: From::from(x.name.as_ref()),
directives: vec![],
fields: x
.input_fields
.iter()
.filter(|x| !x.is_builtin())
.map(GraphQLParserTranslator::translate_argument)
.collect(),
})
}
MetaType::Object(x) => ExternalTypeDefinition::Object(ExternalObjectType {
position: Pos::default(),
description: x.description.as_ref().map(|s| From::from(s.as_str())),
name: From::from(x.name.as_ref()),
directives: vec![],
fields: x
.fields
.iter()
.filter(|x| !x.is_builtin())
.map(GraphQLParserTranslator::translate_field)
.collect(),
implements_interfaces: x
.interface_names
.iter()
.map(|s| From::from(s.as_str()))
.collect(),
}),
_ => panic!("unknown meta type when translating"),
}
}
fn translate_enum_value<'a, T>(input: &'a EnumValue) -> ExternalEnumValue<'a, T>
where
T: Text<'a>,
{
ExternalEnumValue {
position: Pos::default(),
name: From::from(input.name.as_ref()),
description: input.description.as_ref().map(|s| From::from(s.as_str())),
directives: generate_directives(&input.deprecation_status),
}
}
fn translate_field<'a, S: 'a, T>(input: &'a Field<S>) -> ExternalField<'a, T>
where
S: ScalarValue,
T: Text<'a>,
{
let arguments = input
.arguments
.as_ref()
.map(|a| {
a.iter()
.filter(|x| !x.is_builtin())
.map(|x| GraphQLParserTranslator::translate_argument(&x))
.collect()
})
.unwrap_or_else(|| Vec::new());
ExternalField {
position: Pos::default(),
name: From::from(input.name.as_str()),
description: input.description.as_ref().map(|s| From::from(s.as_str())),
directives: generate_directives(&input.deprecation_status),
field_type: GraphQLParserTranslator::translate_type(&input.field_type),
arguments,
}
}
}
fn deprecation_to_directive<'a, T>(status: &DeprecationStatus) -> Option<ExternalDirective<'a, T>>
where
T: Text<'a>,
{
match status {
DeprecationStatus::Current => None,
DeprecationStatus::Deprecated(reason) => Some(ExternalDirective {
position: Pos::default(),
name: From::from("deprecated"),
arguments: if let Some(reason) = reason {
vec![(
From::from("reason"),
ExternalValue::String(reason.to_string()),
)]
} else {
vec![]
},
}),
}
}
// Right now the only directive supported is `@deprecated`. `@skip` and `@include`
// are dealt with elsewhere.
// <https://facebook.github.io/graphql/draft/#sec-Type-System.Directives>
fn generate_directives<'a, T>(status: &DeprecationStatus) -> Vec<ExternalDirective<'a, T>>
where
T: Text<'a>,
{
if let Some(d) = deprecation_to_directive(&status) {
vec![d]
} else {
vec![]
}
}

View file

@ -0,0 +1,8 @@
use crate::{ScalarValue, SchemaType};
pub trait SchemaTranslator<'a, T> {
fn translate_schema<S: 'a + ScalarValue>(s: &'a SchemaType<S>) -> T;
}
#[cfg(feature = "graphql-parser-integration")]
pub mod graphql_parser;