add schema support
This commit is contained in:
parent
8f448c91e9
commit
d0c6db0aba
|
@ -1,3 +1,5 @@
|
|||
# <div style="color: red">WARNING: Some features are not yet implemented. Please do not use in a production environment.</div>
|
||||
|
||||
# The GraphQL server library implemented by rust
|
||||
|
||||
<div align="center">
|
||||
|
@ -49,14 +51,15 @@
|
|||
- [X] Complex Types
|
||||
- [X] List
|
||||
- [X] Non-Null
|
||||
- [X] Object
|
||||
- [ ] Object
|
||||
- [ ] Generic Types
|
||||
- [X] Lifetime cycle
|
||||
- [X] Enum
|
||||
- [X] InputObject
|
||||
- [ ] Field default value
|
||||
- [X] Deprecated flag
|
||||
- [ ] Interface
|
||||
- [ ] Union
|
||||
- [ ] Generic Types
|
||||
- [ ] Query
|
||||
- [X] Fields
|
||||
- [X] Arguments
|
||||
|
@ -67,6 +70,8 @@
|
|||
- [ ] Inline fragments
|
||||
- [X] Operation name
|
||||
- [X] Variables
|
||||
- [X] Parse value
|
||||
- [ ] Check type
|
||||
- [ ] Directives
|
||||
- [ ] @include
|
||||
- [ ] @skip
|
||||
|
|
|
@ -15,6 +15,11 @@ pub fn generate(enum_args: &args::Enum, input: &DeriveInput) -> Result<TokenStre
|
|||
};
|
||||
|
||||
let gql_typename = enum_args.name.clone().unwrap_or_else(|| ident.to_string());
|
||||
let desc = enum_args
|
||||
.desc
|
||||
.as_ref()
|
||||
.map(|s| quote! { Some(#s) })
|
||||
.unwrap_or_else(|| quote! {None});
|
||||
|
||||
let mut enum_items = Vec::new();
|
||||
let mut items = Vec::new();
|
||||
|
@ -51,12 +56,11 @@ pub fn generate(enum_args: &args::Enum, input: &DeriveInput) -> Result<TokenStre
|
|||
items.push(quote! {
|
||||
#crate_name::GQLEnumItem {
|
||||
name: #gql_item_name,
|
||||
desc: #item_desc,
|
||||
value: #ident::#item_ident,
|
||||
}
|
||||
});
|
||||
schema_enum_items.push(quote! {
|
||||
#crate_name::schema::EnumValue {
|
||||
#crate_name::registry::EnumValue {
|
||||
name: #gql_item_name,
|
||||
description: #item_desc,
|
||||
deprecation: #item_deprecation,
|
||||
|
@ -82,9 +86,11 @@ pub fn generate(enum_args: &args::Enum, input: &DeriveInput) -> Result<TokenStre
|
|||
std::borrow::Cow::Borrowed(#gql_typename)
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut #crate_name::schema::Registry) -> String {
|
||||
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> String {
|
||||
registry.create_type(&Self::type_name(), |registry| {
|
||||
#crate_name::schema::Type::Enum {
|
||||
#crate_name::registry::Type::Enum {
|
||||
name: #gql_typename,
|
||||
description: #desc,
|
||||
enum_values: vec![#(#schema_enum_items),*],
|
||||
}
|
||||
})
|
||||
|
@ -92,11 +98,11 @@ pub fn generate(enum_args: &args::Enum, input: &DeriveInput) -> Result<TokenStre
|
|||
}
|
||||
|
||||
impl #crate_name::GQLInputValue for #ident {
|
||||
fn parse(value: #crate_name::Value) -> #crate_name::Result<Self> {
|
||||
fn parse(value: #crate_name::Value) -> Option<Self> {
|
||||
#crate_name::GQLEnum::parse_enum(value)
|
||||
}
|
||||
|
||||
fn parse_from_json(value: #crate_name::serde_json::Value) -> #crate_name::Result<Self> {
|
||||
fn parse_from_json(value: #crate_name::serde_json::Value) -> Option<Self> {
|
||||
#crate_name::GQLEnum::parse_json_enum(value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,11 @@ pub fn generate(object_args: &args::InputObject, input: &DeriveInput) -> Result<
|
|||
.name
|
||||
.clone()
|
||||
.unwrap_or_else(|| ident.to_string());
|
||||
let desc = object_args
|
||||
.desc
|
||||
.as_ref()
|
||||
.map(|s| quote! {Some(#s)})
|
||||
.unwrap_or_else(|| quote! {None});
|
||||
|
||||
let mut get_fields = Vec::new();
|
||||
let mut get_json_fields = Vec::new();
|
||||
|
@ -66,7 +71,7 @@ pub fn generate(object_args: &args::InputObject, input: &DeriveInput) -> Result<
|
|||
});
|
||||
fields.push(ident);
|
||||
schema_fields.push(quote! {
|
||||
#crate_name::schema::InputValue {
|
||||
#crate_name::registry::InputValue {
|
||||
name: #name,
|
||||
description: #desc,
|
||||
ty: <#ty as #crate_name::GQLType>::create_type_info(registry),
|
||||
|
@ -83,38 +88,34 @@ pub fn generate(object_args: &args::InputObject, input: &DeriveInput) -> Result<
|
|||
std::borrow::Cow::Borrowed(#gql_typename)
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut #crate_name::schema::Registry) -> String {
|
||||
registry.create_type(&Self::type_name(), |registry| #crate_name::schema::Type::InputObject {
|
||||
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> String {
|
||||
registry.create_type(&Self::type_name(), |registry| #crate_name::registry::Type::InputObject {
|
||||
name: #gql_typename,
|
||||
description: #desc,
|
||||
input_fields: vec![#(#schema_fields),*]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl #crate_name::GQLInputValue for #ident {
|
||||
fn parse(value: #crate_name::Value) -> #crate_name::Result<Self> {
|
||||
fn parse(value: #crate_name::Value) -> Option<Self> {
|
||||
use #crate_name::GQLType;
|
||||
|
||||
if let #crate_name::Value::Object(mut obj) = value {
|
||||
#(#get_fields)*
|
||||
Ok(Self { #(#fields),* })
|
||||
Some(Self { #(#fields),* })
|
||||
} else {
|
||||
Err(#crate_name::QueryError::ExpectedType {
|
||||
expect: Self::type_name(),
|
||||
actual: value,
|
||||
}.into())
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_from_json(value: #crate_name::serde_json::Value) -> #crate_name::Result<Self> {
|
||||
fn parse_from_json(value: #crate_name::serde_json::Value) -> Option<Self> {
|
||||
use #crate_name::GQLType;
|
||||
if let #crate_name::serde_json::Value::Object(mut obj) = value {
|
||||
#(#get_json_fields)*
|
||||
Ok(Self { #(#fields),* })
|
||||
Some(Self { #(#fields),* })
|
||||
} else {
|
||||
Err(#crate_name::QueryError::ExpectedJsonType {
|
||||
expect: Self::type_name(),
|
||||
actual: value,
|
||||
}.into())
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
|
|||
let crate_name = get_crate_name(object_args.internal);
|
||||
let vis = &input.vis;
|
||||
let ident = &input.ident;
|
||||
let generics = &input.generics;
|
||||
match &input.data {
|
||||
Data::Struct(_) => {}
|
||||
_ => return Err(Error::new_spanned(input, "It should be a struct.")),
|
||||
|
@ -19,6 +20,11 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
|
|||
.name
|
||||
.clone()
|
||||
.unwrap_or_else(|| ident.to_string());
|
||||
let desc = object_args
|
||||
.desc
|
||||
.as_ref()
|
||||
.map(|s| quote! {Some(#s)})
|
||||
.unwrap_or_else(|| quote! {None});
|
||||
let trait_ident = Ident::new(&format!("{}Fields", ident.to_string()), Span::call_site());
|
||||
let mut trait_fns = Vec::new();
|
||||
let mut resolvers = Vec::new();
|
||||
|
@ -68,7 +74,7 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
|
|||
});
|
||||
use_params.push(quote! { #snake_case_name });
|
||||
schema_args.push(quote! {
|
||||
#crate_name::schema::InputValue {
|
||||
#crate_name::registry::InputValue {
|
||||
name: #name_str,
|
||||
description: #desc,
|
||||
ty: <#ty as #crate_name::GQLType>::create_type_info(registry),
|
||||
|
@ -109,7 +115,7 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
|
|||
});
|
||||
|
||||
schema_fields.push(quote! {
|
||||
#crate_name::schema::Field {
|
||||
#crate_name::registry::Field {
|
||||
name: #field_name,
|
||||
description: #desc,
|
||||
args: vec![#(#schema_args),*],
|
||||
|
@ -127,20 +133,22 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
|
|||
#(#trait_fns)*
|
||||
}
|
||||
|
||||
impl #crate_name::GQLType for #ident {
|
||||
impl#generics #crate_name::GQLType for #ident#generics {
|
||||
fn type_name() -> std::borrow::Cow<'static, str> {
|
||||
std::borrow::Cow::Borrowed(#gql_typename)
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut #crate_name::schema::Registry) -> String {
|
||||
registry.create_type(&Self::type_name(), |registry| #crate_name::schema::Type::Object {
|
||||
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> String {
|
||||
registry.create_type(&Self::type_name(), |registry| #crate_name::registry::Type::Object {
|
||||
name: #gql_typename,
|
||||
description: #desc,
|
||||
fields: vec![#(#schema_fields),*]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[#crate_name::async_trait::async_trait]
|
||||
impl #crate_name::GQLOutputValue for #ident {
|
||||
impl#generics #crate_name::GQLOutputValue for #ident#generics {
|
||||
async fn resolve(&self, ctx: &#crate_name::ContextSelectionSet<'_>) -> #crate_name::Result<#crate_name::serde_json::Value> {
|
||||
use #crate_name::ErrorWithPosition;
|
||||
|
||||
|
@ -160,6 +168,9 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
|
|||
result.insert(name, #gql_typename.into());
|
||||
continue;
|
||||
}
|
||||
if field.name.as_str() == "__schema" {
|
||||
continue;
|
||||
}
|
||||
#(#resolvers)*
|
||||
#crate_name::anyhow::bail!(#crate_name::QueryError::FieldNotFound {
|
||||
field_name: field.name.clone(),
|
||||
|
@ -174,7 +185,7 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
|
|||
}
|
||||
}
|
||||
|
||||
impl #crate_name::GQLObject for #ident {}
|
||||
impl#generics #crate_name::GQLObject for #ident#generics {}
|
||||
};
|
||||
Ok(expanded.into())
|
||||
}
|
||||
|
|
|
@ -36,10 +36,12 @@ impl MyObjFields for MyObj {
|
|||
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
let res = async_graphql::QueryBuilder::new(
|
||||
let schema = async_graphql::Schema::<MyObj, async_graphql::GQLEmptyMutation>::new();
|
||||
let res = schema
|
||||
.query(
|
||||
MyObj { value: 100 },
|
||||
async_graphql::GQLEmptyMutation,
|
||||
"{ a b c }",
|
||||
"{ a b c __schema { types { kind name description } } }",
|
||||
)
|
||||
.execute()
|
||||
.await
|
||||
|
|
30
src/base.rs
30
src/base.rs
|
@ -1,4 +1,4 @@
|
|||
use crate::{schema, ContextSelectionSet, Result};
|
||||
use crate::{registry, ContextSelectionSet, Result};
|
||||
use graphql_parser::query::Value;
|
||||
use std::borrow::Cow;
|
||||
|
||||
|
@ -6,13 +6,17 @@ use std::borrow::Cow;
|
|||
pub trait GQLType {
|
||||
fn type_name() -> Cow<'static, str>;
|
||||
|
||||
fn create_type_info(registry: &mut schema::Registry) -> String;
|
||||
fn qualified_type_name() -> String {
|
||||
format!("{}!", Self::type_name())
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut registry::Registry) -> String;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait GQLInputValue: GQLType + Sized {
|
||||
fn parse(value: Value) -> Result<Self>;
|
||||
fn parse_from_json(value: serde_json::Value) -> Result<Self>;
|
||||
fn parse(value: Value) -> Option<Self>;
|
||||
fn parse_from_json(value: serde_json::Value) -> Option<Self>;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
|
@ -29,8 +33,11 @@ pub trait GQLInputObject: GQLInputValue {}
|
|||
|
||||
pub trait Scalar: Sized + Send {
|
||||
fn type_name() -> &'static str;
|
||||
fn parse(value: Value) -> Result<Self>;
|
||||
fn parse_from_json(value: serde_json::Value) -> Result<Self>;
|
||||
fn description() -> Option<&'static str> {
|
||||
None
|
||||
}
|
||||
fn parse(value: Value) -> Option<Self>;
|
||||
fn parse_from_json(value: serde_json::Value) -> Option<Self>;
|
||||
fn to_json(&self) -> Result<serde_json::Value>;
|
||||
}
|
||||
|
||||
|
@ -39,17 +46,20 @@ impl<T: Scalar> GQLType for T {
|
|||
Cow::Borrowed(T::type_name())
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut schema::Registry) -> String {
|
||||
registry.create_type(T::type_name(), |_| schema::Type::Scalar)
|
||||
fn create_type_info(registry: &mut registry::Registry) -> String {
|
||||
registry.create_type(&T::type_name(), |_| registry::Type::Scalar {
|
||||
name: T::type_name().to_string(),
|
||||
description: T::description(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar> GQLInputValue for T {
|
||||
fn parse(value: Value) -> Result<Self> {
|
||||
fn parse(value: Value) -> Option<Self> {
|
||||
T::parse(value)
|
||||
}
|
||||
|
||||
fn parse_from_json(value: serde_json::Value) -> Result<Self> {
|
||||
fn parse_from_json(value: serde_json::Value) -> Option<Self> {
|
||||
T::parse_from_json(value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::registry::Registry;
|
||||
use crate::{ErrorWithPosition, GQLInputValue, QueryError, Result};
|
||||
use fnv::FnvHasher;
|
||||
use graphql_parser::query::{Field, SelectionSet, Value};
|
||||
|
@ -43,6 +44,7 @@ pub struct ContextBase<'a, T> {
|
|||
pub(crate) item: T,
|
||||
pub(crate) data: Option<&'a Data>,
|
||||
pub(crate) variables: Option<&'a Variables>,
|
||||
pub(crate) registry: &'a Registry,
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for ContextBase<'a, T> {
|
||||
|
@ -60,6 +62,7 @@ impl<'a, T> ContextBase<'a, T> {
|
|||
item,
|
||||
data: self.data,
|
||||
variables: self.variables,
|
||||
registry: self.registry.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,8 +88,14 @@ impl<'a> ContextBase<'a, &'a Field> {
|
|||
if let Some(Value::Variable(var_name)) = &value {
|
||||
if let Some(vars) = &self.variables {
|
||||
if let Some(var_value) = vars.get(&*var_name).cloned() {
|
||||
let res = GQLInputValue::parse_from_json(var_value)
|
||||
.map_err(|err| err.with_position(self.item.position))?;
|
||||
let res =
|
||||
GQLInputValue::parse_from_json(var_value.clone()).ok_or_else(|| {
|
||||
QueryError::ExpectedJsonType {
|
||||
expect: T::qualified_type_name(),
|
||||
actual: var_value,
|
||||
}
|
||||
.with_position(self.item.position)
|
||||
})?;
|
||||
return Ok(res);
|
||||
}
|
||||
}
|
||||
|
@ -97,8 +106,14 @@ impl<'a> ContextBase<'a, &'a Field> {
|
|||
.into());
|
||||
};
|
||||
|
||||
let res = GQLInputValue::parse(value.unwrap_or(Value::Null))
|
||||
.map_err(|err| err.with_position(self.item.position))?;
|
||||
let value = value.unwrap_or(Value::Null);
|
||||
let res = GQLInputValue::parse(value.clone()).ok_or_else(|| {
|
||||
QueryError::ExpectedType {
|
||||
expect: T::qualified_type_name(),
|
||||
actual: value,
|
||||
}
|
||||
.with_position(self.item.position)
|
||||
})?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
|
13
src/error.rs
13
src/error.rs
|
@ -1,7 +1,6 @@
|
|||
use crate::Error;
|
||||
use graphql_parser::query::Value;
|
||||
use graphql_parser::Pos;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
@ -14,14 +13,11 @@ pub enum QueryError {
|
|||
NotSupported,
|
||||
|
||||
#[error("Expected type \"{expect}\", found {actual}.")]
|
||||
ExpectedType {
|
||||
expect: Cow<'static, str>,
|
||||
actual: Value,
|
||||
},
|
||||
ExpectedType { expect: String, actual: Value },
|
||||
|
||||
#[error("Expected type \"{expect}\", found {actual}.")]
|
||||
ExpectedJsonType {
|
||||
expect: Cow<'static, str>,
|
||||
expect: String,
|
||||
actual: serde_json::Value,
|
||||
},
|
||||
|
||||
|
@ -41,10 +37,7 @@ pub enum QueryError {
|
|||
NotConfiguredMutations,
|
||||
|
||||
#[error("Invalid value for enum \"{ty}\".")]
|
||||
InvalidEnumValue {
|
||||
ty: Cow<'static, str>,
|
||||
value: String,
|
||||
},
|
||||
InvalidEnumValue { ty: String, value: String },
|
||||
|
||||
#[error("Required field \"{field_name}\" for InputObject \"{object}\" does not exist.")]
|
||||
RequiredField {
|
||||
|
|
|
@ -36,9 +36,9 @@ extern crate thiserror;
|
|||
mod base;
|
||||
mod context;
|
||||
mod error;
|
||||
mod query;
|
||||
mod query_schema;
|
||||
mod model;
|
||||
mod scalars;
|
||||
mod schema;
|
||||
mod types;
|
||||
|
||||
#[doc(hidden)]
|
||||
|
@ -55,8 +55,8 @@ pub use base::Scalar;
|
|||
pub use context::{Context, ContextBase, Data, Variables};
|
||||
pub use error::{ErrorWithPosition, PositionError, QueryError, QueryParseError};
|
||||
pub use graphql_parser::query::Value;
|
||||
pub use query::QueryBuilder;
|
||||
pub use scalars::ID;
|
||||
pub use schema::{QueryBuilder, Schema};
|
||||
pub use types::GQLEmptyMutation;
|
||||
|
||||
pub type Result<T> = anyhow::Result<T>;
|
||||
|
@ -68,6 +68,6 @@ pub use base::{GQLInputObject, GQLInputValue, GQLObject, GQLOutputValue, GQLType
|
|||
#[doc(hidden)]
|
||||
pub use context::ContextSelectionSet;
|
||||
#[doc(hidden)]
|
||||
pub mod schema;
|
||||
pub mod registry;
|
||||
#[doc(hidden)]
|
||||
pub use types::{GQLEnum, GQLEnumItem};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{Context, Result};
|
||||
use crate::{registry, Context, Result};
|
||||
use async_graphql_derive::Object;
|
||||
|
||||
#[Object(
|
||||
|
@ -9,23 +9,26 @@ use async_graphql_derive::Object;
|
|||
field(name = "isDeprecated", type = "bool", owned),
|
||||
field(name = "deprecationReason", type = "Option<String>", owned)
|
||||
)]
|
||||
pub struct __EnumValue {}
|
||||
pub struct __EnumValue<'a> {
|
||||
pub registry: &'a registry::Registry,
|
||||
pub value: &'a registry::EnumValue,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl __EnumValueFields for __EnumValue {
|
||||
impl<'a> __EnumValueFields for __EnumValue<'a> {
|
||||
async fn name(&self, _: &Context<'_>) -> Result<String> {
|
||||
todo!()
|
||||
Ok(self.value.name.to_string())
|
||||
}
|
||||
|
||||
async fn description(&self, _: &Context<'_>) -> Result<Option<String>> {
|
||||
todo!()
|
||||
Ok(self.value.description.map(|s| s.to_string()))
|
||||
}
|
||||
|
||||
async fn is_deprecated(&self, _: &Context<'_>) -> Result<bool> {
|
||||
todo!()
|
||||
Ok(self.value.deprecation.is_some())
|
||||
}
|
||||
|
||||
async fn deprecation_reason(&self, _: &Context<'_>) -> Result<Option<String>> {
|
||||
todo!()
|
||||
Ok(self.value.deprecation.map(|s| s.to_string()))
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use crate::query_schema::__Type;
|
||||
use crate::{Context, Result};
|
||||
use crate::model::__Type;
|
||||
use crate::{registry, Context, Result};
|
||||
use async_graphql_derive::Object;
|
||||
|
||||
#[Object(
|
||||
|
@ -11,28 +11,34 @@ use async_graphql_derive::Object;
|
|||
field(name = "isDeprecated", type = "bool", owned),
|
||||
field(name = "deprecationReason", type = "Option<String>", owned)
|
||||
)]
|
||||
pub struct __Field {}
|
||||
pub struct __Field<'a> {
|
||||
pub registry: &'a registry::Registry,
|
||||
pub field: &'a registry::Field,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
#[allow()]
|
||||
impl __FieldFields for __Field {
|
||||
impl<'a> __FieldFields for __Field<'a> {
|
||||
async fn name(&self, _: &Context<'_>) -> Result<String> {
|
||||
todo!()
|
||||
Ok(self.field.name.to_string())
|
||||
}
|
||||
|
||||
async fn description(&self, _: &Context<'_>) -> Result<Option<String>> {
|
||||
todo!()
|
||||
Ok(self.field.description.map(|s| s.to_string()))
|
||||
}
|
||||
|
||||
async fn ty(&self, _: &Context<'_>) -> Result<__Type> {
|
||||
todo!()
|
||||
async fn ty<'b>(&'b self, _: &Context<'_>) -> Result<__Type<'b>> {
|
||||
Ok(__Type {
|
||||
registry: self.registry,
|
||||
ty: &self.registry[&self.field.ty],
|
||||
})
|
||||
}
|
||||
|
||||
async fn is_deprecated(&self, _: &Context<'_>) -> Result<bool> {
|
||||
todo!()
|
||||
Ok(self.field.deprecation.is_some())
|
||||
}
|
||||
|
||||
async fn deprecation_reason(&self, _: &Context<'_>) -> Result<Option<String>> {
|
||||
todo!()
|
||||
Ok(self.field.deprecation.map(|s| s.to_string()))
|
||||
}
|
||||
}
|
38
src/model/input_value.rs
Normal file
38
src/model/input_value.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
use crate::model::__Type;
|
||||
use crate::{registry, Context, Result};
|
||||
use async_graphql_derive::Object;
|
||||
|
||||
#[Object(
|
||||
internal,
|
||||
desc = "Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.",
|
||||
field(name = "name", type = "String", owned),
|
||||
field(name = "description", type = "Option<String>", owned),
|
||||
field(name = "type", resolver = "ty", type = "__Type", owned),
|
||||
field(name = "defaultValue", type = "Option<String>", owned)
|
||||
)]
|
||||
pub struct __InputValue<'a> {
|
||||
pub registry: &'a registry::Registry,
|
||||
pub input_value: &'a registry::InputValue,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<'a> __InputValueFields for __InputValue<'a> {
|
||||
async fn name(&self, _: &Context<'_>) -> Result<String> {
|
||||
Ok(self.input_value.name.to_string())
|
||||
}
|
||||
|
||||
async fn description(&self, _: &Context<'_>) -> Result<Option<String>> {
|
||||
Ok(self.input_value.description.map(|s| s.to_string()))
|
||||
}
|
||||
|
||||
async fn ty<'b>(&'b self, _: &Context<'_>) -> Result<__Type<'b>> {
|
||||
Ok(__Type {
|
||||
registry: self.registry,
|
||||
ty: &self.registry[&self.input_value.ty],
|
||||
})
|
||||
}
|
||||
|
||||
async fn default_value(&self, _: &Context<'_>) -> Result<Option<String>> {
|
||||
Ok(self.input_value.default_value.map(|s| s.to_string()))
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ mod enum_value;
|
|||
mod field;
|
||||
mod input_value;
|
||||
mod kind;
|
||||
mod schema;
|
||||
mod r#type;
|
||||
|
||||
pub use enum_value::__EnumValue;
|
||||
|
@ -9,3 +10,4 @@ pub use field::__Field;
|
|||
pub use input_value::__InputValue;
|
||||
pub use kind::__TypeKind;
|
||||
pub use r#type::__Type;
|
||||
pub use schema::__Schema;
|
60
src/model/schema.rs
Normal file
60
src/model/schema.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
use crate::model::__Type;
|
||||
use crate::registry;
|
||||
use crate::{Context, Result};
|
||||
use async_graphql_derive::Object;
|
||||
|
||||
#[Object(
|
||||
internal,
|
||||
desc = "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.",
|
||||
field(
|
||||
name = "types",
|
||||
desc = "A list of all types supported by this server.",
|
||||
type = "Vec<__Type>",
|
||||
owned
|
||||
),
|
||||
field(
|
||||
name = "queryType",
|
||||
desc = "The type that query operations will be rooted at.",
|
||||
type = "__Type",
|
||||
owned
|
||||
),
|
||||
field(
|
||||
name = "mutationType",
|
||||
desc = "If this server supports mutation, the type that mutation operations will be rooted at.",
|
||||
type = "__Type",
|
||||
owned
|
||||
)
|
||||
)]
|
||||
pub struct __Schema<'a> {
|
||||
pub registry: &'a registry::Registry,
|
||||
pub query_type: &'a str,
|
||||
pub mutation_type: &'a str,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<'a> __SchemaFields for __Schema<'a> {
|
||||
async fn types<'b>(&'b self, _: &Context<'_>) -> Result<Vec<__Type<'b>>> {
|
||||
Ok(self
|
||||
.registry
|
||||
.values()
|
||||
.map(|ty| __Type {
|
||||
registry: &self.registry,
|
||||
ty,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn query_type<'b>(&'b self, _: &Context<'_>) -> Result<__Type<'b>> {
|
||||
Ok(__Type {
|
||||
registry: &self.registry,
|
||||
ty: &self.registry[self.query_type],
|
||||
})
|
||||
}
|
||||
|
||||
async fn mutation_type<'b>(&'b self, _: &Context<'_>) -> Result<__Type<'b>> {
|
||||
Ok(__Type {
|
||||
registry: &self.registry,
|
||||
ty: &self.registry[self.mutation_type],
|
||||
})
|
||||
}
|
||||
}
|
160
src/model/type.rs
Normal file
160
src/model/type.rs
Normal file
|
@ -0,0 +1,160 @@
|
|||
use crate::model::{__EnumValue, __Field, __InputValue, __TypeKind};
|
||||
use crate::registry::Type;
|
||||
use crate::{registry, Context, Result};
|
||||
use async_graphql_derive::Object;
|
||||
|
||||
#[Object(
|
||||
internal,
|
||||
desc = r#"
|
||||
The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.
|
||||
|
||||
Depending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.
|
||||
"#,
|
||||
field(name = "kind", type = "__TypeKind", owned),
|
||||
field(name = "name", type = "Option<String>", owned),
|
||||
field(name = "description", type = "Option<String>", owned),
|
||||
field(
|
||||
name = "fields",
|
||||
type = "Option<Vec<__Field>>",
|
||||
owned,
|
||||
arg(name = "includeDeprecated", type = "bool")
|
||||
),
|
||||
field(name = "interfaces", type = "Option<Vec<__Type>>", owned),
|
||||
field(name = "possibleTypes", type = "Option<Vec<__Type>>", owned),
|
||||
field(
|
||||
name = "enumValues",
|
||||
type = "Option<Vec<__EnumValue>>",
|
||||
owned,
|
||||
arg(name = "includeDeprecated", type = "bool")
|
||||
),
|
||||
field(name = "inputFields", type = "Option<Vec<__InputValue>>", owned),
|
||||
field(name = "ofType", type = "Option<__Type>", owned)
|
||||
)]
|
||||
pub struct __Type<'a> {
|
||||
pub registry: &'a registry::Registry,
|
||||
pub ty: &'a registry::Type,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<'a> __TypeFields for __Type<'a> {
|
||||
async fn kind(&self, _: &Context<'_>) -> Result<__TypeKind> {
|
||||
Ok(match self.ty {
|
||||
registry::Type::Scalar { .. } => __TypeKind::SCALAR,
|
||||
registry::Type::Object { .. } => __TypeKind::OBJECT,
|
||||
registry::Type::Interface { .. } => __TypeKind::INTERFACE,
|
||||
registry::Type::Union { .. } => __TypeKind::UNION,
|
||||
registry::Type::Enum { .. } => __TypeKind::ENUM,
|
||||
registry::Type::InputObject { .. } => __TypeKind::INPUT_OBJECT,
|
||||
registry::Type::List { .. } => __TypeKind::LIST,
|
||||
registry::Type::NonNull { .. } => __TypeKind::NON_NULL,
|
||||
})
|
||||
}
|
||||
|
||||
async fn name(&self, _: &Context<'_>) -> Result<Option<String>> {
|
||||
Ok(match self.ty {
|
||||
registry::Type::Scalar { name, .. } => Some(name.clone()),
|
||||
registry::Type::Object { name, .. } => Some(name.to_string()),
|
||||
registry::Type::Interface { name, .. } => Some(name.to_string()),
|
||||
registry::Type::Union { name, .. } => Some(name.to_string()),
|
||||
registry::Type::Enum { name, .. } => Some(name.to_string()),
|
||||
registry::Type::InputObject { name, .. } => Some(name.to_string()),
|
||||
registry::Type::List { .. } => None,
|
||||
registry::Type::NonNull { .. } => None,
|
||||
})
|
||||
}
|
||||
|
||||
async fn description(&self, _: &Context<'_>) -> Result<Option<String>> {
|
||||
Ok(match self.ty {
|
||||
registry::Type::Scalar { description, .. } => description.map(|s| s.to_string()),
|
||||
registry::Type::Object { description, .. } => description.map(|s| s.to_string()),
|
||||
registry::Type::Interface { description, .. } => description.map(|s| s.to_string()),
|
||||
registry::Type::Union { description, .. } => description.map(|s| s.to_string()),
|
||||
registry::Type::Enum { description, .. } => description.map(|s| s.to_string()),
|
||||
registry::Type::InputObject { description, .. } => description.map(|s| s.to_string()),
|
||||
registry::Type::List { .. } => None,
|
||||
registry::Type::NonNull { .. } => None,
|
||||
})
|
||||
}
|
||||
|
||||
async fn fields<'b>(
|
||||
&'b self,
|
||||
_: &Context<'_>,
|
||||
include_deprecated: bool,
|
||||
) -> Result<Option<Vec<__Field<'b>>>> {
|
||||
if let Type::Object { fields, .. } = self.ty {
|
||||
Ok(Some(
|
||||
fields
|
||||
.iter()
|
||||
.filter(|field| {
|
||||
if include_deprecated {
|
||||
true
|
||||
} else {
|
||||
field.deprecation.is_none()
|
||||
}
|
||||
})
|
||||
.map(|field| __Field {
|
||||
registry: self.registry,
|
||||
field,
|
||||
})
|
||||
.collect(),
|
||||
))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
async fn interfaces<'b>(&'b self, _: &Context<'_>) -> Result<Option<Vec<__Type<'b>>>> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn possible_types<'b>(&'b self, _: &Context<'_>) -> Result<Option<Vec<__Type<'b>>>> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn enum_values<'b>(
|
||||
&'b self,
|
||||
_: &Context<'_>,
|
||||
include_deprecated: bool,
|
||||
) -> Result<Option<Vec<__EnumValue<'b>>>> {
|
||||
if let Type::Enum { enum_values, .. } = self.ty {
|
||||
Ok(Some(
|
||||
enum_values
|
||||
.iter()
|
||||
.filter(|field| {
|
||||
if include_deprecated {
|
||||
true
|
||||
} else {
|
||||
field.deprecation.is_none()
|
||||
}
|
||||
})
|
||||
.map(|value| __EnumValue {
|
||||
registry: self.registry,
|
||||
value,
|
||||
})
|
||||
.collect(),
|
||||
))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
async fn input_fields<'b>(&'b self, _: &Context<'_>) -> Result<Option<Vec<__InputValue<'b>>>> {
|
||||
if let Type::InputObject { input_fields, .. } = self.ty {
|
||||
Ok(Some(
|
||||
input_fields
|
||||
.iter()
|
||||
.map(|input_value| __InputValue {
|
||||
registry: self.registry,
|
||||
input_value,
|
||||
})
|
||||
.collect(),
|
||||
))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
async fn of_type<'b>(&'b self, _: &Context<'_>) -> Result<Option<__Type<'b>>> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
use crate::query_schema::__Type;
|
||||
use crate::{Context, Result};
|
||||
use async_graphql_derive::Object;
|
||||
|
||||
#[Object(
|
||||
internal,
|
||||
desc = "Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.",
|
||||
field(name = "name", type = "String", owned),
|
||||
field(name = "description", type = "Option<String>", owned),
|
||||
field(name = "type", resolver = "ty", type = "__Type", owned),
|
||||
field(name = "defaultValue", type = "String", owned)
|
||||
)]
|
||||
pub struct __InputValue {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl __InputValueFields for __InputValue {
|
||||
async fn name(&self, _: &Context<'_>) -> Result<String> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn description(&self, _: &Context<'_>) -> Result<Option<String>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn ty(&self, _: &Context<'_>) -> Result<__Type> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn default_value(&self, _: &Context<'_>) -> Result<String> {
|
||||
todo!()
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
use crate::query_schema::{__EnumValue, __InputValue, __TypeKind};
|
||||
use crate::{Context, Result};
|
||||
use async_graphql_derive::Object;
|
||||
|
||||
#[Object(
|
||||
internal,
|
||||
desc = r#"
|
||||
The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.
|
||||
|
||||
Depending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.
|
||||
"#,
|
||||
field(name = "kind", type = "__TypeKind", owned),
|
||||
field(name = "name", type = "String", owned),
|
||||
field(name = "description", type = "Option<String>", owned),
|
||||
field(
|
||||
name = "fields",
|
||||
type = "Option<Vec<__Type>>",
|
||||
owned,
|
||||
arg(name = "includeDeprecated", type = "bool")
|
||||
),
|
||||
field(name = "interfaces", type = "Option<Vec<__Type>>", owned),
|
||||
field(name = "possibleTypes", type = "Option<Vec<__Type>>", owned),
|
||||
field(name = "enumValues", type = "Option<Vec<__EnumValue>>", owned),
|
||||
field(name = "inputFields", type = "Option<Vec<__InputValue>>", owned),
|
||||
field(name = "ofType", type = "Option<__Type>", owned)
|
||||
)]
|
||||
pub struct __Type {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl __TypeFields for __Type {
|
||||
async fn kind<'a>(&'a self, _: &Context<'_>) -> Result<__TypeKind> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn name<'a>(&'a self, _: &Context<'_>) -> Result<String> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn description<'a>(&'a self, _: &Context<'_>) -> Result<Option<String>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn fields<'a>(
|
||||
&'a self,
|
||||
_: &Context<'_>,
|
||||
include_deprecated: bool,
|
||||
) -> Result<Option<Vec<__Type>>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn interfaces<'a>(&'a self, _: &Context<'_>) -> Result<Option<Vec<__Type>>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn possible_types<'a>(&'a self, _: &Context<'_>) -> Result<Option<Vec<__Type>>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn enum_values<'a>(&'a self, _: &Context<'_>) -> Result<Option<Vec<__EnumValue>>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn input_fields<'a>(&'a self, _: &Context<'_>) -> Result<Option<Vec<__InputValue>>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn of_type<'a>(&'a self, _: &Context<'_>) -> Result<Option<__Type>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
|
@ -1,12 +1,20 @@
|
|||
use crate::schema::Type;
|
||||
use std::cell::RefCell;
|
||||
use crate::registry::Type;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Registry {
|
||||
types: HashMap<String, Type>,
|
||||
}
|
||||
|
||||
impl Deref for Registry {
|
||||
type Target = HashMap<String, Type>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.types
|
||||
}
|
||||
}
|
||||
|
||||
impl Registry {
|
||||
pub fn create_type<F: FnMut(&mut Registry) -> Type>(&mut self, name: &str, mut f: F) -> String {
|
||||
if !self.types.contains_key(name) {
|
||||
|
@ -15,8 +23,4 @@ impl Registry {
|
|||
}
|
||||
name.to_string()
|
||||
}
|
||||
|
||||
pub fn get_type(&self, name: &str) -> Option<&Type> {
|
||||
self.types.get(name)
|
||||
}
|
||||
}
|
|
@ -20,21 +20,34 @@ pub struct EnumValue {
|
|||
}
|
||||
|
||||
pub enum Type {
|
||||
Scalar,
|
||||
Scalar {
|
||||
name: String,
|
||||
description: Option<&'static str>,
|
||||
},
|
||||
Object {
|
||||
name: &'static str,
|
||||
description: Option<&'static str>,
|
||||
fields: Vec<Field>,
|
||||
},
|
||||
Interface {
|
||||
name: &'static str,
|
||||
description: Option<&'static str>,
|
||||
fields: Vec<Field>,
|
||||
possible_types: Vec<usize>,
|
||||
},
|
||||
Union {
|
||||
name: &'static str,
|
||||
description: Option<&'static str>,
|
||||
possible_types: Vec<usize>,
|
||||
},
|
||||
Enum {
|
||||
name: &'static str,
|
||||
description: Option<&'static str>,
|
||||
enum_values: Vec<EnumValue>,
|
||||
},
|
||||
InputObject {
|
||||
name: &'static str,
|
||||
description: Option<&'static str>,
|
||||
input_fields: Vec<InputValue>,
|
||||
},
|
||||
List {
|
|
@ -1,33 +1,25 @@
|
|||
use crate::{GQLType, QueryError, Result, Scalar, Value};
|
||||
use crate::{Result, Scalar, Value};
|
||||
|
||||
impl Scalar for bool {
|
||||
fn type_name() -> &'static str {
|
||||
"Boolean!"
|
||||
"Boolean"
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> Result<Self> {
|
||||
fn description() -> Option<&'static str> {
|
||||
Some("The `Boolean` scalar type represents `true` or `false`.")
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> Option<Self> {
|
||||
match value {
|
||||
Value::Boolean(n) => Ok(n),
|
||||
_ => {
|
||||
return Err(QueryError::ExpectedType {
|
||||
expect: <Self as GQLType>::type_name(),
|
||||
actual: value,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
Value::Boolean(n) => Some(n),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_from_json(value: serde_json::Value) -> Result<Self> {
|
||||
fn parse_from_json(value: serde_json::Value) -> Option<Self> {
|
||||
match value {
|
||||
serde_json::Value::Bool(n) => Ok(n),
|
||||
_ => {
|
||||
return Err(QueryError::ExpectedJsonType {
|
||||
expect: <Self as GQLType>::type_name(),
|
||||
actual: value,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
serde_json::Value::Bool(n) => Some(n),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,35 +1,22 @@
|
|||
use crate::{QueryError, Result, Scalar, Value};
|
||||
use crate::{Result, Scalar, Value};
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
use std::borrow::Cow;
|
||||
|
||||
impl Scalar for DateTime<Utc> {
|
||||
fn type_name() -> &'static str {
|
||||
"DateTime"
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> Result<Self> {
|
||||
fn parse(value: Value) -> Option<Self> {
|
||||
match value {
|
||||
Value::String(s) => Ok(Utc.datetime_from_str(&s, "%+")?),
|
||||
_ => {
|
||||
return Err(QueryError::ExpectedType {
|
||||
expect: Cow::Borrowed(Self::type_name()),
|
||||
actual: value,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
Value::String(s) => Some(Utc.datetime_from_str(&s, "%+").ok()?),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_from_json(value: serde_json::Value) -> Result<Self> {
|
||||
fn parse_from_json(value: serde_json::Value) -> Option<Self> {
|
||||
match value {
|
||||
serde_json::Value::String(s) => Ok(Utc.datetime_from_str(&s, "%+")?),
|
||||
_ => {
|
||||
return Err(QueryError::ExpectedJsonType {
|
||||
expect: Cow::Borrowed(Self::type_name()),
|
||||
actual: value,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
serde_json::Value::String(s) => Some(Utc.datetime_from_str(&s, "%+").ok()?),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,38 +1,29 @@
|
|||
use crate::{GQLType, QueryError, Result, Scalar, Value};
|
||||
use std::borrow::Cow;
|
||||
use crate::{Result, Scalar, Value};
|
||||
|
||||
macro_rules! impl_float_scalars {
|
||||
($($ty:ty),*) => {
|
||||
$(
|
||||
impl Scalar for $ty {
|
||||
fn type_name() -> &'static str {
|
||||
"Float!"
|
||||
"Float"
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> Result<Self> {
|
||||
fn description() -> Option<&'static str> {
|
||||
Some("The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point).")
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> Option<Self> {
|
||||
match value {
|
||||
Value::Int(n) => Ok(n.as_i64().unwrap() as Self),
|
||||
Value::Float(n) => Ok(n as Self),
|
||||
_ => {
|
||||
return Err(QueryError::ExpectedType {
|
||||
expect: Cow::Borrowed(<Self as Scalar>::type_name()),
|
||||
actual: value,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
Value::Int(n) => Some(n.as_i64().unwrap() as Self),
|
||||
Value::Float(n) => Some(n as Self),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_from_json(value: serde_json::Value) -> Result<Self> {
|
||||
fn parse_from_json(value: serde_json::Value) -> Option<Self> {
|
||||
match value {
|
||||
serde_json::Value::Number(n) => Ok(n.as_f64().unwrap() as Self),
|
||||
_ => {
|
||||
return Err(QueryError::ExpectedJsonType {
|
||||
expect: <Self as GQLType>::type_name(),
|
||||
actual: value,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
serde_json::Value::Number(n) => Some(n.as_f64().unwrap() as Self),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use crate::{QueryError, Result, Scalar, Value};
|
||||
use std::borrow::Cow;
|
||||
use crate::{Result, Scalar, Value};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
|
||||
|
@ -24,31 +23,19 @@ impl Scalar for ID {
|
|||
"ID"
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> Result<Self> {
|
||||
fn parse(value: Value) -> Option<Self> {
|
||||
match value {
|
||||
Value::Int(n) => Ok(ID(n.as_i64().unwrap().to_string())),
|
||||
Value::String(s) => Ok(ID(s)),
|
||||
_ => {
|
||||
return Err(QueryError::ExpectedType {
|
||||
expect: Cow::Borrowed(Self::type_name()),
|
||||
actual: value,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
Value::Int(n) => Some(ID(n.as_i64().unwrap().to_string())),
|
||||
Value::String(s) => Some(ID(s)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_from_json(value: serde_json::Value) -> Result<Self> {
|
||||
fn parse_from_json(value: serde_json::Value) -> Option<Self> {
|
||||
match value {
|
||||
serde_json::Value::Number(n) if n.is_i64() => Ok(ID(n.as_i64().unwrap().to_string())),
|
||||
serde_json::Value::String(s) => Ok(ID(s)),
|
||||
_ => {
|
||||
return Err(QueryError::ExpectedJsonType {
|
||||
expect: Cow::Borrowed(Self::type_name()),
|
||||
actual: value,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
serde_json::Value::Number(n) if n.is_i64() => Some(ID(n.as_i64().unwrap().to_string())),
|
||||
serde_json::Value::String(s) => Some(ID(s)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,38 +1,29 @@
|
|||
use crate::{GQLType, QueryError, Result, Scalar, Value};
|
||||
use std::borrow::Cow;
|
||||
use crate::{Result, Scalar, Value};
|
||||
|
||||
macro_rules! impl_integer_scalars {
|
||||
($($ty:ty),*) => {
|
||||
$(
|
||||
impl Scalar for $ty {
|
||||
fn type_name() -> &'static str {
|
||||
"Int!"
|
||||
"Int"
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> Result<Self> {
|
||||
fn description() -> Option<&'static str> {
|
||||
Some("The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.")
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> Option<Self> {
|
||||
match value {
|
||||
Value::Int(n) => Ok(n.as_i64().unwrap() as Self),
|
||||
_ => {
|
||||
return Err(QueryError::ExpectedType {
|
||||
expect: Cow::Borrowed(<Self as Scalar>::type_name()),
|
||||
actual: value,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
Value::Int(n) => Some(n.as_i64().unwrap() as Self),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_from_json(value: serde_json::Value) -> Result<Self> {
|
||||
fn parse_from_json(value: serde_json::Value) -> Option<Self> {
|
||||
match value {
|
||||
serde_json::Value::Number(n) if n.is_i64() => Ok(n.as_i64().unwrap() as Self),
|
||||
serde_json::Value::Number(n) => Ok(n.as_f64().unwrap() as Self),
|
||||
_ => {
|
||||
return Err(QueryError::ExpectedJsonType {
|
||||
expect: <Self as GQLType>::type_name(),
|
||||
actual: value,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
serde_json::Value::Number(n) if n.is_i64() => Some(n.as_i64().unwrap() as Self),
|
||||
serde_json::Value::Number(n) => Some(n.as_f64().unwrap() as Self),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,35 +1,29 @@
|
|||
use crate::schema;
|
||||
use crate::{ContextSelectionSet, GQLOutputValue, GQLType, QueryError, Result, Scalar, Value};
|
||||
use crate::registry;
|
||||
use crate::{ContextSelectionSet, GQLOutputValue, GQLType, Result, Scalar, Value};
|
||||
use std::borrow::Cow;
|
||||
|
||||
const STRING_DESC:&'static str = "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.";
|
||||
|
||||
impl Scalar for String {
|
||||
fn type_name() -> &'static str {
|
||||
"String!"
|
||||
"String"
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> Result<Self> {
|
||||
fn description() -> Option<&'static str> {
|
||||
Some(STRING_DESC)
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> Option<Self> {
|
||||
match value {
|
||||
Value::String(s) => Ok(s),
|
||||
_ => {
|
||||
return Err(QueryError::ExpectedType {
|
||||
expect: <Self as GQLType>::type_name(),
|
||||
actual: value,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
Value::String(s) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_from_json(value: serde_json::Value) -> Result<Self> {
|
||||
fn parse_from_json(value: serde_json::Value) -> Option<Self> {
|
||||
match value {
|
||||
serde_json::Value::String(s) => Ok(s),
|
||||
_ => {
|
||||
return Err(QueryError::ExpectedJsonType {
|
||||
expect: <Self as GQLType>::type_name(),
|
||||
actual: value,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
serde_json::Value::String(s) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,11 +34,14 @@ impl Scalar for String {
|
|||
|
||||
impl<'a> GQLType for &'a str {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("String!")
|
||||
Cow::Borrowed("String")
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut schema::Registry) -> String {
|
||||
registry.create_type(&Self::type_name(), |_| schema::Type::Scalar)
|
||||
fn create_type_info(registry: &mut registry::Registry) -> String {
|
||||
registry.create_type(&Self::type_name(), |_| registry::Type::Scalar {
|
||||
name: Self::type_name().to_string(),
|
||||
description: Some(STRING_DESC),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use crate::{QueryError, Result, Scalar, Value};
|
||||
use std::borrow::Cow;
|
||||
use crate::{Result, Scalar, Value};
|
||||
use uuid::Uuid;
|
||||
|
||||
impl Scalar for Uuid {
|
||||
|
@ -7,29 +6,17 @@ impl Scalar for Uuid {
|
|||
"UUID"
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> Result<Self> {
|
||||
fn parse(value: Value) -> Option<Self> {
|
||||
match value {
|
||||
Value::String(s) => Ok(Uuid::parse_str(&s)?),
|
||||
_ => {
|
||||
return Err(QueryError::ExpectedType {
|
||||
expect: Cow::Borrowed(Self::type_name()),
|
||||
actual: value,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
Value::String(s) => Some(Uuid::parse_str(&s).ok()?),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_from_json(value: serde_json::Value) -> Result<Self> {
|
||||
fn parse_from_json(value: serde_json::Value) -> Option<Self> {
|
||||
match value {
|
||||
serde_json::Value::String(s) => Ok(Uuid::parse_str(&s)?),
|
||||
_ => {
|
||||
return Err(QueryError::ExpectedJsonType {
|
||||
expect: Cow::Borrowed(Self::type_name()),
|
||||
actual: value,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
serde_json::Value::String(s) => Some(Uuid::parse_str(&s).ok()?),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,30 +1,64 @@
|
|||
use crate::registry::Registry;
|
||||
use crate::types::QueryRoot;
|
||||
use crate::{
|
||||
ContextBase, Data, ErrorWithPosition, GQLObject, QueryError, QueryParseError, Result, Variables,
|
||||
ContextBase, Data, ErrorWithPosition, GQLObject, GQLOutputValue, QueryError, QueryParseError,
|
||||
Result, Variables,
|
||||
};
|
||||
use graphql_parser::parse_query;
|
||||
use graphql_parser::query::{Definition, OperationDefinition};
|
||||
use serde::export::PhantomData;
|
||||
|
||||
pub struct QueryBuilder<'a, Query, Mutation> {
|
||||
pub struct Schema<Query, Mutation> {
|
||||
mark_query: PhantomData<Query>,
|
||||
mark_mutation: PhantomData<Mutation>,
|
||||
registry: Registry,
|
||||
}
|
||||
|
||||
impl<Query: GQLObject, Mutation: GQLObject> Schema<Query, Mutation> {
|
||||
pub fn new() -> Self {
|
||||
let mut registry = Default::default();
|
||||
Query::create_type_info(&mut registry);
|
||||
Mutation::create_type_info(&mut registry);
|
||||
Self {
|
||||
mark_query: PhantomData,
|
||||
mark_mutation: PhantomData,
|
||||
registry,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn query<'a>(
|
||||
&'a self,
|
||||
query: Query,
|
||||
mutation: Mutation,
|
||||
query_source: &'a str,
|
||||
) -> QueryBuilder<'a, Query, Mutation> {
|
||||
QueryBuilder {
|
||||
query: QueryRoot {
|
||||
inner: query,
|
||||
query_type: Query::type_name().to_string(),
|
||||
mutation_type: Mutation::type_name().to_string(),
|
||||
},
|
||||
mutation,
|
||||
registry: &self.registry,
|
||||
query_source,
|
||||
operation_name: None,
|
||||
variables: None,
|
||||
data: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct QueryBuilder<'a, Query, Mutation> {
|
||||
query: QueryRoot<Query>,
|
||||
mutation: Mutation,
|
||||
registry: &'a Registry,
|
||||
query_source: &'a str,
|
||||
operation_name: Option<&'a str>,
|
||||
variables: Option<&'a Variables>,
|
||||
data: Option<&'a Data>,
|
||||
}
|
||||
|
||||
impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
|
||||
pub fn new(query: Query, mutation: Mutation, query_source: &'a str) -> Self {
|
||||
Self {
|
||||
query,
|
||||
mutation,
|
||||
query_source,
|
||||
operation_name: None,
|
||||
variables: None,
|
||||
data: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn operator_name(self, name: &'a str) -> Self {
|
||||
QueryBuilder {
|
||||
operation_name: Some(name),
|
||||
|
@ -48,8 +82,8 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
|
|||
|
||||
pub async fn execute(self) -> Result<serde_json::Value>
|
||||
where
|
||||
Query: GQLObject,
|
||||
Mutation: GQLObject,
|
||||
Query: GQLObject + Send + Sync,
|
||||
Mutation: GQLObject + Send + Sync,
|
||||
{
|
||||
let document =
|
||||
parse_query(self.query_source).map_err(|err| QueryParseError(err.to_string()))?;
|
||||
|
@ -62,6 +96,7 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
|
|||
item: selection_set,
|
||||
data: self.data.as_deref(),
|
||||
variables: self.variables.as_deref(),
|
||||
registry: &self.registry,
|
||||
};
|
||||
return self.query.resolve(&ctx).await;
|
||||
}
|
||||
|
@ -74,6 +109,7 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
|
|||
item: &query.selection_set,
|
||||
data: self.data.as_deref(),
|
||||
variables: self.variables.as_deref(),
|
||||
registry: self.registry.clone(),
|
||||
};
|
||||
return self.query.resolve(&ctx).await;
|
||||
}
|
||||
|
@ -86,6 +122,7 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
|
|||
item: &mutation.selection_set,
|
||||
data: self.data.as_deref(),
|
||||
variables: self.variables.as_deref(),
|
||||
registry: self.registry.clone(),
|
||||
};
|
||||
return self.mutation.resolve(&ctx).await;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
schema, ContextSelectionSet, ErrorWithPosition, GQLObject, GQLOutputValue, GQLType, QueryError,
|
||||
Result,
|
||||
registry, ContextSelectionSet, ErrorWithPosition, GQLObject, GQLOutputValue, GQLType,
|
||||
QueryError, Result,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
|
||||
|
@ -11,8 +11,10 @@ impl GQLType for GQLEmptyMutation {
|
|||
Cow::Borrowed("EmptyMutation")
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut schema::Registry) -> String {
|
||||
registry.create_type(&Self::type_name(), |_| schema::Type::Object {
|
||||
fn create_type_info(registry: &mut registry::Registry) -> String {
|
||||
registry.create_type(&Self::type_name(), |_| registry::Type::Object {
|
||||
name: "EmptyMutation",
|
||||
description: None,
|
||||
fields: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use crate::{GQLType, QueryError, Result};
|
||||
use crate::{GQLType, Result};
|
||||
use graphql_parser::query::Value;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct GQLEnumItem<T> {
|
||||
pub name: &'static str,
|
||||
pub desc: Option<&'static str>,
|
||||
pub value: T,
|
||||
}
|
||||
|
||||
|
@ -13,54 +12,34 @@ pub struct GQLEnumItem<T> {
|
|||
pub trait GQLEnum: GQLType + Sized + Eq + Send + Copy + Sized + 'static {
|
||||
fn items() -> &'static [GQLEnumItem<Self>];
|
||||
|
||||
fn parse_enum(value: Value) -> Result<Self> {
|
||||
fn parse_enum(value: Value) -> Option<Self> {
|
||||
match value {
|
||||
Value::Enum(s) => {
|
||||
let items = Self::items();
|
||||
for item in items {
|
||||
if item.name == s {
|
||||
return Ok(item.value);
|
||||
return Some(item.value);
|
||||
}
|
||||
}
|
||||
Err(QueryError::InvalidEnumValue {
|
||||
ty: Self::type_name(),
|
||||
value: s,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
_ => {
|
||||
return Err(QueryError::ExpectedType {
|
||||
expect: Self::type_name(),
|
||||
actual: value,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn parse_json_enum(value: serde_json::Value) -> Result<Self> {
|
||||
fn parse_json_enum(value: serde_json::Value) -> Option<Self> {
|
||||
match value {
|
||||
serde_json::Value::String(s) => {
|
||||
let items = Self::items();
|
||||
for item in items {
|
||||
if item.name == s {
|
||||
return Ok(item.value);
|
||||
return Some(item.value);
|
||||
}
|
||||
}
|
||||
Err(QueryError::InvalidEnumValue {
|
||||
ty: Self::type_name(),
|
||||
value: s,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
_ => {
|
||||
return Err(QueryError::ExpectedJsonType {
|
||||
expect: Self::type_name(),
|
||||
actual: value,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn resolve_enum(&self) -> Result<serde_json::Value> {
|
||||
|
|
|
@ -1,54 +1,40 @@
|
|||
use crate::{
|
||||
schema, ContextSelectionSet, GQLInputValue, GQLOutputValue, GQLType, QueryError, Result, Value,
|
||||
};
|
||||
use crate::{registry, ContextSelectionSet, GQLInputValue, GQLOutputValue, GQLType, Result, Value};
|
||||
use std::borrow::Cow;
|
||||
|
||||
impl<T: GQLType> GQLType for Vec<T> {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Owned(format!("[{}]!", T::type_name()))
|
||||
Cow::Owned(format!("[{}]", T::qualified_type_name()))
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut schema::Registry) -> String {
|
||||
fn create_type_info(registry: &mut registry::Registry) -> String {
|
||||
T::create_type_info(registry)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: GQLInputValue> GQLInputValue for Vec<T> {
|
||||
fn parse(value: Value) -> Result<Self> {
|
||||
fn parse(value: Value) -> Option<Self> {
|
||||
match value {
|
||||
Value::List(values) => {
|
||||
let mut result = Vec::new();
|
||||
for value in values {
|
||||
result.push(GQLInputValue::parse(value)?);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
_ => {
|
||||
return Err(QueryError::ExpectedType {
|
||||
expect: Self::type_name(),
|
||||
actual: value,
|
||||
}
|
||||
.into())
|
||||
Some(result)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_from_json(value: serde_json::Value) -> Result<Self> {
|
||||
fn parse_from_json(value: serde_json::Value) -> Option<Self> {
|
||||
match value {
|
||||
serde_json::Value::Array(values) => {
|
||||
let mut result = Vec::new();
|
||||
for value in values {
|
||||
result.push(GQLInputValue::parse_from_json(value)?);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
_ => {
|
||||
return Err(QueryError::ExpectedJsonType {
|
||||
expect: Self::type_name(),
|
||||
actual: value,
|
||||
}
|
||||
.into())
|
||||
Some(result)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +44,7 @@ impl<T: GQLOutputValue + Send + Sync> GQLOutputValue for Vec<T> {
|
|||
async fn resolve(&self, ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
|
||||
let mut res = Vec::new();
|
||||
for item in self {
|
||||
res.push(item.resolve(ctx).await?);
|
||||
res.push(item.resolve(&ctx).await?);
|
||||
}
|
||||
Ok(res.into())
|
||||
}
|
||||
|
@ -66,10 +52,10 @@ impl<T: GQLOutputValue + Send + Sync> GQLOutputValue for Vec<T> {
|
|||
|
||||
impl<T: GQLType> GQLType for &[T] {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Owned(format!("[{}]!", T::type_name()))
|
||||
Cow::Owned(format!("[{}]", T::type_name()))
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut schema::Registry) -> String {
|
||||
fn create_type_info(registry: &mut registry::Registry) -> String {
|
||||
T::create_type_info(registry)
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +65,7 @@ impl<T: GQLOutputValue + Send + Sync> GQLOutputValue for &[T] {
|
|||
async fn resolve(&self, ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
|
||||
let mut res = Vec::new();
|
||||
for item in self.iter() {
|
||||
res.push(item.resolve(ctx).await?);
|
||||
res.push(item.resolve(&ctx).await?);
|
||||
}
|
||||
Ok(res.into())
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
mod empty_mutation;
|
||||
mod r#enum;
|
||||
mod list;
|
||||
mod non_null;
|
||||
mod optional;
|
||||
mod query_root;
|
||||
|
||||
pub use empty_mutation::GQLEmptyMutation;
|
||||
pub use query_root::QueryRoot;
|
||||
pub use r#enum::{GQLEnum, GQLEnumItem};
|
||||
|
|
|
@ -1,29 +1,32 @@
|
|||
use crate::{schema, ContextSelectionSet, GQLInputValue, GQLOutputValue, GQLType, Result, Value};
|
||||
use crate::{registry, ContextSelectionSet, GQLInputValue, GQLOutputValue, GQLType, Result, Value};
|
||||
use std::borrow::Cow;
|
||||
|
||||
impl<T: GQLType> GQLType for Option<T> {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
let name = T::type_name();
|
||||
Cow::Owned(format!("{}", &name[..name.len() - 1]))
|
||||
T::type_name()
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut schema::Registry) -> String {
|
||||
fn qualified_type_name() -> String {
|
||||
T::type_name().to_string()
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut registry::Registry) -> String {
|
||||
T::create_type_info(registry)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: GQLInputValue> GQLInputValue for Option<T> {
|
||||
fn parse(value: Value) -> Result<Self> {
|
||||
fn parse(value: Value) -> Option<Self> {
|
||||
match value {
|
||||
Value::Null => Ok(None),
|
||||
_ => Ok(Some(GQLInputValue::parse(value)?)),
|
||||
Value::Null => Some(None),
|
||||
_ => Some(GQLInputValue::parse(value)?),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_from_json(value: serde_json::Value) -> Result<Self> {
|
||||
fn parse_from_json(value: serde_json::Value) -> Option<Self> {
|
||||
match value {
|
||||
serde_json::Value::Null => Ok(None),
|
||||
_ => Ok(Some(GQLInputValue::parse_from_json(value)?)),
|
||||
serde_json::Value::Null => Some(None),
|
||||
_ => Some(GQLInputValue::parse_from_json(value)?),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,20 @@
|
|||
use crate::{schema, ContextSelectionSet, GQLOutputValue, GQLType, Result};
|
||||
use crate::model::__Schema;
|
||||
use crate::{registry, ContextSelectionSet, GQLOutputValue, GQLType, Result};
|
||||
use graphql_parser::query::Selection;
|
||||
use std::borrow::Cow;
|
||||
|
||||
struct QueryRoot<T>(T);
|
||||
pub struct QueryRoot<T> {
|
||||
pub inner: T,
|
||||
pub query_type: String,
|
||||
pub mutation_type: String,
|
||||
}
|
||||
|
||||
impl<T: GQLType> GQLType for QueryRoot<T> {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
T::type_name()
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut schema::Registry) -> String {
|
||||
fn create_type_info(registry: &mut registry::Registry) -> String {
|
||||
T::create_type_info(registry)
|
||||
}
|
||||
}
|
||||
|
@ -17,16 +22,28 @@ impl<T: GQLType> GQLType for QueryRoot<T> {
|
|||
#[async_trait::async_trait]
|
||||
impl<T: GQLOutputValue + Send + Sync> GQLOutputValue for QueryRoot<T> {
|
||||
async fn resolve(&self, ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
|
||||
let mut value = self.0.resolve(ctx).await?;
|
||||
if let serde_json::Value::Object(obj) = &mut value {
|
||||
let mut res = self.inner.resolve(ctx).await?;
|
||||
|
||||
if let serde_json::Value::Object(obj) = &mut res {
|
||||
for item in &ctx.item.items {
|
||||
if let Selection::Field(field) = item {
|
||||
if field.name == "__schema" {
|
||||
todo!()
|
||||
let ctx_obj = ctx.with_item(&field.selection_set);
|
||||
obj.insert(
|
||||
"__schema".to_string(),
|
||||
__Schema {
|
||||
registry: &ctx.registry,
|
||||
query_type: &self.query_type,
|
||||
mutation_type: &self.mutation_type,
|
||||
}
|
||||
.resolve(&ctx_obj)
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(value)
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user