* Provide impls for arrays * Remove redundant Default bound * Recheck other places of mem::transmute usage * Fix missing marker impls * Extend GraphQL list validation with optional expected size * Improve input object codegen * Cover arrays with tests * Add CHANGELOG entry * Consider panic safety in FromInputValue implementation for array * Tune up codegen failure tests
This commit is contained in:
parent
8a90f867d4
commit
39d1e43420
25 changed files with 629 additions and 70 deletions
|
@ -0,0 +1,23 @@
|
||||||
|
use juniper::{graphql_interface, GraphQLObject};
|
||||||
|
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
|
#[graphql(impl = CharacterValue)]
|
||||||
|
pub struct ObjA {
|
||||||
|
test: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[graphql_interface]
|
||||||
|
impl Character for ObjA {}
|
||||||
|
|
||||||
|
#[graphql_interface(for = ObjA)]
|
||||||
|
trait Character {
|
||||||
|
fn wrong(
|
||||||
|
&self,
|
||||||
|
#[graphql(default = [true, false, false])]
|
||||||
|
input: [bool; 2],
|
||||||
|
) -> bool {
|
||||||
|
input[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,12 @@
|
||||||
|
error[E0277]: the trait bound `[bool; 2]: From<[bool; 3]>` is not satisfied
|
||||||
|
--> $DIR/argument_wrong_default_array.rs:12:1
|
||||||
|
|
|
||||||
|
12 | #[graphql_interface(for = ObjA)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<[bool; 3]>` is not implemented for `[bool; 2]`
|
||||||
|
|
|
||||||
|
= help: the following implementations were found:
|
||||||
|
<&'a [ascii::ascii_char::AsciiChar] as From<&'a ascii::ascii_str::AsciiStr>>
|
||||||
|
<&'a [u8] as From<&'a ascii::ascii_str::AsciiStr>>
|
||||||
|
<&'a mut [ascii::ascii_char::AsciiChar] as From<&'a mut ascii::ascii_str::AsciiStr>>
|
||||||
|
= note: required because of the requirements on the impl of `Into<[bool; 2]>` for `[bool; 3]`
|
||||||
|
= note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -0,0 +1,11 @@
|
||||||
|
struct Object;
|
||||||
|
|
||||||
|
#[juniper::graphql_object]
|
||||||
|
impl Object {
|
||||||
|
#[graphql(arguments(input(default = [true, false, false])))]
|
||||||
|
fn wrong(input: [bool; 2]) -> bool {
|
||||||
|
input[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,7 @@
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/impl_argument_wrong_default_array.rs:3:1
|
||||||
|
|
|
||||||
|
3 | #[juniper::graphql_object]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an array with a fixed size of 2 elements, found one with 3 elements
|
||||||
|
|
|
||||||
|
= note: this error originates in the attribute macro `juniper::graphql_object` (in Nightly builds, run with -Z macro-backtrace for more info)
|
257
integration_tests/juniper_tests/src/array.rs
Normal file
257
integration_tests/juniper_tests/src/array.rs
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
use juniper::{
|
||||||
|
graphql_object, graphql_value, EmptyMutation, EmptySubscription, GraphQLInputObject, RootNode,
|
||||||
|
Variables,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod as_output_field {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
struct Query;
|
||||||
|
|
||||||
|
#[graphql_object]
|
||||||
|
impl Query {
|
||||||
|
fn roll() -> [bool; 3] {
|
||||||
|
[true, false, true]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn works() {
|
||||||
|
let query = r#"
|
||||||
|
query Query {
|
||||||
|
roll
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||||
|
let (res, errors) = juniper::execute(query, None, &schema, &Variables::new(), &())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(errors.len(), 0);
|
||||||
|
assert_eq!(res, graphql_value!({"roll": [true, false, true]}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod as_input_field {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(GraphQLInputObject)]
|
||||||
|
struct Input {
|
||||||
|
two: [bool; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(GraphQLInputObject)]
|
||||||
|
struct InputSingle {
|
||||||
|
one: [bool; 1],
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Query;
|
||||||
|
|
||||||
|
#[graphql_object]
|
||||||
|
impl Query {
|
||||||
|
fn first(input: InputSingle) -> bool {
|
||||||
|
input.one[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn second(input: Input) -> bool {
|
||||||
|
input.two[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn works() {
|
||||||
|
let query = r#"
|
||||||
|
query Query {
|
||||||
|
second(input: { two: [true, false] })
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||||
|
let (res, errors) = juniper::execute(query, None, &schema, &Variables::new(), &())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(errors.len(), 0);
|
||||||
|
assert_eq!(res, graphql_value!({"second": false}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fails_on_incorrect_count() {
|
||||||
|
let query = r#"
|
||||||
|
query Query {
|
||||||
|
second(input: { two: [true, true, false] })
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||||
|
let res = juniper::execute(query, None, &schema, &Variables::new(), &()).await;
|
||||||
|
|
||||||
|
assert!(res.is_err());
|
||||||
|
assert!(res
|
||||||
|
.unwrap_err()
|
||||||
|
.to_string()
|
||||||
|
.contains(r#"Invalid value for argument "input", expected type "Input!""#));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn cannot_coerce_from_raw_value_if_multiple() {
|
||||||
|
let query = r#"
|
||||||
|
query Query {
|
||||||
|
second(input: { two: true })
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||||
|
let res = juniper::execute(query, None, &schema, &Variables::new(), &()).await;
|
||||||
|
|
||||||
|
assert!(res.is_err());
|
||||||
|
assert!(res
|
||||||
|
.unwrap_err()
|
||||||
|
.to_string()
|
||||||
|
.contains(r#"Invalid value for argument "input", expected type "Input!""#));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn can_coerce_from_raw_value_if_single() {
|
||||||
|
let query = r#"
|
||||||
|
query Query {
|
||||||
|
first(input: { one: true })
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||||
|
let (res, errors) = juniper::execute(query, None, &schema, &Variables::new(), &())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(errors.len(), 0);
|
||||||
|
assert_eq!(res, graphql_value!({"first": true}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod as_input_argument {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
struct Query;
|
||||||
|
|
||||||
|
#[graphql_object]
|
||||||
|
impl Query {
|
||||||
|
fn second(input: [bool; 2]) -> bool {
|
||||||
|
input[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn first(input: [bool; 1]) -> bool {
|
||||||
|
input[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[graphql(arguments(input(default = [true, false, false])))]
|
||||||
|
fn third(input: [bool; 3]) -> bool {
|
||||||
|
input[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn works() {
|
||||||
|
let query = r#"
|
||||||
|
query Query {
|
||||||
|
second(input: [false, true])
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||||
|
let (res, errors) = juniper::execute(query, None, &schema, &Variables::new(), &())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(errors.len(), 0);
|
||||||
|
assert_eq!(res, graphql_value!({"second": true}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fails_on_incorrect_count() {
|
||||||
|
let query = r#"
|
||||||
|
query Query {
|
||||||
|
second(input: [true, true, false])
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||||
|
let res = juniper::execute(query, None, &schema, &Variables::new(), &()).await;
|
||||||
|
|
||||||
|
assert!(res.is_err());
|
||||||
|
assert!(res
|
||||||
|
.unwrap_err()
|
||||||
|
.to_string()
|
||||||
|
.contains(r#"Invalid value for argument "input", expected type "[Boolean!]!""#));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn cannot_coerce_from_raw_value_if_multiple() {
|
||||||
|
let query = r#"
|
||||||
|
query Query {
|
||||||
|
second(input: true)
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||||
|
let res = juniper::execute(query, None, &schema, &Variables::new(), &()).await;
|
||||||
|
|
||||||
|
assert!(res.is_err());
|
||||||
|
assert!(res
|
||||||
|
.unwrap_err()
|
||||||
|
.to_string()
|
||||||
|
.contains(r#"Invalid value for argument "input", expected type "[Boolean!]!""#));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn can_coerce_from_raw_value_if_single() {
|
||||||
|
let query = r#"
|
||||||
|
query Query {
|
||||||
|
first(input: true)
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||||
|
let (res, errors) = juniper::execute(query, None, &schema, &Variables::new(), &())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(errors.len(), 0);
|
||||||
|
assert_eq!(res, graphql_value!({"first": true}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn picks_default() {
|
||||||
|
let query = r#"
|
||||||
|
query Query {
|
||||||
|
third
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||||
|
let (res, errors) = juniper::execute(query, None, &schema, &Variables::new(), &())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(errors.len(), 0);
|
||||||
|
assert_eq!(res, graphql_value!({"third": false}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn picks_specified_over_default() {
|
||||||
|
let query = r#"
|
||||||
|
query Query {
|
||||||
|
third(input: [false, false, true])
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||||
|
let (res, errors) = juniper::execute(query, None, &schema, &Variables::new(), &())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(errors.len(), 0);
|
||||||
|
assert_eq!(res, graphql_value!({"third": true}));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod arc_fields;
|
mod arc_fields;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
mod array;
|
||||||
|
#[cfg(test)]
|
||||||
mod codegen;
|
mod codegen;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod custom_scalar;
|
mod custom_scalar;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
- Allow spreading interface fragments on unions and other interfaces ([#965](https://github.com/graphql-rust/juniper/pull/965), [#798](https://github.com/graphql-rust/juniper/issues/798))
|
- Allow spreading interface fragments on unions and other interfaces ([#965](https://github.com/graphql-rust/juniper/pull/965), [#798](https://github.com/graphql-rust/juniper/issues/798))
|
||||||
- Expose `GraphQLRequest` fields ([#750](https://github.com/graphql-rust/juniper/issues/750))
|
- Expose `GraphQLRequest` fields ([#750](https://github.com/graphql-rust/juniper/issues/750))
|
||||||
|
- Support using Rust array as GraphQL list ([#966](https://github.com/graphql-rust/juniper/pull/966), [#918](https://github.com/graphql-rust/juniper/issues/918))
|
||||||
|
|
||||||
# [[0.15.7] 2021-07-08](https://github.com/graphql-rust/juniper/releases/tag/juniper-v0.15.7)
|
# [[0.15.7] 2021-07-08](https://github.com/graphql-rust/juniper/releases/tag/juniper-v0.15.7)
|
||||||
|
|
||||||
|
|
|
@ -19,13 +19,13 @@ pub enum Type<'a> {
|
||||||
/// A nullable list type, e.g. `[String]`
|
/// A nullable list type, e.g. `[String]`
|
||||||
///
|
///
|
||||||
/// The list itself is what's nullable, the containing type might be non-null.
|
/// The list itself is what's nullable, the containing type might be non-null.
|
||||||
List(Box<Type<'a>>),
|
List(Box<Type<'a>>, Option<usize>),
|
||||||
/// A non-null named type, e.g. `String!`
|
/// A non-null named type, e.g. `String!`
|
||||||
NonNullNamed(Cow<'a, str>),
|
NonNullNamed(Cow<'a, str>),
|
||||||
/// A non-null list type, e.g. `[String]!`.
|
/// A non-null list type, e.g. `[String]!`.
|
||||||
///
|
///
|
||||||
/// The list itself is what's non-null, the containing type might be null.
|
/// The list itself is what's non-null, the containing type might be null.
|
||||||
NonNullList(Box<Type<'a>>),
|
NonNullList(Box<Type<'a>>, Option<usize>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A JSON-like value that can be passed into the query execution, either
|
/// A JSON-like value that can be passed into the query execution, either
|
||||||
|
@ -192,13 +192,13 @@ impl<'a> Type<'a> {
|
||||||
pub fn innermost_name(&self) -> &str {
|
pub fn innermost_name(&self) -> &str {
|
||||||
match *self {
|
match *self {
|
||||||
Type::Named(ref n) | Type::NonNullNamed(ref n) => n,
|
Type::Named(ref n) | Type::NonNullNamed(ref n) => n,
|
||||||
Type::List(ref l) | Type::NonNullList(ref l) => l.innermost_name(),
|
Type::List(ref l, _) | Type::NonNullList(ref l, _) => l.innermost_name(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines if a type only can represent non-null values.
|
/// Determines if a type only can represent non-null values.
|
||||||
pub fn is_non_null(&self) -> bool {
|
pub fn is_non_null(&self) -> bool {
|
||||||
matches!(*self, Type::NonNullNamed(_) | Type::NonNullList(_))
|
matches!(*self, Type::NonNullNamed(_) | Type::NonNullList(..))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,8 +207,8 @@ impl<'a> fmt::Display for Type<'a> {
|
||||||
match *self {
|
match *self {
|
||||||
Type::Named(ref n) => write!(f, "{}", n),
|
Type::Named(ref n) => write!(f, "{}", n),
|
||||||
Type::NonNullNamed(ref n) => write!(f, "{}!", n),
|
Type::NonNullNamed(ref n) => write!(f, "{}!", n),
|
||||||
Type::List(ref t) => write!(f, "[{}]", t),
|
Type::List(ref t, _) => write!(f, "[{}]", t),
|
||||||
Type::NonNullList(ref t) => write!(f, "[{}]!", t),
|
Type::NonNullList(ref t, _) => write!(f, "[{}]!", t),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1235,9 +1235,10 @@ where
|
||||||
pub fn build_list_type<T: GraphQLType<S> + ?Sized>(
|
pub fn build_list_type<T: GraphQLType<S> + ?Sized>(
|
||||||
&mut self,
|
&mut self,
|
||||||
info: &T::TypeInfo,
|
info: &T::TypeInfo,
|
||||||
|
expected_size: Option<usize>,
|
||||||
) -> ListMeta<'r> {
|
) -> ListMeta<'r> {
|
||||||
let of_type = self.get_type::<T>(info);
|
let of_type = self.get_type::<T>(info);
|
||||||
ListMeta::new(of_type)
|
ListMeta::new(of_type, expected_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a nullable meta type
|
/// Create a nullable meta type
|
||||||
|
|
|
@ -520,7 +520,11 @@ pub fn parse_type<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Type<'a>> {
|
||||||
{
|
{
|
||||||
let inner_type = parse_type(parser)?;
|
let inner_type = parse_type(parser)?;
|
||||||
let Spanning { end: end_pos, .. } = parser.expect(&Token::BracketClose)?;
|
let Spanning { end: end_pos, .. } = parser.expect(&Token::BracketClose)?;
|
||||||
Spanning::start_end(&start_pos, &end_pos, Type::List(Box::new(inner_type.item)))
|
Spanning::start_end(
|
||||||
|
&start_pos,
|
||||||
|
&end_pos,
|
||||||
|
Type::List(Box::new(inner_type.item), None),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
parser.expect_name()?.map(|s| Type::Named(Cow::Borrowed(s)))
|
parser.expect_name()?.map(|s| Type::Named(Cow::Borrowed(s)))
|
||||||
};
|
};
|
||||||
|
@ -542,7 +546,7 @@ fn wrap_non_null<'a>(
|
||||||
|
|
||||||
let wrapped = match inner.item {
|
let wrapped = match inner.item {
|
||||||
Type::Named(name) => Type::NonNullNamed(name),
|
Type::Named(name) => Type::NonNullNamed(name),
|
||||||
Type::List(l) => Type::NonNullList(l),
|
Type::List(l, expected_size) => Type::NonNullList(l, expected_size),
|
||||||
t => t,
|
t => t,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,9 @@ pub struct ScalarMeta<'a, S> {
|
||||||
pub struct ListMeta<'a> {
|
pub struct ListMeta<'a> {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub of_type: Type<'a>,
|
pub of_type: Type<'a>,
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub expected_size: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Nullable type metadata
|
/// Nullable type metadata
|
||||||
|
@ -311,12 +314,15 @@ impl<'a, S> MetaType<'a, S> {
|
||||||
| MetaType::InputObject(InputObjectMeta { ref name, .. }) => {
|
| MetaType::InputObject(InputObjectMeta { ref name, .. }) => {
|
||||||
Type::NonNullNamed(name.clone())
|
Type::NonNullNamed(name.clone())
|
||||||
}
|
}
|
||||||
MetaType::List(ListMeta { ref of_type }) => {
|
MetaType::List(ListMeta {
|
||||||
Type::NonNullList(Box::new(of_type.clone()))
|
ref of_type,
|
||||||
}
|
expected_size,
|
||||||
|
}) => Type::NonNullList(Box::new(of_type.clone()), expected_size),
|
||||||
MetaType::Nullable(NullableMeta { ref of_type }) => match *of_type {
|
MetaType::Nullable(NullableMeta { ref of_type }) => match *of_type {
|
||||||
Type::NonNullNamed(ref inner) => Type::Named(inner.clone()),
|
Type::NonNullNamed(ref inner) => Type::Named(inner.clone()),
|
||||||
Type::NonNullList(ref inner) => Type::List(inner.clone()),
|
Type::NonNullList(ref inner, expected_size) => {
|
||||||
|
Type::List(inner.clone(), expected_size)
|
||||||
|
}
|
||||||
ref t => t.clone(),
|
ref t => t.clone(),
|
||||||
},
|
},
|
||||||
MetaType::Placeholder(PlaceholderMeta { ref of_type }) => of_type.clone(),
|
MetaType::Placeholder(PlaceholderMeta { ref of_type }) => of_type.clone(),
|
||||||
|
@ -446,8 +452,11 @@ where
|
||||||
|
|
||||||
impl<'a> ListMeta<'a> {
|
impl<'a> ListMeta<'a> {
|
||||||
/// Build a new list type by wrapping the specified type
|
/// Build a new list type by wrapping the specified type
|
||||||
pub fn new(of_type: Type<'a>) -> ListMeta<'a> {
|
pub fn new(of_type: Type<'a>, expected_size: Option<usize>) -> Self {
|
||||||
ListMeta { of_type }
|
Self {
|
||||||
|
of_type,
|
||||||
|
expected_size,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrap the list in a generic meta type
|
/// Wrap the list in a generic meta type
|
||||||
|
|
|
@ -62,7 +62,7 @@ impl<'a, S> Context for SchemaType<'a, S> {}
|
||||||
pub enum TypeType<'a, S: 'a> {
|
pub enum TypeType<'a, S: 'a> {
|
||||||
Concrete(&'a MetaType<'a, S>),
|
Concrete(&'a MetaType<'a, S>),
|
||||||
NonNull(Box<TypeType<'a, S>>),
|
NonNull(Box<TypeType<'a, S>>),
|
||||||
List(Box<TypeType<'a, S>>),
|
List(Box<TypeType<'a, S>>, Option<usize>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -271,7 +271,7 @@ impl<'a, S> SchemaType<'a, S> {
|
||||||
Type::NonNullNamed(ref name) | Type::Named(ref name) => {
|
Type::NonNullNamed(ref name) | Type::Named(ref name) => {
|
||||||
self.concrete_type_by_name(name)
|
self.concrete_type_by_name(name)
|
||||||
}
|
}
|
||||||
Type::List(ref inner) | Type::NonNullList(ref inner) => self.lookup_type(inner),
|
Type::List(ref inner, _) | Type::NonNullList(ref inner, _) => self.lookup_type(inner),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,11 +339,13 @@ impl<'a, S> SchemaType<'a, S> {
|
||||||
Type::NonNullNamed(ref n) => TypeType::NonNull(Box::new(
|
Type::NonNullNamed(ref n) => TypeType::NonNull(Box::new(
|
||||||
self.type_by_name(n).expect("Type not found in schema"),
|
self.type_by_name(n).expect("Type not found in schema"),
|
||||||
)),
|
)),
|
||||||
Type::NonNullList(ref inner) => {
|
Type::NonNullList(ref inner, expected_size) => TypeType::NonNull(Box::new(
|
||||||
TypeType::NonNull(Box::new(TypeType::List(Box::new(self.make_type(inner)))))
|
TypeType::List(Box::new(self.make_type(inner)), expected_size),
|
||||||
}
|
)),
|
||||||
Type::Named(ref n) => self.type_by_name(n).expect("Type not found in schema"),
|
Type::Named(ref n) => self.type_by_name(n).expect("Type not found in schema"),
|
||||||
Type::List(ref inner) => TypeType::List(Box::new(self.make_type(inner))),
|
Type::List(ref inner, expected_size) => {
|
||||||
|
TypeType::List(Box::new(self.make_type(inner)), expected_size)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,9 +425,9 @@ impl<'a, S> SchemaType<'a, S> {
|
||||||
| (&Named(ref super_name), &NonNullNamed(ref sub_name)) => {
|
| (&Named(ref super_name), &NonNullNamed(ref sub_name)) => {
|
||||||
self.is_named_subtype(sub_name, super_name)
|
self.is_named_subtype(sub_name, super_name)
|
||||||
}
|
}
|
||||||
(&NonNullList(ref super_inner), &NonNullList(ref sub_inner))
|
(&NonNullList(ref super_inner, _), &NonNullList(ref sub_inner, _))
|
||||||
| (&List(ref super_inner), &List(ref sub_inner))
|
| (&List(ref super_inner, _), &List(ref sub_inner, _))
|
||||||
| (&List(ref super_inner), &NonNullList(ref sub_inner)) => {
|
| (&List(ref super_inner, _), &NonNullList(ref sub_inner, _)) => {
|
||||||
self.is_subtype(sub_inner, super_inner)
|
self.is_subtype(sub_inner, super_inner)
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
|
@ -460,14 +462,14 @@ impl<'a, S> TypeType<'a, S> {
|
||||||
pub fn innermost_concrete(&self) -> &'a MetaType<S> {
|
pub fn innermost_concrete(&self) -> &'a MetaType<S> {
|
||||||
match *self {
|
match *self {
|
||||||
TypeType::Concrete(t) => t,
|
TypeType::Concrete(t) => t,
|
||||||
TypeType::NonNull(ref n) | TypeType::List(ref n) => n.innermost_concrete(),
|
TypeType::NonNull(ref n) | TypeType::List(ref n, _) => n.innermost_concrete(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn list_contents(&self) -> Option<&TypeType<'a, S>> {
|
pub fn list_contents(&self) -> Option<&TypeType<'a, S>> {
|
||||||
match *self {
|
match *self {
|
||||||
TypeType::List(ref n) => Some(n),
|
TypeType::List(ref n, _) => Some(n),
|
||||||
TypeType::NonNull(ref n) => n.list_contents(),
|
TypeType::NonNull(ref n) => n.list_contents(),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
@ -550,7 +552,7 @@ impl<'a, S> fmt::Display for TypeType<'a, S> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
TypeType::Concrete(t) => f.write_str(t.name().unwrap()),
|
TypeType::Concrete(t) => f.write_str(t.name().unwrap()),
|
||||||
TypeType::List(ref i) => write!(f, "[{}]", i),
|
TypeType::List(ref i, _) => write!(f, "[{}]", i),
|
||||||
TypeType::NonNull(ref i) => write!(f, "{}!", i),
|
TypeType::NonNull(ref i) => write!(f, "{}!", i),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,7 +201,7 @@ where
|
||||||
fn kind(&self) -> TypeKind {
|
fn kind(&self) -> TypeKind {
|
||||||
match *self {
|
match *self {
|
||||||
TypeType::Concrete(t) => t.type_kind(),
|
TypeType::Concrete(t) => t.type_kind(),
|
||||||
TypeType::List(_) => TypeKind::List,
|
TypeType::List(..) => TypeKind::List,
|
||||||
TypeType::NonNull(_) => TypeKind::NonNull,
|
TypeType::NonNull(_) => TypeKind::NonNull,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,7 +224,7 @@ where
|
||||||
fn of_type(&self) -> Option<&Box<TypeType<S>>> {
|
fn of_type(&self) -> Option<&Box<TypeType<S>>> {
|
||||||
match *self {
|
match *self {
|
||||||
TypeType::Concrete(_) => None,
|
TypeType::Concrete(_) => None,
|
||||||
TypeType::List(ref l) | TypeType::NonNull(ref l) => Some(l),
|
TypeType::List(ref l, _) | TypeType::NonNull(ref l) => Some(l),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -140,13 +140,13 @@ impl GraphQLParserTranslator {
|
||||||
{
|
{
|
||||||
match input {
|
match input {
|
||||||
Type::Named(x) => ExternalType::NamedType(From::from(x.as_ref())),
|
Type::Named(x) => ExternalType::NamedType(From::from(x.as_ref())),
|
||||||
Type::List(x) => {
|
Type::List(x, _) => {
|
||||||
ExternalType::ListType(GraphQLParserTranslator::translate_type(x).into())
|
ExternalType::ListType(GraphQLParserTranslator::translate_type(x).into())
|
||||||
}
|
}
|
||||||
Type::NonNullNamed(x) => {
|
Type::NonNullNamed(x) => {
|
||||||
ExternalType::NonNullType(Box::new(ExternalType::NamedType(From::from(x.as_ref()))))
|
ExternalType::NonNullType(Box::new(ExternalType::NamedType(From::from(x.as_ref()))))
|
||||||
}
|
}
|
||||||
Type::NonNullList(x) => ExternalType::NonNullType(Box::new(ExternalType::ListType(
|
Type::NonNullList(x, _) => ExternalType::NonNullType(Box::new(ExternalType::ListType(
|
||||||
Box::new(GraphQLParserTranslator::translate_type(x)),
|
Box::new(GraphQLParserTranslator::translate_type(x)),
|
||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
use std::{
|
||||||
|
mem::{self, MaybeUninit},
|
||||||
|
ptr,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{FromInputValue, InputValue, Selection, ToInputValue},
|
ast::{FromInputValue, InputValue, Selection, ToInputValue},
|
||||||
executor::{ExecutionResult, Executor, Registry},
|
executor::{ExecutionResult, Executor, Registry},
|
||||||
|
@ -80,7 +85,7 @@ where
|
||||||
T: FromInputValue<S>,
|
T: FromInputValue<S>,
|
||||||
S: ScalarValue,
|
S: ScalarValue,
|
||||||
{
|
{
|
||||||
fn from_input_value(v: &InputValue<S>) -> Option<Option<T>> {
|
fn from_input_value(v: &InputValue<S>) -> Option<Self> {
|
||||||
match v {
|
match v {
|
||||||
&InputValue::Null => Some(None),
|
&InputValue::Null => Some(None),
|
||||||
v => v.convert().map(Some),
|
v => v.convert().map(Some),
|
||||||
|
@ -114,7 +119,7 @@ where
|
||||||
where
|
where
|
||||||
S: 'r,
|
S: 'r,
|
||||||
{
|
{
|
||||||
registry.build_list_type::<T>(info).into_meta()
|
registry.build_list_type::<T>(info, None).into_meta()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,17 +168,11 @@ where
|
||||||
T: FromInputValue<S>,
|
T: FromInputValue<S>,
|
||||||
S: ScalarValue,
|
S: ScalarValue,
|
||||||
{
|
{
|
||||||
fn from_input_value(v: &InputValue<S>) -> Option<Vec<T>>
|
fn from_input_value(v: &InputValue<S>) -> Option<Self> {
|
||||||
where {
|
|
||||||
match *v {
|
match *v {
|
||||||
InputValue::List(ref ls) => {
|
InputValue::List(ref ls) => {
|
||||||
let v: Vec<_> = ls.iter().filter_map(|i| i.item.convert()).collect();
|
let v: Vec<_> = ls.iter().filter_map(|i| i.item.convert()).collect();
|
||||||
|
(v.len() == ls.len()).then(|| v)
|
||||||
if v.len() == ls.len() {
|
|
||||||
Some(v)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ref other => other.convert().map(|e| vec![e]),
|
ref other => other.convert().map(|e| vec![e]),
|
||||||
}
|
}
|
||||||
|
@ -203,7 +202,7 @@ where
|
||||||
where
|
where
|
||||||
S: 'r,
|
S: 'r,
|
||||||
{
|
{
|
||||||
registry.build_list_type::<T>(info).into_meta()
|
registry.build_list_type::<T>(info, None).into_meta()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,6 +256,179 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<S, T, const N: usize> GraphQLType<S> for [T; N]
|
||||||
|
where
|
||||||
|
S: ScalarValue,
|
||||||
|
T: GraphQLType<S>,
|
||||||
|
{
|
||||||
|
fn name(_: &Self::TypeInfo) -> Option<&'static str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn meta<'r>(info: &Self::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S>
|
||||||
|
where
|
||||||
|
S: 'r,
|
||||||
|
{
|
||||||
|
registry.build_list_type::<T>(info, Some(N)).into_meta()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, T, const N: usize> GraphQLValue<S> for [T; N]
|
||||||
|
where
|
||||||
|
S: ScalarValue,
|
||||||
|
T: GraphQLValue<S>,
|
||||||
|
{
|
||||||
|
type Context = T::Context;
|
||||||
|
type TypeInfo = T::TypeInfo;
|
||||||
|
|
||||||
|
fn type_name(&self, _: &Self::TypeInfo) -> Option<&'static str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve(
|
||||||
|
&self,
|
||||||
|
info: &Self::TypeInfo,
|
||||||
|
_: Option<&[Selection<S>]>,
|
||||||
|
executor: &Executor<Self::Context, S>,
|
||||||
|
) -> ExecutionResult<S> {
|
||||||
|
resolve_into_list(executor, info, self.iter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, T, const N: usize> GraphQLValueAsync<S> for [T; N]
|
||||||
|
where
|
||||||
|
T: GraphQLValueAsync<S>,
|
||||||
|
T::TypeInfo: Sync,
|
||||||
|
T::Context: Sync,
|
||||||
|
S: ScalarValue + Send + Sync,
|
||||||
|
{
|
||||||
|
fn resolve_async<'a>(
|
||||||
|
&'a self,
|
||||||
|
info: &'a Self::TypeInfo,
|
||||||
|
_: Option<&'a [Selection<S>]>,
|
||||||
|
executor: &'a Executor<Self::Context, S>,
|
||||||
|
) -> crate::BoxFuture<'a, ExecutionResult<S>> {
|
||||||
|
let f = resolve_into_list_async(executor, info, self.iter());
|
||||||
|
Box::pin(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, S, const N: usize> FromInputValue<S> for [T; N]
|
||||||
|
where
|
||||||
|
T: FromInputValue<S>,
|
||||||
|
S: ScalarValue,
|
||||||
|
{
|
||||||
|
fn from_input_value(v: &InputValue<S>) -> Option<Self> {
|
||||||
|
struct PartiallyInitializedArray<T, const N: usize> {
|
||||||
|
arr: [MaybeUninit<T>; N],
|
||||||
|
init_len: usize,
|
||||||
|
no_drop: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, const N: usize> Drop for PartiallyInitializedArray<T, N> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.no_drop {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Dropping a `MaybeUninit` does nothing, thus we need to drop
|
||||||
|
// the initialized elements manually, otherwise we may introduce
|
||||||
|
// a memory/resource leak if `T: Drop`.
|
||||||
|
for elem in &mut self.arr[0..self.init_len] {
|
||||||
|
// SAFETY: This is safe, because `self.init_len` represents
|
||||||
|
// exactly the number of initialized elements.
|
||||||
|
unsafe {
|
||||||
|
ptr::drop_in_place(elem.as_mut_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match *v {
|
||||||
|
InputValue::List(ref ls) => {
|
||||||
|
// SAFETY: The reason we're using a wrapper struct implementing
|
||||||
|
// `Drop` here is to be panic safe:
|
||||||
|
// `T: FromInputValue<S>` implementation is not
|
||||||
|
// controlled by us, so calling `i.item.convert()` below
|
||||||
|
// may cause a panic when our array is initialized only
|
||||||
|
// partially. In such situation we need to drop already
|
||||||
|
// initialized values to avoid possible memory/resource
|
||||||
|
// leaks if `T: Drop`.
|
||||||
|
let mut out = PartiallyInitializedArray::<T, N> {
|
||||||
|
// SAFETY: The `.assume_init()` here is safe, because the
|
||||||
|
// type we are claiming to have initialized here is
|
||||||
|
// a bunch of `MaybeUninit`s, which do not require
|
||||||
|
// any initialization.
|
||||||
|
arr: unsafe { MaybeUninit::uninit().assume_init() },
|
||||||
|
init_len: 0,
|
||||||
|
no_drop: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut items = ls.iter().filter_map(|i| i.item.convert());
|
||||||
|
for elem in &mut out.arr[..] {
|
||||||
|
if let Some(i) = items.next() {
|
||||||
|
*elem = MaybeUninit::new(i);
|
||||||
|
out.init_len += 1;
|
||||||
|
} else {
|
||||||
|
// There is not enough `items` to fill the array.
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if items.next().is_some() {
|
||||||
|
// There is too much `items` to fit into the array.
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not drop collected `items`, because we're going to return
|
||||||
|
// them.
|
||||||
|
out.no_drop = true;
|
||||||
|
|
||||||
|
// TODO: Use `mem::transmute` instead of `mem::transmute_copy`
|
||||||
|
// below, once it's allowed for const generics:
|
||||||
|
// https://github.com/rust-lang/rust/issues/61956
|
||||||
|
// SAFETY: `mem::transmute_copy` is safe here, because we have
|
||||||
|
// exactly `N` initialized `items`.
|
||||||
|
// Also, despite `mem::transmute_copy` copies the value,
|
||||||
|
// we won't have a double-free when `T: Drop` here,
|
||||||
|
// because original array elements are `MaybeUninit`, so
|
||||||
|
// do nothing on `Drop`.
|
||||||
|
Some(unsafe { mem::transmute_copy::<_, Self>(&out.arr) })
|
||||||
|
}
|
||||||
|
ref other => {
|
||||||
|
other.convert().and_then(|e: T| {
|
||||||
|
// TODO: Use `mem::transmute` instead of
|
||||||
|
// `mem::transmute_copy` below, once it's allowed for
|
||||||
|
// const generics:
|
||||||
|
// https://github.com/rust-lang/rust/issues/61956
|
||||||
|
if N == 1 {
|
||||||
|
// SAFETY: `mem::transmute_copy` is safe here, because
|
||||||
|
// we check `N` to be `1`.
|
||||||
|
// Also, despite `mem::transmute_copy` copies
|
||||||
|
// the value, we won't have a double-free when
|
||||||
|
// `T: Drop` here, because original `e: T` value
|
||||||
|
// is wrapped into `mem::ManuallyDrop`, so does
|
||||||
|
// nothing on `Drop`.
|
||||||
|
Some(unsafe {
|
||||||
|
mem::transmute_copy::<_, Self>(&[mem::ManuallyDrop::new(e)])
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, S, const N: usize> ToInputValue<S> for [T; N]
|
||||||
|
where
|
||||||
|
T: ToInputValue<S>,
|
||||||
|
S: ScalarValue,
|
||||||
|
{
|
||||||
|
fn to_input_value(&self) -> InputValue<S> {
|
||||||
|
InputValue::list(self.iter().map(T::to_input_value).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_into_list<'t, S, T, I>(
|
fn resolve_into_list<'t, S, T, I>(
|
||||||
executor: &Executor<T::Context, S>,
|
executor: &Executor<T::Context, S>,
|
||||||
info: &T::TypeInfo,
|
info: &T::TypeInfo,
|
||||||
|
|
|
@ -253,6 +253,17 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<S, T, const N: usize> IsOutputType<S> for [T; N]
|
||||||
|
where
|
||||||
|
T: IsOutputType<S>,
|
||||||
|
S: ScalarValue,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn mark() {
|
||||||
|
T::mark()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, S> IsOutputType<S> for str where S: ScalarValue {}
|
impl<'a, S> IsOutputType<S> for str where S: ScalarValue {}
|
||||||
|
|
||||||
/// Marker trait for types which can be used as input types.
|
/// Marker trait for types which can be used as input types.
|
||||||
|
@ -335,4 +346,15 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<S, T, const N: usize> IsInputType<S> for [T; N]
|
||||||
|
where
|
||||||
|
T: IsInputType<S>,
|
||||||
|
S: ScalarValue,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn mark() {
|
||||||
|
T::mark()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, S> IsInputType<S> for str where S: ScalarValue {}
|
impl<'a, S> IsInputType<S> for str where S: ScalarValue {}
|
||||||
|
|
|
@ -24,11 +24,25 @@ where
|
||||||
is_valid_literal_value(schema, inner, arg_value)
|
is_valid_literal_value(schema, inner, arg_value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TypeType::List(ref inner) => match *arg_value {
|
TypeType::List(ref inner, expected_size) => match *arg_value {
|
||||||
InputValue::List(ref items) => items
|
InputValue::List(ref items) => {
|
||||||
.iter()
|
if let Some(expected) = expected_size {
|
||||||
.all(|i| is_valid_literal_value(schema, inner, &i.item)),
|
if items.len() != expected {
|
||||||
ref v => is_valid_literal_value(schema, inner, v),
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items
|
||||||
|
.iter()
|
||||||
|
.all(|i| is_valid_literal_value(schema, inner, &i.item))
|
||||||
|
}
|
||||||
|
ref v => {
|
||||||
|
if let Some(expected) = expected_size {
|
||||||
|
if expected != 1 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is_valid_literal_value(schema, inner, v)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
TypeType::Concrete(t) => {
|
TypeType::Concrete(t) => {
|
||||||
// Even though InputValue::String can be parsed into an enum, they
|
// Even though InputValue::String can be parsed into an enum, they
|
||||||
|
|
|
@ -108,13 +108,27 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeType::List(ref inner) => {
|
TypeType::List(ref inner, expected_size) => {
|
||||||
if value.is_null() {
|
if value.is_null() {
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
match value.to_list_value() {
|
match value.to_list_value() {
|
||||||
Some(l) => {
|
Some(l) => {
|
||||||
|
if let Some(expected) = expected_size {
|
||||||
|
if l.len() != expected {
|
||||||
|
errors.push(unification_error(
|
||||||
|
var_name,
|
||||||
|
var_pos,
|
||||||
|
&path,
|
||||||
|
&format!(
|
||||||
|
"Expected list of {} elements, found {} elements",
|
||||||
|
expected,
|
||||||
|
l.len()
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
for (i, v) in l.iter().enumerate() {
|
for (i, v) in l.iter().enumerate() {
|
||||||
errors.append(&mut unify_value(
|
errors.append(&mut unify_value(
|
||||||
var_name,
|
var_name,
|
||||||
|
|
|
@ -529,8 +529,14 @@ impl<'a, S: Debug> OverlappingFieldsCanBeMerged<'a, S> {
|
||||||
|
|
||||||
fn is_type_conflict(&self, ctx: &ValidatorContext<'a, S>, t1: &Type, t2: &Type) -> bool {
|
fn is_type_conflict(&self, ctx: &ValidatorContext<'a, S>, t1: &Type, t2: &Type) -> bool {
|
||||||
match (t1, t2) {
|
match (t1, t2) {
|
||||||
(&Type::List(ref inner1), &Type::List(ref inner2))
|
(&Type::List(ref inner1, expected_size1), &Type::List(ref inner2, expected_size2))
|
||||||
| (&Type::NonNullList(ref inner1), &Type::NonNullList(ref inner2)) => {
|
| (
|
||||||
|
&Type::NonNullList(ref inner1, expected_size1),
|
||||||
|
&Type::NonNullList(ref inner2, expected_size2),
|
||||||
|
) => {
|
||||||
|
if expected_size1 != expected_size2 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
self.is_type_conflict(ctx, inner1, inner2)
|
self.is_type_conflict(ctx, inner1, inner2)
|
||||||
}
|
}
|
||||||
(&Type::NonNullNamed(ref n1), &Type::NonNullNamed(ref n2))
|
(&Type::NonNullNamed(ref n1), &Type::NonNullNamed(ref n2))
|
||||||
|
|
|
@ -54,7 +54,9 @@ impl<'a, S: Debug> VariableInAllowedPosition<'a, S> {
|
||||||
.find(|&&&(ref n, _)| n.item == var_name.item)
|
.find(|&&&(ref n, _)| n.item == var_name.item)
|
||||||
{
|
{
|
||||||
let expected_type = match (&var_def.default_value, &var_def.var_type.item) {
|
let expected_type = match (&var_def.default_value, &var_def.var_type.item) {
|
||||||
(&Some(_), &Type::List(ref inner)) => Type::NonNullList(inner.clone()),
|
(&Some(_), &Type::List(ref inner, expected_size)) => {
|
||||||
|
Type::NonNullList(inner.clone(), expected_size)
|
||||||
|
}
|
||||||
(&Some(_), &Type::Named(ref inner)) => {
|
(&Some(_), &Type::Named(ref inner)) => {
|
||||||
Type::NonNullNamed(Cow::Borrowed(inner))
|
Type::NonNullNamed(Cow::Borrowed(inner))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{FromInputValue, InputValue},
|
ast::{FromInputValue, InputValue},
|
||||||
executor::Registry,
|
executor::Registry,
|
||||||
|
@ -862,12 +864,10 @@ where
|
||||||
|
|
||||||
let doc =
|
let doc =
|
||||||
parse_document_source(q, &root.schema).expect(&format!("Parse error on input {:#?}", q));
|
parse_document_source(q, &root.schema).expect(&format!("Parse error on input {:#?}", q));
|
||||||
let mut ctx = ValidatorContext::new(unsafe { ::std::mem::transmute(&root.schema) }, &doc);
|
let mut ctx = ValidatorContext::new(unsafe { mem::transmute(&root.schema) }, &doc);
|
||||||
|
|
||||||
let mut mv = MultiVisitorNil.with(factory());
|
let mut mv = MultiVisitorNil.with(factory());
|
||||||
visit(&mut mv, &mut ctx, unsafe {
|
visit(&mut mv, &mut ctx, unsafe { mem::transmute(doc.as_slice()) });
|
||||||
::std::mem::transmute(doc.as_slice())
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.into_errors()
|
ctx.into_errors()
|
||||||
}
|
}
|
||||||
|
|
|
@ -336,7 +336,7 @@ fn visit_input_value<'a, S, V>(
|
||||||
}
|
}
|
||||||
InputValue::List(ref ls) => {
|
InputValue::List(ref ls) => {
|
||||||
let inner_type = ctx.current_input_type_literal().and_then(|t| match *t {
|
let inner_type = ctx.current_input_type_literal().and_then(|t| match *t {
|
||||||
Type::List(ref inner) | Type::NonNullList(ref inner) => {
|
Type::List(ref inner, _) | Type::NonNullList(ref inner, _) => {
|
||||||
Some(inner.as_ref().clone())
|
Some(inner.as_ref().clone())
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -164,8 +164,12 @@ impl<S: ScalarValue> Value<S> {
|
||||||
/// Maps the [`ScalarValue`] type of this [`Value`] into the specified one.
|
/// Maps the [`ScalarValue`] type of this [`Value`] into the specified one.
|
||||||
pub fn map_scalar_value<Into: ScalarValue>(self) -> Value<Into> {
|
pub fn map_scalar_value<Into: ScalarValue>(self) -> Value<Into> {
|
||||||
if TypeId::of::<Into>() == TypeId::of::<S>() {
|
if TypeId::of::<Into>() == TypeId::of::<S>() {
|
||||||
// This is totally safe, because we're transmuting the value into itself,
|
// SAFETY: This is safe, because we're transmuting the value into
|
||||||
// so no invariants may change and we're just satisfying the type checker.
|
// itself, so no invariants may change and we're just
|
||||||
|
// satisfying the type checker.
|
||||||
|
// As `mem::transmute_copy` creates a copy of data, we need
|
||||||
|
// `mem::ManuallyDrop` here to omit double-free when
|
||||||
|
// `S: Drop`.
|
||||||
let val = mem::ManuallyDrop::new(self);
|
let val = mem::ManuallyDrop::new(self);
|
||||||
unsafe { mem::transmute_copy(&*val) }
|
unsafe { mem::transmute_copy(&*val) }
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1731,7 +1731,7 @@ impl GraphQLTypeDefiniton {
|
||||||
// TODO: investigate the unwraps here, they seem dangerous!
|
// TODO: investigate the unwraps here, they seem dangerous!
|
||||||
match obj.get(#field_name) {
|
match obj.get(#field_name) {
|
||||||
#from_input_default
|
#from_input_default
|
||||||
Some(ref v) => ::juniper::FromInputValue::from_input_value(v).unwrap(),
|
Some(ref v) => ::juniper::FromInputValue::from_input_value(v)?,
|
||||||
None => ::juniper::FromInputValue::<#scalar>::from_implicit_null(),
|
None => ::juniper::FromInputValue::<#scalar>::from_implicit_null(),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1849,15 +1849,10 @@ impl GraphQLTypeDefiniton {
|
||||||
{
|
{
|
||||||
fn from_input_value(value: &::juniper::InputValue<#scalar>) -> Option<Self>
|
fn from_input_value(value: &::juniper::InputValue<#scalar>) -> Option<Self>
|
||||||
{
|
{
|
||||||
if let Some(obj) = value.to_object_value() {
|
let obj = value.to_object_value()?;
|
||||||
let item = #ty {
|
Some(#ty {
|
||||||
#( #from_inputs )*
|
#( #from_inputs )*
|
||||||
};
|
})
|
||||||
Some(item)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
use std::{any::Any, fmt, marker::PhantomPinned, mem};
|
||||||
|
|
||||||
use juniper::{ExecutionError, GraphQLError, ScalarValue, Value};
|
use juniper::{ExecutionError, GraphQLError, ScalarValue, Value};
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
use std::{any::Any, fmt, marker::PhantomPinned};
|
|
||||||
|
|
||||||
/// The payload for errors that are not associated with a GraphQL operation.
|
/// The payload for errors that are not associated with a GraphQL operation.
|
||||||
#[derive(Debug, Serialize, PartialEq)]
|
#[derive(Debug, Serialize, PartialEq)]
|
||||||
|
@ -45,7 +46,7 @@ impl ErrorPayload {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
_execution_params: Some(execution_params),
|
_execution_params: Some(execution_params),
|
||||||
error: std::mem::transmute(error),
|
error: mem::transmute(error),
|
||||||
_marker: PhantomPinned,
|
_marker: PhantomPinned,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue