Refactor FromInputValue
to return Result
instead of Option
(#987)
- propagate `FromInputValue` conversion errors during validation - replace panics with errors during resolving Co-authored-by: ilslv <ilya.solovyiov@gmail.com>
This commit is contained in:
parent
e264cf509d
commit
46be97ada4
96 changed files with 1305 additions and 837 deletions
|
@ -113,7 +113,7 @@ The example below is used just for illustration.
|
|||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
#
|
||||
use juniper::{Value, ParseScalarResult, ParseScalarValue};
|
||||
use date::Date;
|
||||
|
||||
|
@ -128,10 +128,11 @@ where
|
|||
}
|
||||
|
||||
// Define how to parse a primitive type into your custom scalar.
|
||||
fn from_input_value(v: &InputValue) -> Option<Date> {
|
||||
v.as_scalar_value()
|
||||
.and_then(|v| v.as_str())
|
||||
.and_then(|s| s.parse().ok())
|
||||
// NOTE: The error type should implement `IntoFieldError<S>`.
|
||||
fn from_input_value(v: &InputValue) -> Result<Date, String> {
|
||||
v.as_string_value()
|
||||
.ok_or_else(|| format!("Expected `String`, found: {}", v))
|
||||
.and_then(|s| s.parse().map_err(|e| format!("Failed to parse `Date`: {}", e)))
|
||||
}
|
||||
|
||||
// Define how to parse a string value.
|
||||
|
@ -139,6 +140,6 @@ where
|
|||
<String as ParseScalarValue<S>>::from_str(value)
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# fn main() {}
|
||||
```
|
||||
|
|
|
@ -70,11 +70,18 @@ impl Query {
|
|||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn async_simple() {
|
||||
let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||
let doc = r#"
|
||||
query {
|
||||
fn main() {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use juniper::{graphql_value, EmptyMutation, EmptySubscription, GraphQLError, RootNode, Value};
|
||||
|
||||
use super::Query;
|
||||
|
||||
#[tokio::test]
|
||||
async fn async_simple() {
|
||||
let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||
let doc = r#"query {
|
||||
fieldSync
|
||||
fieldAsyncPlain
|
||||
delayed
|
||||
|
@ -83,39 +90,37 @@ async fn async_simple() {
|
|||
name
|
||||
delayed
|
||||
}
|
||||
}
|
||||
"#;
|
||||
}"#;
|
||||
|
||||
let vars = Default::default();
|
||||
let (res, errs) = juniper::execute(doc, None, &schema, &vars, &())
|
||||
.await
|
||||
.unwrap();
|
||||
let vars = Default::default();
|
||||
let (res, errs) = juniper::execute(doc, None, &schema, &vars, &())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(errs.is_empty());
|
||||
assert!(errs.is_empty());
|
||||
|
||||
let obj = res.into_object().unwrap();
|
||||
let value = Value::Object(obj);
|
||||
let obj = res.into_object().unwrap();
|
||||
let value = Value::Object(obj);
|
||||
|
||||
assert_eq!(
|
||||
value,
|
||||
graphql_value!({
|
||||
"delayed": true,
|
||||
"fieldAsyncPlain": "field_async_plain",
|
||||
"fieldSync": "field_sync",
|
||||
"user": {
|
||||
assert_eq!(
|
||||
value,
|
||||
graphql_value!({
|
||||
"delayed": true,
|
||||
"kind": "USER",
|
||||
"name": "user1",
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
"fieldAsyncPlain": "field_async_plain",
|
||||
"fieldSync": "field_sync",
|
||||
"user": {
|
||||
"delayed": true,
|
||||
"kind": "USER",
|
||||
"name": "user1",
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn async_field_validation_error() {
|
||||
let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||
let doc = r#"
|
||||
query {
|
||||
#[tokio::test]
|
||||
async fn async_field_validation_error() {
|
||||
let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||
let doc = r#"query {
|
||||
nonExistentField
|
||||
fieldSync
|
||||
fieldAsyncPlain
|
||||
|
@ -125,40 +130,38 @@ async fn async_field_validation_error() {
|
|||
name
|
||||
delayed
|
||||
}
|
||||
}
|
||||
"#;
|
||||
}"#;
|
||||
|
||||
let vars = Default::default();
|
||||
let result = juniper::execute(doc, None, &schema, &vars, &()).await;
|
||||
assert!(result.is_err());
|
||||
let vars = Default::default();
|
||||
let result = juniper::execute(doc, None, &schema, &vars, &()).await;
|
||||
assert!(result.is_err());
|
||||
|
||||
let error = result.err().unwrap();
|
||||
let is_validation_error = match error {
|
||||
GraphQLError::ValidationError(_) => true,
|
||||
_ => false,
|
||||
};
|
||||
assert!(is_validation_error);
|
||||
let error = result.err().unwrap();
|
||||
let is_validation_error = match error {
|
||||
GraphQLError::ValidationError(_) => true,
|
||||
_ => false,
|
||||
};
|
||||
assert!(is_validation_error);
|
||||
}
|
||||
|
||||
// FIXME: test seems broken by design, re-enable later
|
||||
// #[tokio::test]
|
||||
// async fn resolve_into_stream_validation_error() {
|
||||
// let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||
// let doc = r#"
|
||||
// subscription {
|
||||
// nonExistent
|
||||
// }
|
||||
// "#;
|
||||
// let vars = Default::default();
|
||||
// let result = juniper::resolve_into_stream(doc, None, &schema, &vars, &()).await;
|
||||
// assert!(result.is_err());
|
||||
|
||||
// let error = result.err().unwrap();
|
||||
// let is_validation_error = match error {
|
||||
// GraphQLError::ValidationError(_) => true,
|
||||
// _ => false,
|
||||
// };
|
||||
// assert!(is_validation_error);
|
||||
// }
|
||||
}
|
||||
|
||||
// FIXME: test seems broken by design, re-enable later
|
||||
// #[tokio::test]
|
||||
// async fn resolve_into_stream_validation_error() {
|
||||
// let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||
// let doc = r#"
|
||||
// subscription {
|
||||
// nonExistent
|
||||
// }
|
||||
// "#;
|
||||
// let vars = Default::default();
|
||||
// let result = juniper::resolve_into_stream(doc, None, &schema, &vars, &()).await;
|
||||
// assert!(result.is_err());
|
||||
|
||||
// let error = result.err().unwrap();
|
||||
// let is_validation_error = match error {
|
||||
// GraphQLError::ValidationError(_) => true,
|
||||
// _ => false,
|
||||
// };
|
||||
// assert!(is_validation_error);
|
||||
// }
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: GraphQL enum expects at least one field
|
||||
--> $DIR/derive_no_fields.rs:2:1
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Enums
|
||||
|
||||
--> fail/enum/derive_no_fields.rs:2:1
|
||||
|
|
||||
2 | pub enum Test {}
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Enums
|
||||
| ^^^
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
error[E0277]: the trait bound `ObjectA: IsInputType<__S>` is not satisfied
|
||||
--> $DIR/derive_incompatible_object.rs:6:10
|
||||
|
|
||||
6 | #[derive(juniper::GraphQLInputObject)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsInputType<__S>` is not implemented for `ObjectA`
|
||||
|
|
||||
= note: required by `juniper::marker::IsInputType::mark`
|
||||
= note: this error originates in the derive macro `juniper::GraphQLInputObject` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
--> fail/input-object/derive_incompatible_object.rs:6:10
|
||||
|
|
||||
6 | #[derive(juniper::GraphQLInputObject)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsInputType<__S>` is not implemented for `ObjectA`
|
||||
|
|
||||
note: required by `juniper::marker::IsInputType::mark`
|
||||
--> $WORKSPACE/juniper/src/types/marker.rs
|
||||
|
|
||||
| fn mark() {}
|
||||
| ^^^^^^^^^
|
||||
= note: this error originates in the derive macro `juniper::GraphQLInputObject` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0277]: the trait bound `ObjectA: FromInputValue<__S>` is not satisfied
|
||||
--> $DIR/derive_incompatible_object.rs:6:10
|
||||
--> fail/input-object/derive_incompatible_object.rs:6:10
|
||||
|
|
||||
6 | #[derive(juniper::GraphQLInputObject)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue<__S>` is not implemented for `ObjectA`
|
||||
|
@ -16,16 +20,20 @@ error[E0277]: the trait bound `ObjectA: FromInputValue<__S>` is not satisfied
|
|||
= note: this error originates in the derive macro `juniper::GraphQLInputObject` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0277]: the trait bound `ObjectA: FromInputValue<__S>` is not satisfied
|
||||
--> $DIR/derive_incompatible_object.rs:6:10
|
||||
|
|
||||
6 | #[derive(juniper::GraphQLInputObject)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue<__S>` is not implemented for `ObjectA`
|
||||
|
|
||||
= note: required by `from_input_value`
|
||||
= note: this error originates in the derive macro `juniper::GraphQLInputObject` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
--> fail/input-object/derive_incompatible_object.rs:6:10
|
||||
|
|
||||
6 | #[derive(juniper::GraphQLInputObject)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue<__S>` is not implemented for `ObjectA`
|
||||
|
|
||||
note: required by `from_input_value`
|
||||
--> $WORKSPACE/juniper/src/ast.rs
|
||||
|
|
||||
| fn from_input_value(v: &InputValue<S>) -> Result<Self, Self::Error>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: this error originates in the derive macro `juniper::GraphQLInputObject` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0599]: no method named `to_input_value` found for struct `ObjectA` in the current scope
|
||||
--> $DIR/derive_incompatible_object.rs:6:10
|
||||
--> fail/input-object/derive_incompatible_object.rs:6:10
|
||||
|
|
||||
2 | struct ObjectA {
|
||||
| -------------- method `to_input_value` not found for this
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: GraphQL input object expects at least one field
|
||||
--> $DIR/derive_no_fields.rs:2:1
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Input-Objects
|
||||
|
||||
--> fail/input-object/derive_no_fields.rs:2:1
|
||||
|
|
||||
2 | struct Object {}
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Input-Objects
|
||||
| ^^^^^^
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
||||
--> $DIR/derive_no_underscore.rs:3:15
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
||||
--> fail/input-object/derive_no_underscore.rs:3:15
|
||||
|
|
||||
3 | #[graphql(name = "__test")]
|
||||
| ^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
error: GraphQL input object does not allow fields with the same name
|
||||
--> $DIR/derive_unique_name.rs:4:5
|
||||
|
|
||||
4 | / #[graphql(name = "test")]
|
||||
5 | | test2: String,
|
||||
| |_________________^
|
||||
|
|
||||
|
||||
= help: There is at least one other field with the same name `test`, possibly renamed via the #[graphql] attribute
|
||||
= note: https://spec.graphql.org/June2018/#sec-Input-Objects
|
||||
|
||||
--> fail/input-object/derive_unique_name.rs:4:5
|
||||
|
|
||||
4 | #[graphql(name = "test")]
|
||||
| ^
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
||||
--> $DIR/argument_double_underscored.rs:14:18
|
||||
|
|
||||
14 | fn id(&self, __num: i32) -> &str {
|
||||
| ^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
||||
error[E0412]: cannot find type `CharacterValue` in this scope
|
||||
--> $DIR/argument_double_underscored.rs:4:18
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
error[E0277]: the trait bound `ObjA: IsInputType<__S>` is not satisfied
|
||||
--> $DIR/argument_non_input_type.rs:16:1
|
||||
|
|
||||
16 | #[graphql_interface(for = ObjA)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsInputType<__S>` is not implemented for `ObjA`
|
||||
|
|
||||
= note: required by `juniper::marker::IsInputType::mark`
|
||||
= note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
--> fail/interface/argument_non_input_type.rs:16:1
|
||||
|
|
||||
16 | #[graphql_interface(for = ObjA)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsInputType<__S>` is not implemented for `ObjA`
|
||||
|
|
||||
note: required by `juniper::marker::IsInputType::mark`
|
||||
--> $WORKSPACE/juniper/src/types/marker.rs
|
||||
|
|
||||
| fn mark() {}
|
||||
| ^^^^^^^^^
|
||||
= note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0277]: the trait bound `ObjA: FromInputValue<__S>` is not satisfied
|
||||
--> $DIR/argument_non_input_type.rs:16:1
|
||||
--> fail/interface/argument_non_input_type.rs:16:1
|
||||
|
|
||||
16 | #[graphql_interface(for = ObjA)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue<__S>` is not implemented for `ObjA`
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
error: GraphQL interface trait method `as_obja` conflicts with the external downcast function `downcast_obja` declared on the trait to downcast into the implementer type `ObjA`
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Interfaces
|
||||
= note: use `#[graphql(ignore)]` attribute argument to ignore this trait method for interface implementers downcasting
|
||||
|
||||
--> $DIR/downcast_method_conflicts_with_external_downcast_fn.rs:26:5
|
||||
|
|
||||
26 | fn as_obja(&self) -> Option<&ObjA>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Interfaces
|
||||
= note: use `#[graphql(ignore)]` attribute argument to ignore this trait method for interface implementers downcasting
|
||||
| ^^
|
||||
|
||||
error[E0412]: cannot find type `CharacterValue` in this scope
|
||||
--> $DIR/downcast_method_conflicts_with_external_downcast_fn.rs:4:18
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
error: GraphQL interface expects trait method to accept `&self` only and, optionally, `&Context`
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Interfaces
|
||||
|
||||
--> $DIR/downcast_method_wrong_input_args.rs:10:10
|
||||
|
|
||||
10 | fn a(&self, ctx: &(), rand: u8) -> Option<&Human> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Interfaces
|
||||
| ^
|
||||
|
||||
error[E0412]: cannot find type `CharacterValue` in this scope
|
||||
--> $DIR/downcast_method_wrong_input_args.rs:16:18
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
error: GraphQL interface expects trait method return type to be `Option<&ImplementerType>` only
|
||||
--> $DIR/downcast_method_wrong_return_type.rs:10:40
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Interfaces
|
||||
|
||||
--> fail/interface/downcast_method_wrong_return_type.rs:10:40
|
||||
|
|
||||
10 | fn a(&self, ctx: &(), rand: u8) -> &Human {
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Interfaces
|
||||
| ^
|
||||
|
||||
error[E0412]: cannot find type `CharacterValue` in this scope
|
||||
--> $DIR/downcast_method_wrong_return_type.rs:16:18
|
||||
--> fail/interface/downcast_method_wrong_return_type.rs:16:18
|
||||
|
|
||||
16 | #[graphql(impl = CharacterValue)]
|
||||
| ^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0405]: cannot find trait `Character` in this scope
|
||||
--> $DIR/downcast_method_wrong_return_type.rs:22:6
|
||||
--> fail/interface/downcast_method_wrong_return_type.rs:22:6
|
||||
|
|
||||
22 | impl Character for Human {}
|
||||
| ^^^^^^^^^ not found in this scope
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
||||
--> $DIR/field_double_underscored.rs:14:8
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
||||
--> fail/interface/field_double_underscored.rs:14:8
|
||||
|
|
||||
14 | fn __id(&self) -> &str {
|
||||
| ^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
||||
error[E0412]: cannot find type `CharacterValue` in this scope
|
||||
--> $DIR/field_double_underscored.rs:4:18
|
||||
--> fail/interface/field_double_underscored.rs:4:18
|
||||
|
|
||||
4 | #[graphql(impl = CharacterValue)]
|
||||
| ^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0405]: cannot find trait `Character` in this scope
|
||||
--> $DIR/field_double_underscored.rs:10:6
|
||||
--> fail/interface/field_double_underscored.rs:10:6
|
||||
|
|
||||
10 | impl Character for ObjA {}
|
||||
| ^^^^^^^^^ not found in this scope
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
error[E0277]: the trait bound `ObjB: IsOutputType<__S>` is not satisfied
|
||||
--> $DIR/field_non_output_return_type.rs:17:1
|
||||
|
|
||||
17 | #[graphql_interface(for = ObjA)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsOutputType<__S>` is not implemented for `ObjB`
|
||||
|
|
||||
= note: required by `juniper::marker::IsOutputType::mark`
|
||||
= note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
--> fail/interface/field_non_output_return_type.rs:17:1
|
||||
|
|
||||
17 | #[graphql_interface(for = ObjA)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsOutputType<__S>` is not implemented for `ObjB`
|
||||
|
|
||||
note: required by `juniper::marker::IsOutputType::mark`
|
||||
--> $WORKSPACE/juniper/src/types/marker.rs
|
||||
|
|
||||
| fn mark() {}
|
||||
| ^^^^^^^^^
|
||||
= note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
|
|
@ -1,25 +1,20 @@
|
|||
error: GraphQL interface must have a different name for each field
|
||||
--> $DIR/fields_duplicate.rs:13:1
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Interfaces
|
||||
|
||||
--> fail/interface/fields_duplicate.rs:13:1
|
||||
|
|
||||
13 | / trait Character {
|
||||
14 | | fn id(&self) -> &str {
|
||||
15 | | "funA"
|
||||
16 | | }
|
||||
... |
|
||||
21 | | }
|
||||
22 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Interfaces
|
||||
13 | trait Character {
|
||||
| ^^^^^
|
||||
|
||||
error[E0412]: cannot find type `CharacterValue` in this scope
|
||||
--> $DIR/fields_duplicate.rs:4:18
|
||||
--> fail/interface/fields_duplicate.rs:4:18
|
||||
|
|
||||
4 | #[graphql(impl = CharacterValue)]
|
||||
| ^^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
error[E0405]: cannot find trait `Character` in this scope
|
||||
--> $DIR/fields_duplicate.rs:10:6
|
||||
--> fail/interface/fields_duplicate.rs:10:6
|
||||
|
|
||||
10 | impl Character for ObjA {}
|
||||
| ^^^^^^^^^ not found in this scope
|
||||
|
|
|
@ -1,17 +1,25 @@
|
|||
error[E0277]: the trait bound `ObjA: GraphQLObject<__S>` is not satisfied
|
||||
--> $DIR/implementer_non_object_type.rs:15:1
|
||||
--> fail/interface/implementer_non_object_type.rs:15:1
|
||||
|
|
||||
15 | #[graphql_interface(for = ObjA)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `GraphQLObject<__S>` is not implemented for `ObjA`
|
||||
|
|
||||
= note: required by `juniper::GraphQLObject::mark`
|
||||
note: required by `juniper::GraphQLObject::mark`
|
||||
--> $WORKSPACE/juniper/src/types/marker.rs
|
||||
|
|
||||
| fn mark() {}
|
||||
| ^^^^^^^^^
|
||||
= note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0277]: the trait bound `ObjA: IsOutputType<__S>` is not satisfied
|
||||
--> $DIR/implementer_non_object_type.rs:15:1
|
||||
|
|
||||
15 | #[graphql_interface(for = ObjA)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsOutputType<__S>` is not implemented for `ObjA`
|
||||
|
|
||||
= note: required by `juniper::marker::IsOutputType::mark`
|
||||
= note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
--> fail/interface/implementer_non_object_type.rs:15:1
|
||||
|
|
||||
15 | #[graphql_interface(for = ObjA)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsOutputType<__S>` is not implemented for `ObjA`
|
||||
|
|
||||
note: required by `juniper::marker::IsOutputType::mark`
|
||||
--> $WORKSPACE/juniper/src/types/marker.rs
|
||||
|
|
||||
| fn mark() {}
|
||||
| ^^^^^^^^^
|
||||
= note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
||||
--> $DIR/name_double_underscored.rs:4:7
|
||||
|
|
||||
4 | trait __Character {
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
error: GraphQL interface must have at least one field
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Interfaces
|
||||
|
||||
--> $DIR/no_fields.rs:13:1
|
||||
|
|
||||
13 | trait Character {}
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Interfaces
|
||||
| ^^^^^
|
||||
|
||||
error[E0412]: cannot find type `CharacterValue` in this scope
|
||||
--> $DIR/no_fields.rs:4:18
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
||||
--> $DIR/argument_double_underscored.rs:7:18
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
||||
--> fail/object/argument_double_underscored.rs:7:18
|
||||
|
|
||||
7 | fn id(&self, __num: i32) -> &str {
|
||||
| ^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
error[E0277]: the trait bound `ObjA: IsInputType<__S>` is not satisfied
|
||||
--> $DIR/argument_non_input_type.rs:10:1
|
||||
|
|
||||
10 | #[graphql_object]
|
||||
| ^^^^^^^^^^^^^^^^^ the trait `IsInputType<__S>` is not implemented for `ObjA`
|
||||
|
|
||||
= note: required by `juniper::marker::IsInputType::mark`
|
||||
= note: this error originates in the attribute macro `graphql_object` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
--> fail/object/argument_non_input_type.rs:10:1
|
||||
|
|
||||
10 | #[graphql_object]
|
||||
| ^^^^^^^^^^^^^^^^^ the trait `IsInputType<__S>` is not implemented for `ObjA`
|
||||
|
|
||||
note: required by `juniper::marker::IsInputType::mark`
|
||||
--> $WORKSPACE/juniper/src/types/marker.rs
|
||||
|
|
||||
| fn mark() {}
|
||||
| ^^^^^^^^^
|
||||
= note: this error originates in the attribute macro `graphql_object` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0277]: the trait bound `ObjA: FromInputValue<__S>` is not satisfied
|
||||
--> $DIR/argument_non_input_type.rs:10:1
|
||||
--> fail/object/argument_non_input_type.rs:10:1
|
||||
|
|
||||
10 | #[graphql_object]
|
||||
| ^^^^^^^^^^^^^^^^^ the trait `FromInputValue<__S>` is not implemented for `ObjA`
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
error[E0277]: the trait bound `ObjB: IsOutputType<__S>` is not satisfied
|
||||
--> $DIR/attr_field_non_output_return_type.rs:10:1
|
||||
|
|
||||
10 | #[graphql_object]
|
||||
| ^^^^^^^^^^^^^^^^^ the trait `IsOutputType<__S>` is not implemented for `ObjB`
|
||||
|
|
||||
= note: required by `juniper::marker::IsOutputType::mark`
|
||||
= note: this error originates in the attribute macro `graphql_object` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
--> fail/object/attr_field_non_output_return_type.rs:10:1
|
||||
|
|
||||
10 | #[graphql_object]
|
||||
| ^^^^^^^^^^^^^^^^^ the trait `IsOutputType<__S>` is not implemented for `ObjB`
|
||||
|
|
||||
note: required by `juniper::marker::IsOutputType::mark`
|
||||
--> $WORKSPACE/juniper/src/types/marker.rs
|
||||
|
|
||||
| fn mark() {}
|
||||
| ^^^^^^^^^
|
||||
= note: this error originates in the attribute macro `graphql_object` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: GraphQL object must have a different name for each field
|
||||
--> $DIR/attr_fields_duplicate.rs:6:6
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Objects
|
||||
|
||||
--> fail/object/attr_fields_duplicate.rs:6:6
|
||||
|
|
||||
6 | impl ObjA {
|
||||
| ^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Objects
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
||||
--> $DIR/attr_name_double_underscored.rs:6:6
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
||||
--> fail/object/attr_name_double_underscored.rs:6:6
|
||||
|
|
||||
6 | impl __Obj {
|
||||
| ^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: GraphQL object must have at least one field
|
||||
--> $DIR/attr_no_fields.rs:6:6
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Objects
|
||||
|
||||
--> fail/object/attr_no_fields.rs:6:6
|
||||
|
|
||||
6 | impl Obj {}
|
||||
| ^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Objects
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
||||
--> $DIR/derive_field_double_underscored.rs:5:5
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
||||
--> fail/object/derive_field_double_underscored.rs:5:5
|
||||
|
|
||||
5 | __test: String,
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
error[E0277]: the trait bound `ObjB: IsOutputType<__S>` is not satisfied
|
||||
--> $DIR/derive_field_non_output_return_type.rs:8:10
|
||||
|
|
||||
8 | #[derive(GraphQLObject)]
|
||||
| ^^^^^^^^^^^^^ the trait `IsOutputType<__S>` is not implemented for `ObjB`
|
||||
|
|
||||
= note: required by `juniper::marker::IsOutputType::mark`
|
||||
= note: this error originates in the derive macro `GraphQLObject` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
--> fail/object/derive_field_non_output_return_type.rs:8:10
|
||||
|
|
||||
8 | #[derive(GraphQLObject)]
|
||||
| ^^^^^^^^^^^^^ the trait `IsOutputType<__S>` is not implemented for `ObjB`
|
||||
|
|
||||
note: required by `juniper::marker::IsOutputType::mark`
|
||||
--> $WORKSPACE/juniper/src/types/marker.rs
|
||||
|
|
||||
| fn mark() {}
|
||||
| ^^^^^^^^^
|
||||
= note: this error originates in the derive macro `GraphQLObject` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
error: GraphQL object must have a different name for each field
|
||||
--> $DIR/derive_fields_duplicate.rs:4:1
|
||||
|
|
||||
4 | / struct ObjA {
|
||||
5 | | id: String,
|
||||
6 | | #[graphql(name = "id")]
|
||||
7 | | id2: String,
|
||||
8 | | }
|
||||
| |_^
|
||||
|
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Objects
|
||||
|
||||
--> fail/object/derive_fields_duplicate.rs:4:1
|
||||
|
|
||||
4 | struct ObjA {
|
||||
| ^^^^^^
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
||||
--> $DIR/derive_name_double_underscored.rs:4:8
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
||||
--> fail/object/derive_name_double_underscored.rs:4:8
|
||||
|
|
||||
4 | struct __Obj {
|
||||
| ^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: GraphQL object must have at least one field
|
||||
--> $DIR/derive_no_fields.rs:4:1
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Objects
|
||||
|
||||
--> fail/object/derive_no_fields.rs:4:1
|
||||
|
|
||||
4 | struct Obj {}
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Objects
|
||||
| ^^^^^^
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: GraphQL object can only be derived for structs
|
||||
--> $DIR/derive_wrong_item.rs:4:1
|
||||
--> fail/object/derive_wrong_item.rs:4:1
|
||||
|
|
||||
4 | enum Character {}
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
| ^^^^
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
||||
--> $DIR/argument_double_underscored.rs:11:24
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
||||
--> fail/subscription/argument_double_underscored.rs:11:24
|
||||
|
|
||||
11 | async fn id(&self, __num: i32) -> Stream<'static, &'static str> {
|
||||
| ^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
error[E0277]: the trait bound `ObjA: IsInputType<__S>` is not satisfied
|
||||
--> $DIR/argument_non_input_type.rs:15:1
|
||||
|
|
||||
15 | #[graphql_subscription]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsInputType<__S>` is not implemented for `ObjA`
|
||||
|
|
||||
= note: required by `juniper::marker::IsInputType::mark`
|
||||
= note: this error originates in the attribute macro `graphql_subscription` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
--> fail/subscription/argument_non_input_type.rs:15:1
|
||||
|
|
||||
15 | #[graphql_subscription]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsInputType<__S>` is not implemented for `ObjA`
|
||||
|
|
||||
note: required by `juniper::marker::IsInputType::mark`
|
||||
--> $WORKSPACE/juniper/src/types/marker.rs
|
||||
|
|
||||
| fn mark() {}
|
||||
| ^^^^^^^^^
|
||||
= note: this error originates in the attribute macro `graphql_subscription` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0277]: the trait bound `ObjA: FromInputValue<__S>` is not satisfied
|
||||
--> $DIR/argument_non_input_type.rs:15:1
|
||||
--> fail/subscription/argument_non_input_type.rs:15:1
|
||||
|
|
||||
15 | #[graphql_subscription]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue<__S>` is not implemented for `ObjA`
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
error[E0277]: the trait bound `ObjB: IsOutputType<__S>` is not satisfied
|
||||
--> $DIR/field_non_output_return_type.rs:15:1
|
||||
|
|
||||
15 | #[graphql_subscription]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsOutputType<__S>` is not implemented for `ObjB`
|
||||
|
|
||||
= note: required by `juniper::marker::IsOutputType::mark`
|
||||
= note: this error originates in the attribute macro `graphql_subscription` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
--> fail/subscription/field_non_output_return_type.rs:15:1
|
||||
|
|
||||
15 | #[graphql_subscription]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsOutputType<__S>` is not implemented for `ObjB`
|
||||
|
|
||||
note: required by `juniper::marker::IsOutputType::mark`
|
||||
--> $WORKSPACE/juniper/src/types/marker.rs
|
||||
|
|
||||
| fn mark() {}
|
||||
| ^^^^^^^^^
|
||||
= note: this error originates in the attribute macro `graphql_subscription` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
error: GraphQL object synchronous resolvers are not supported
|
||||
--> $DIR/field_not_async.rs:11:5
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Objects
|
||||
= note: Specify that this function is async: `async fn foo()`
|
||||
|
||||
--> fail/subscription/field_not_async.rs:11:5
|
||||
|
|
||||
11 | fn id(&self) -> Stream<'static, bool> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Objects
|
||||
= note: Specify that this function is async: `async fn foo()`
|
||||
| ^^
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: GraphQL object must have a different name for each field
|
||||
--> $DIR/fields_duplicate.rs:10:6
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Objects
|
||||
|
||||
--> fail/subscription/fields_duplicate.rs:10:6
|
||||
|
|
||||
10 | impl ObjA {
|
||||
| ^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Objects
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
||||
--> $DIR/name_double_underscored.rs:10:6
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
||||
--> fail/subscription/name_double_underscored.rs:10:6
|
||||
|
|
||||
10 | impl __Obj {
|
||||
| ^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: GraphQL object must have at least one field
|
||||
--> $DIR/no_fields.rs:6:6
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Objects
|
||||
|
||||
--> fail/subscription/no_fields.rs:6:6
|
||||
|
|
||||
6 | impl Obj {}
|
||||
| ^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Objects
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: GraphQL union can only be derived for enums and structs
|
||||
--> $DIR/derive_wrong_item.rs:4:1
|
||||
--> fail/union/derive_wrong_item.rs:4:1
|
||||
|
|
||||
4 | union Character { id: i32 }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: GraphQL union variant `Human` already has external resolver function `resolve_fn1` declared on the enum
|
||||
--> $DIR/enum_external_resolver_fn_conflicts_with_variant_external_resolver_fn.rs:6:15
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
|
||||
--> fail/union/enum_external_resolver_fn_conflicts_with_variant_external_resolver_fn.rs:6:15
|
||||
|
|
||||
6 | #[graphql(with = resolve_fn2)]
|
||||
| ^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
||||
--> $DIR/enum_name_double_underscored.rs:4:6
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
||||
--> fail/union/enum_name_double_underscored.rs:4:6
|
||||
|
|
||||
4 | enum __Character {
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: GraphQL union expects at least one union variant
|
||||
--> $DIR/enum_no_fields.rs:4:1
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
|
||||
--> fail/union/enum_no_fields.rs:4:1
|
||||
|
|
||||
4 | enum Character {}
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
| ^^^^
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
error[E0277]: the trait bound `Test: GraphQLObject<__S>` is not satisfied
|
||||
--> $DIR/enum_non_object_variant.rs:9:10
|
||||
|
|
||||
9 | #[derive(GraphQLUnion)]
|
||||
| ^^^^^^^^^^^^ the trait `GraphQLObject<__S>` is not implemented for `Test`
|
||||
|
|
||||
= note: required by `juniper::GraphQLObject::mark`
|
||||
= note: this error originates in the derive macro `GraphQLUnion` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
--> fail/union/enum_non_object_variant.rs:9:10
|
||||
|
|
||||
9 | #[derive(GraphQLUnion)]
|
||||
| ^^^^^^^^^^^^ the trait `GraphQLObject<__S>` is not implemented for `Test`
|
||||
|
|
||||
note: required by `juniper::GraphQLObject::mark`
|
||||
--> $WORKSPACE/juniper/src/types/marker.rs
|
||||
|
|
||||
| fn mark() {}
|
||||
| ^^^^^^^^^
|
||||
= note: this error originates in the derive macro `GraphQLUnion` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
error: GraphQL union must have a different type for each union variant
|
||||
--> $DIR/enum_same_type_pretty.rs:4:1
|
||||
|
|
||||
4 | / enum Character {
|
||||
5 | | A(u8),
|
||||
6 | | B(u8),
|
||||
7 | | }
|
||||
| |_^
|
||||
|
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
|
||||
--> fail/union/enum_same_type_pretty.rs:4:1
|
||||
|
|
||||
4 | enum Character {
|
||||
| ^^^^
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
error: GraphQL union enum allows only unnamed variants with a single field, e.g. `Some(T)`
|
||||
--> $DIR/enum_wrong_variant_field.rs:5:5
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
|
||||
--> fail/union/enum_wrong_variant_field.rs:5:5
|
||||
|
|
||||
5 | A { human: Human },
|
||||
| ^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
|
||||
error: GraphQL union enum allows only unnamed variants with a single field, e.g. `Some(T)`
|
||||
--> $DIR/enum_wrong_variant_field.rs:10:6
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
|
||||
--> fail/union/enum_wrong_variant_field.rs:10:6
|
||||
|
|
||||
10 | A(Human, u8),
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
||||
--> $DIR/struct_name_double_underscored.rs:5:8
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
||||
--> fail/union/struct_name_double_underscored.rs:5:8
|
||||
|
|
||||
5 | struct __Character;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: GraphQL union expects at least one union variant
|
||||
--> $DIR/struct_no_fields.rs:4:1
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
|
||||
--> fail/union/struct_no_fields.rs:4:1
|
||||
|
|
||||
4 | struct Character;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
| ^^^^^^
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
error[E0277]: the trait bound `Test: GraphQLObject<__S>` is not satisfied
|
||||
--> $DIR/struct_non_object_variant.rs:9:10
|
||||
|
|
||||
9 | #[derive(GraphQLUnion)]
|
||||
| ^^^^^^^^^^^^ the trait `GraphQLObject<__S>` is not implemented for `Test`
|
||||
|
|
||||
= note: required by `juniper::GraphQLObject::mark`
|
||||
= note: this error originates in the derive macro `GraphQLUnion` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
--> fail/union/struct_non_object_variant.rs:9:10
|
||||
|
|
||||
9 | #[derive(GraphQLUnion)]
|
||||
| ^^^^^^^^^^^^ the trait `GraphQLObject<__S>` is not implemented for `Test`
|
||||
|
|
||||
note: required by `juniper::GraphQLObject::mark`
|
||||
--> $WORKSPACE/juniper/src/types/marker.rs
|
||||
|
|
||||
| fn mark() {}
|
||||
| ^^^^^^^^^
|
||||
= note: this error originates in the derive macro `GraphQLUnion` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
|
|
@ -1,14 +1,23 @@
|
|||
error[E0277]: the trait bound `CustomContext: FromContext<SubContext>` is not satisfied
|
||||
--> $DIR/trait_fail_infer_context.rs:3:1
|
||||
|
|
||||
3 | #[graphql_union]
|
||||
| ^^^^^^^^^^^^^^^^ expected an implementor of trait `FromContext<SubContext>`
|
||||
|
|
||||
= note: required by `juniper::FromContext::from`
|
||||
= note: this error originates in the attribute macro `graphql_union` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
--> fail/union/trait_fail_infer_context.rs:3:1
|
||||
|
|
||||
3 | #[graphql_union]
|
||||
| ^^^^^^^^^^^^^^^^ expected an implementor of trait `FromContext<SubContext>`
|
||||
4 | trait Character {
|
||||
| _______-
|
||||
5 | | fn a(&self, ctx: &SubContext) -> Option<&Human>;
|
||||
6 | | fn b(&self, ctx: &CustomContext) -> Option<&Droid>;
|
||||
| |________- required by a bound introduced by this call
|
||||
|
|
||||
note: required by `juniper::FromContext::from`
|
||||
--> $WORKSPACE/juniper/src/executor/mod.rs
|
||||
|
|
||||
| fn from(value: &T) -> &Self;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: this error originates in the attribute macro `graphql_union` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/trait_fail_infer_context.rs:3:1
|
||||
--> fail/union/trait_fail_infer_context.rs:3:1
|
||||
|
|
||||
3 | #[graphql_union]
|
||||
| ^^^^^^^^^^^^^^^^ expected struct `CustomContext`, found struct `SubContext`
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
error: GraphQL union trait method `a` conflicts with the external resolver function `some_fn` declared on the trait to resolve the variant type `Human`
|
||||
--> $DIR/trait_method_conflicts_with_external_resolver_fn.rs:5:5
|
||||
|
|
||||
5 | fn a(&self) -> Option<&Human>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
= note: use `#[graphql(ignore)]` attribute to ignore this trait method for union variants resolution
|
||||
|
||||
--> fail/union/trait_method_conflicts_with_external_resolver_fn.rs:5:5
|
||||
|
|
||||
5 | fn a(&self) -> Option<&Human>;
|
||||
| ^^
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
||||
--> $DIR/trait_name_double_underscored.rs:4:7
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
||||
--> fail/union/trait_name_double_underscored.rs:4:7
|
||||
|
|
||||
4 | trait __Character {
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: GraphQL union expects at least one union variant
|
||||
--> $DIR/trait_no_fields.rs:4:1
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
|
||||
--> fail/union/trait_no_fields.rs:4:1
|
||||
|
|
||||
4 | trait Character {}
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
| ^^^^^
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
error[E0277]: the trait bound `Test: GraphQLObject<__S>` is not satisfied
|
||||
--> $DIR/trait_non_object_variant.rs:9:1
|
||||
|
|
||||
9 | #[graphql_union]
|
||||
| ^^^^^^^^^^^^^^^^ the trait `GraphQLObject<__S>` is not implemented for `Test`
|
||||
|
|
||||
= note: required by `juniper::GraphQLObject::mark`
|
||||
= note: this error originates in the attribute macro `graphql_union` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
--> fail/union/trait_non_object_variant.rs:9:1
|
||||
|
|
||||
9 | #[graphql_union]
|
||||
| ^^^^^^^^^^^^^^^^ the trait `GraphQLObject<__S>` is not implemented for `Test`
|
||||
|
|
||||
note: required by `juniper::GraphQLObject::mark`
|
||||
--> $WORKSPACE/juniper/src/types/marker.rs
|
||||
|
|
||||
| fn mark() {}
|
||||
| ^^^^^^^^^
|
||||
= note: this error originates in the attribute macro `graphql_union` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
error: GraphQL union must have a different type for each union variant
|
||||
--> $DIR/trait_same_type_pretty.rs:4:1
|
||||
|
|
||||
4 | / trait Character {
|
||||
5 | | fn a(&self) -> Option<&u8>;
|
||||
6 | | fn b(&self) -> Option<&u8>;
|
||||
7 | | }
|
||||
| |_^
|
||||
|
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
|
||||
--> fail/union/trait_same_type_pretty.rs:4:1
|
||||
|
|
||||
4 | trait Character {
|
||||
| ^^^^^
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
error: GraphQL union cannot use #[graphql(with = ...)] attribute on a trait method
|
||||
--> $DIR/trait_with_attr_on_method.rs:5:15
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
= note: instead use #[graphql(ignore)] on the method with #[graphql_union(on ... = ...)] on the trait itself
|
||||
|
||||
--> fail/union/trait_with_attr_on_method.rs:5:15
|
||||
|
|
||||
5 | #[graphql(with = something)]
|
||||
| ^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
= note: instead use #[graphql(ignore)] on the method with #[graphql_union(on ... = ...)] on the trait itself
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: GraphQL union expects trait method to accept `&self` only and, optionally, `&Context`
|
||||
--> $DIR/trait_wrong_method_input_args.rs:5:10
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
|
||||
--> fail/union/trait_wrong_method_input_args.rs:5:10
|
||||
|
|
||||
5 | fn a(&self, ctx: &(), rand: u8) -> Option<&Human>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
| ^
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
error: GraphQL union expects trait method return type to be `Option<&VariantType>` only
|
||||
--> $DIR/trait_wrong_method_return_type.rs:5:20
|
||||
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
|
||||
--> fail/union/trait_wrong_method_return_type.rs:5:20
|
||||
|
|
||||
5 | fn a(&self) -> &Human;
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: https://spec.graphql.org/June2018/#sec-Unions
|
||||
| ^
|
||||
|
|
|
@ -84,7 +84,7 @@ fn test_derived_enum() {
|
|||
);
|
||||
assert_eq!(
|
||||
FromInputValue::<DefaultScalarValue>::from_input_value(&graphql_input_value!(REGULAR)),
|
||||
Some(SomeEnum::Regular),
|
||||
Ok(SomeEnum::Regular),
|
||||
);
|
||||
|
||||
// Test FULL variant.
|
||||
|
@ -94,7 +94,7 @@ fn test_derived_enum() {
|
|||
);
|
||||
assert_eq!(
|
||||
FromInputValue::<DefaultScalarValue>::from_input_value(&graphql_input_value!(FULL)),
|
||||
Some(SomeEnum::Full)
|
||||
Ok(SomeEnum::Full)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use fnv::FnvHashMap;
|
||||
use juniper::{
|
||||
graphql_input_value, marker, DefaultScalarValue, FromInputValue, GraphQLInputObject,
|
||||
GraphQLType, GraphQLValue, InputValue, Registry, ToInputValue,
|
||||
graphql_input_value, marker, DefaultScalarValue, FieldError, FromInputValue,
|
||||
GraphQLInputObject, GraphQLType, GraphQLValue, InputValue, Registry, ToInputValue,
|
||||
};
|
||||
|
||||
#[derive(GraphQLInputObject, Debug, PartialEq)]
|
||||
|
@ -58,8 +58,10 @@ struct Fake;
|
|||
impl<'a> marker::IsInputType<DefaultScalarValue> for &'a Fake {}
|
||||
|
||||
impl<'a> FromInputValue for &'a Fake {
|
||||
fn from_input_value(_v: &InputValue) -> Option<&'a Fake> {
|
||||
None
|
||||
type Error = FieldError;
|
||||
|
||||
fn from_input_value(_v: &InputValue) -> Result<&'a Fake, Self::Error> {
|
||||
Err("This is fake".into())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,10 +31,10 @@ where
|
|||
Value::scalar(self.0)
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<DefaultName> {
|
||||
v.as_scalar_value()
|
||||
.and_then(|s| s.as_int())
|
||||
.map(|i| DefaultName(i))
|
||||
fn from_input_value(v: &InputValue) -> Result<DefaultName, String> {
|
||||
v.as_int_value()
|
||||
.map(DefaultName)
|
||||
.ok_or_else(|| format!("Expected `Int`, found: {}", v))
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||
|
@ -48,8 +48,10 @@ impl GraphQLScalar for OtherOrder {
|
|||
Value::scalar(self.0)
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<OtherOrder> {
|
||||
v.as_scalar_value::<i32>().map(|i| OtherOrder(*i))
|
||||
fn from_input_value(v: &InputValue) -> Result<OtherOrder, String> {
|
||||
v.as_int_value()
|
||||
.map(OtherOrder)
|
||||
.ok_or_else(|| format!("Expected `Int`, found: {}", v))
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> {
|
||||
|
@ -63,8 +65,10 @@ impl GraphQLScalar for Named {
|
|||
Value::scalar(self.0)
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<Named> {
|
||||
v.as_scalar_value::<i32>().map(|i| Named(*i))
|
||||
fn from_input_value(v: &InputValue) -> Result<Named, String> {
|
||||
v.as_int_value()
|
||||
.map(Named)
|
||||
.ok_or_else(|| format!("Expected `Int`, found: {}", v))
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> {
|
||||
|
@ -78,8 +82,10 @@ impl GraphQLScalar for ScalarDescription {
|
|||
Value::scalar(self.0)
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<ScalarDescription> {
|
||||
v.as_scalar_value::<i32>().map(|i| ScalarDescription(*i))
|
||||
fn from_input_value(v: &InputValue) -> Result<ScalarDescription, String> {
|
||||
v.as_int_value()
|
||||
.map(ScalarDescription)
|
||||
.ok_or_else(|| format!("Expected `Int`, found: {}", v))
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> {
|
||||
|
@ -98,10 +104,11 @@ macro_rules! impl_scalar {
|
|||
Value::scalar(self.0.clone())
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<Self> {
|
||||
fn from_input_value(v: &InputValue) -> Result<Self, &'static str> {
|
||||
v.as_scalar_value()
|
||||
.and_then(|v| v.as_str())
|
||||
.and_then(|s| Some(Self(s.to_owned())))
|
||||
.ok_or_else(|| "Expected `String`")
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||
|
@ -140,9 +147,10 @@ impl GraphQLScalar for WithCustomScalarValue {
|
|||
Value::scalar(self.0)
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue<MyScalarValue>) -> Option<WithCustomScalarValue> {
|
||||
v.as_scalar_value::<i32>()
|
||||
.map(|i| WithCustomScalarValue(*i))
|
||||
fn from_input_value(v: &InputValue<MyScalarValue>) -> Result<WithCustomScalarValue, String> {
|
||||
v.as_int_value()
|
||||
.map(WithCustomScalarValue)
|
||||
.ok_or_else(|| format!("Expected Int, found: {}", v))
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, MyScalarValue> {
|
||||
|
@ -198,8 +206,10 @@ fn path_in_resolve_return_type() {
|
|||
Value::scalar(self.0)
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<ResolvePath> {
|
||||
v.as_scalar_value::<i32>().map(|i| ResolvePath(*i))
|
||||
fn from_input_value(v: &InputValue) -> Result<ResolvePath, String> {
|
||||
v.as_int_value()
|
||||
.map(ResolvePath)
|
||||
.ok_or_else(|| format!("Expected `Int`, found: {}", v))
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> {
|
||||
|
|
|
@ -138,11 +138,10 @@ impl GraphQLScalar for i64 {
|
|||
Value::scalar(*self)
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<i64> {
|
||||
match *v {
|
||||
InputValue::Scalar(MyScalarValue::Long(i)) => Some(i),
|
||||
_ => None,
|
||||
}
|
||||
fn from_input_value(v: &InputValue) -> Result<i64, String> {
|
||||
v.as_scalar_value::<i64>()
|
||||
.copied()
|
||||
.ok_or_else(|| format!("Expected `MyScalarValue::Long`, found: {}", v))
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, MyScalarValue> {
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
- `#[graphql_object]` and `#[graphql_subscription]` macros expansion now preserves defined `impl` blocks "as is" and reuses defined methods in opaque way. ([#971](https://github.com/graphql-rust/juniper/pull/971))
|
||||
- `rename = "<policy>"` attribute's argument renamed to `rename_all = "<policy>"`. ([#971](https://github.com/graphql-rust/juniper/pull/971))
|
||||
- Upgrade `bson` feature to [2.0 version of its crate](https://github.com/mongodb/bson-rust/releases/tag/v2.0.0). ([#979](https://github.com/graphql-rust/juniper/pull/979))
|
||||
- Make `FromInputValue` methods fallible to allow post-validation. ([#987](https://github.com/graphql-rust/juniper/pull/987))
|
||||
- Change `Option` to `Result` in `from_input_value()` return type of `#[graphql_scalar]` macro. ([#987](https://github.com/graphql-rust/juniper/pull/987))
|
||||
- Forbid `__typename` field on `subscription` operations [accordingly to October 2021 spec](https://spec.graphql.org/October2021/#note-bc213). ([#1001](https://github.com/graphql-rust/juniper/pull/1001), [#1000](https://github.com/graphql-rust/juniper/pull/1000))
|
||||
|
||||
## Features
|
||||
|
|
|
@ -150,22 +150,39 @@ pub type Document<'a, S> = [Definition<'a, S>];
|
|||
#[doc(hidden)]
|
||||
pub type OwnedDocument<'a, S> = Vec<Definition<'a, S>>;
|
||||
|
||||
/// Parse an unstructured input value into a Rust data type.
|
||||
/// Parsing of an unstructured input value into a Rust data type.
|
||||
///
|
||||
/// The conversion _can_ fail, and must in that case return None. Implemented
|
||||
/// automatically by the convenience proc macro `graphql_scalar` or by deriving GraphQLEnum.
|
||||
/// The conversion _can_ fail, and must in that case return [`Err`]. Thus not
|
||||
/// restricted in the definition of this trait, the returned [`Err`] should be
|
||||
/// convertible with [`IntoFieldError`] to fit well into the library machinery.
|
||||
///
|
||||
/// Implemented automatically by the convenience proc macro [`graphql_scalar!`]
|
||||
/// or by deriving `GraphQLEnum`.
|
||||
///
|
||||
/// Must be implemented manually when manually exposing new enums or scalars.
|
||||
///
|
||||
/// [`graphql_scalar!`]: macro@crate::graphql_scalar
|
||||
/// [`IntoFieldError`]: crate::IntoFieldError
|
||||
pub trait FromInputValue<S = DefaultScalarValue>: Sized {
|
||||
/// Performs the conversion.
|
||||
fn from_input_value(v: &InputValue<S>) -> Option<Self>;
|
||||
|
||||
/// Performs the conversion from an absent value (e.g. to distinguish between
|
||||
/// implicit and explicit null). The default implementation just uses
|
||||
/// `from_input_value` as if an explicit null were provided.
|
||||
/// Type of this conversion error.
|
||||
///
|
||||
/// This conversion must not fail.
|
||||
fn from_implicit_null() -> Option<Self> {
|
||||
/// Thus not restricted, it should be convertible with [`IntoFieldError`] to
|
||||
/// fit well into the library machinery.
|
||||
///
|
||||
/// [`IntoFieldError`]: crate::IntoFieldError
|
||||
type Error;
|
||||
|
||||
/// Performs the conversion.
|
||||
fn from_input_value(v: &InputValue<S>) -> Result<Self, Self::Error>;
|
||||
|
||||
/// Performs the conversion from an absent value (e.g. to distinguish
|
||||
/// between implicit and explicit `null`).
|
||||
///
|
||||
/// The default implementation just calls [`from_input_value()`] as if an
|
||||
/// explicit `null` was provided.
|
||||
///
|
||||
/// [`from_input_value()`]: FromInputValue::from_input_value
|
||||
fn from_implicit_null() -> Result<Self, Self::Error> {
|
||||
Self::from_input_value(&InputValue::<S>::Null)
|
||||
}
|
||||
}
|
||||
|
@ -299,11 +316,8 @@ impl<S> InputValue<S> {
|
|||
}
|
||||
|
||||
/// Shorthand form of invoking [`FromInputValue::from_input_value()`].
|
||||
pub fn convert<T>(&self) -> Option<T>
|
||||
where
|
||||
T: FromInputValue<S>,
|
||||
{
|
||||
<T as FromInputValue<S>>::from_input_value(self)
|
||||
pub fn convert<T: FromInputValue<S>>(&self) -> Result<T, T::Error> {
|
||||
T::from_input_value(self)
|
||||
}
|
||||
|
||||
/// Does the value represent a `null`?
|
||||
|
|
|
@ -149,47 +149,37 @@ where
|
|||
/// Ok(s)
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct FieldError<S = DefaultScalarValue> {
|
||||
message: String,
|
||||
extensions: Value<S>,
|
||||
}
|
||||
|
||||
impl<T: Display, S> From<T> for FieldError<S>
|
||||
where
|
||||
S: crate::value::ScalarValue,
|
||||
{
|
||||
fn from(e: T) -> FieldError<S> {
|
||||
FieldError {
|
||||
message: format!("{}", e),
|
||||
extensions: Value::null(),
|
||||
impl<T: Display, S> From<T> for FieldError<S> {
|
||||
fn from(e: T) -> Self {
|
||||
Self {
|
||||
message: e.to_string(),
|
||||
extensions: Value::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> FieldError<S> {
|
||||
/// Construct a new error with additional data
|
||||
///
|
||||
/// You can use the `graphql_value!` macro to construct an error:
|
||||
/// Construct a new [`FieldError`] with additional data.
|
||||
///
|
||||
/// You can use the [`graphql_value!`] macro for construction:
|
||||
/// ```rust
|
||||
/// use juniper::FieldError;
|
||||
/// # use juniper::DefaultScalarValue;
|
||||
/// use juniper::graphql_value;
|
||||
/// use juniper::{graphql_value, FieldError};
|
||||
///
|
||||
/// # fn sample() {
|
||||
/// # let _: FieldError<DefaultScalarValue> =
|
||||
/// # let _: FieldError =
|
||||
/// FieldError::new(
|
||||
/// "Could not open connection to the database",
|
||||
/// graphql_value!({ "internal_error": "Connection refused" })
|
||||
/// graphql_value!({"internal_error": "Connection refused"}),
|
||||
/// );
|
||||
/// # }
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// The `extensions` parameter will be added to the `"extensions"` field of the error
|
||||
/// object in the JSON response:
|
||||
///
|
||||
/// The `extensions` parameter will be added to the `"extensions"` field of
|
||||
/// the `"errors"` object in response:
|
||||
/// ```json
|
||||
/// {
|
||||
/// "errors": [
|
||||
|
@ -202,25 +192,34 @@ impl<S> FieldError<S> {
|
|||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// If the argument is `Value::null()`, no extra data will be included.
|
||||
pub fn new<T: Display>(e: T, extensions: Value<S>) -> FieldError<S> {
|
||||
FieldError {
|
||||
message: format!("{}", e),
|
||||
/// If the argument is [`Value::Null`], then no extra data will be included.
|
||||
///
|
||||
/// [`graphql_value!`]: macro@crate::graphql_value
|
||||
#[must_use]
|
||||
pub fn new<T: Display>(e: T, extensions: Value<S>) -> Self {
|
||||
Self {
|
||||
message: e.to_string(),
|
||||
extensions,
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Returns `"message"` field of this [`FieldError`].
|
||||
#[must_use]
|
||||
pub fn message(&self) -> &str {
|
||||
&self.message
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Returns `"extensions"` field of this [`FieldError`].
|
||||
///
|
||||
/// If there is no `"extensions"`, then [`Value::Null`] will be returned.
|
||||
#[must_use]
|
||||
pub fn extensions(&self) -> &Value<S> {
|
||||
&self.extensions
|
||||
}
|
||||
|
||||
/// Maps the [`ScalarValue`] type of this [`FieldError`] into the specified one.
|
||||
/// Maps the [`ScalarValue`] type of this [`FieldError`] into the specified
|
||||
/// one.
|
||||
#[must_use]
|
||||
pub fn map_scalar_value<Into>(self) -> FieldError<Into>
|
||||
where
|
||||
S: ScalarValue,
|
||||
|
@ -231,6 +230,15 @@ impl<S> FieldError<S> {
|
|||
extensions: self.extensions.map_scalar_value(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps the [`FieldError::message`] with the given function.
|
||||
#[must_use]
|
||||
pub fn map_message(self, f: impl FnOnce(String) -> String) -> Self {
|
||||
Self {
|
||||
message: f(self.message),
|
||||
extensions: self.extensions,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The result of resolving the value of a field of type `T`
|
||||
|
@ -246,17 +254,18 @@ pub type ValuesStream<'a, S = DefaultScalarValue> =
|
|||
/// The map of variables used for substitution during query execution
|
||||
pub type Variables<S = DefaultScalarValue> = HashMap<String, InputValue<S>>;
|
||||
|
||||
/// Custom error handling trait to enable Error types other than `FieldError` to be specified
|
||||
/// as return value.
|
||||
/// Custom error handling trait to enable error types other than [`FieldError`]
|
||||
/// to be specified as return value.
|
||||
///
|
||||
/// Any custom error type should implement this trait to convert it to `FieldError`.
|
||||
/// Any custom error type should implement this trait to convert itself into a
|
||||
/// [`FieldError`].
|
||||
pub trait IntoFieldError<S = DefaultScalarValue> {
|
||||
#[doc(hidden)]
|
||||
/// Performs the custom conversion into a [`FieldError`].
|
||||
#[must_use]
|
||||
fn into_field_error(self) -> FieldError<S>;
|
||||
}
|
||||
|
||||
impl<S1: ScalarValue, S2: ScalarValue> IntoFieldError<S2> for FieldError<S1> {
|
||||
#[inline]
|
||||
fn into_field_error(self) -> FieldError<S2> {
|
||||
self.map_scalar_value()
|
||||
}
|
||||
|
@ -268,6 +277,24 @@ impl<S> IntoFieldError<S> for std::convert::Infallible {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, S> IntoFieldError<S> for &'a str {
|
||||
fn into_field_error(self) -> FieldError<S> {
|
||||
FieldError::<S>::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> IntoFieldError<S> for String {
|
||||
fn into_field_error(self) -> FieldError<S> {
|
||||
FieldError::<S>::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S> IntoFieldError<S> for Cow<'a, str> {
|
||||
fn into_field_error(self) -> FieldError<S> {
|
||||
FieldError::<S>::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait IntoResolvable<'a, S, T, C>
|
||||
where
|
||||
|
@ -1125,22 +1152,21 @@ where
|
|||
Ok((value, errors))
|
||||
}
|
||||
|
||||
impl<'r, S> Registry<'r, S>
|
||||
where
|
||||
S: ScalarValue + 'r,
|
||||
{
|
||||
/// Construct a new registry
|
||||
pub fn new(types: FnvHashMap<Name, MetaType<'r, S>>) -> Registry<'r, S> {
|
||||
Registry { types }
|
||||
impl<'r, S: 'r> Registry<'r, S> {
|
||||
/// Constructs a new [`Registry`] out of the given `types`.
|
||||
pub fn new(types: FnvHashMap<Name, MetaType<'r, S>>) -> Self {
|
||||
Self { types }
|
||||
}
|
||||
|
||||
/// Get the `Type` instance for a given GraphQL type
|
||||
/// Returns a [`Type`] instance for the given [`GraphQLType`], registered in
|
||||
/// this [`Registry`].
|
||||
///
|
||||
/// If the registry hasn't seen a type with this name before, it will
|
||||
/// construct its metadata and store it.
|
||||
/// If this [`Registry`] hasn't seen a [`Type`] with such
|
||||
/// [`GraphQLType::name`] before, it will construct the one and store it.
|
||||
pub fn get_type<T>(&mut self, info: &T::TypeInfo) -> Type<'r>
|
||||
where
|
||||
T: GraphQLType<S> + ?Sized,
|
||||
S: ScalarValue,
|
||||
{
|
||||
if let Some(name) = T::name(info) {
|
||||
let validated_name = name.parse::<Name>().unwrap();
|
||||
|
@ -1158,10 +1184,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Create a field with the provided name
|
||||
/// Creates a [`Field`] with the provided `name`.
|
||||
pub fn field<T>(&mut self, name: &str, info: &T::TypeInfo) -> Field<'r, S>
|
||||
where
|
||||
T: GraphQLType<S> + ?Sized,
|
||||
S: ScalarValue,
|
||||
{
|
||||
Field {
|
||||
name: smartstring::SmartString::from(name),
|
||||
|
@ -1180,6 +1207,7 @@ where
|
|||
) -> Field<'r, S>
|
||||
where
|
||||
I: GraphQLType<S>,
|
||||
S: ScalarValue,
|
||||
{
|
||||
Field {
|
||||
name: smartstring::SmartString::from(name),
|
||||
|
@ -1190,18 +1218,19 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Create an argument with the provided name
|
||||
/// Creates an [`Argument`] with the provided `name`.
|
||||
pub fn arg<T>(&mut self, name: &str, info: &T::TypeInfo) -> Argument<'r, S>
|
||||
where
|
||||
T: GraphQLType<S> + FromInputValue<S> + ?Sized,
|
||||
T: GraphQLType<S> + FromInputValue<S>,
|
||||
S: ScalarValue,
|
||||
{
|
||||
Argument::new(name, self.get_type::<T>(info))
|
||||
}
|
||||
|
||||
/// Create an argument with a default value
|
||||
/// Creates an [`Argument`] with the provided default `value`.
|
||||
///
|
||||
/// When called with type `T`, the actual argument will be given the type
|
||||
/// `Option<T>`.
|
||||
/// When called with type `T`, the actual [`Argument`] will be given the
|
||||
/// type `Option<T>`.
|
||||
pub fn arg_with_default<T>(
|
||||
&mut self,
|
||||
name: &str,
|
||||
|
@ -1209,7 +1238,8 @@ where
|
|||
info: &T::TypeInfo,
|
||||
) -> Argument<'r, S>
|
||||
where
|
||||
T: GraphQLType<S> + ToInputValue<S> + FromInputValue<S> + ?Sized,
|
||||
T: GraphQLType<S> + ToInputValue<S> + FromInputValue<S>,
|
||||
S: ScalarValue,
|
||||
{
|
||||
Argument::new(name, self.get_type::<Option<T>>(info)).default_value(value.to_input_value())
|
||||
}
|
||||
|
@ -1220,40 +1250,46 @@ where
|
|||
.or_insert(MetaType::Placeholder(PlaceholderMeta { of_type }));
|
||||
}
|
||||
|
||||
/// Create a scalar meta type
|
||||
///
|
||||
/// This expects the type to implement `FromInputValue`.
|
||||
/// Creates a [`ScalarMeta`] type.
|
||||
pub fn build_scalar_type<T>(&mut self, info: &T::TypeInfo) -> ScalarMeta<'r, S>
|
||||
where
|
||||
T: FromInputValue<S> + GraphQLType<S> + ParseScalarValue<S> + ?Sized + 'r,
|
||||
T: GraphQLType<S> + FromInputValue<S> + ParseScalarValue<S> + 'r,
|
||||
T::Error: IntoFieldError<S>,
|
||||
S: ScalarValue,
|
||||
{
|
||||
let name = T::name(info).expect("Scalar types must be named. Implement name()");
|
||||
let name = T::name(info).expect("Scalar types must be named. Implement `name()`");
|
||||
|
||||
ScalarMeta::new::<T>(Cow::Owned(name.to_string()))
|
||||
}
|
||||
|
||||
/// Create a list meta type
|
||||
pub fn build_list_type<T: GraphQLType<S> + ?Sized>(
|
||||
/// Creates a [`ListMeta`] type.
|
||||
///
|
||||
/// Specifying `expected_size` will be used to ensure that values of this
|
||||
/// type will always match it.
|
||||
pub fn build_list_type<T>(
|
||||
&mut self,
|
||||
info: &T::TypeInfo,
|
||||
expected_size: Option<usize>,
|
||||
) -> ListMeta<'r> {
|
||||
) -> ListMeta<'r>
|
||||
where
|
||||
T: GraphQLType<S> + ?Sized,
|
||||
S: ScalarValue,
|
||||
{
|
||||
let of_type = self.get_type::<T>(info);
|
||||
ListMeta::new(of_type, expected_size)
|
||||
}
|
||||
|
||||
/// Create a nullable meta type
|
||||
pub fn build_nullable_type<T: GraphQLType<S> + ?Sized>(
|
||||
&mut self,
|
||||
info: &T::TypeInfo,
|
||||
) -> NullableMeta<'r> {
|
||||
/// Creates a [`NullableMeta`] type.
|
||||
pub fn build_nullable_type<T>(&mut self, info: &T::TypeInfo) -> NullableMeta<'r>
|
||||
where
|
||||
T: GraphQLType<S> + ?Sized,
|
||||
S: ScalarValue,
|
||||
{
|
||||
let of_type = self.get_type::<T>(info);
|
||||
NullableMeta::new(of_type)
|
||||
}
|
||||
|
||||
/// Create an object meta type
|
||||
///
|
||||
/// To prevent infinite recursion by enforcing ordering, this returns a
|
||||
/// function that needs to be called with the list of fields on the object.
|
||||
/// Creates an [`ObjectMeta`] type with the given `fields`.
|
||||
pub fn build_object_type<T>(
|
||||
&mut self,
|
||||
info: &T::TypeInfo,
|
||||
|
@ -1261,6 +1297,7 @@ where
|
|||
) -> ObjectMeta<'r, S>
|
||||
where
|
||||
T: GraphQLType<S> + ?Sized,
|
||||
S: ScalarValue,
|
||||
{
|
||||
let name = T::name(info).expect("Object types must be named. Implement name()");
|
||||
|
||||
|
@ -1269,22 +1306,23 @@ where
|
|||
ObjectMeta::new(Cow::Owned(name.to_string()), &v)
|
||||
}
|
||||
|
||||
/// Create an enum meta type
|
||||
/// Creates an [`EnumMeta`] type out of the provided `values`.
|
||||
pub fn build_enum_type<T>(
|
||||
&mut self,
|
||||
info: &T::TypeInfo,
|
||||
values: &[EnumValue],
|
||||
) -> EnumMeta<'r, S>
|
||||
where
|
||||
T: FromInputValue<S> + GraphQLType<S> + ?Sized,
|
||||
T: GraphQLType<S> + FromInputValue<S>,
|
||||
T::Error: IntoFieldError<S>,
|
||||
S: ScalarValue,
|
||||
{
|
||||
let name = T::name(info).expect("Enum types must be named. Implement name()");
|
||||
let name = T::name(info).expect("Enum types must be named. Implement `name()`");
|
||||
|
||||
EnumMeta::new::<T>(Cow::Owned(name.to_string()), values)
|
||||
}
|
||||
|
||||
/// Create an interface meta type,
|
||||
/// by providing a type info object.
|
||||
/// Creates an [`InterfaceMeta`] type with the given `fields`.
|
||||
pub fn build_interface_type<T>(
|
||||
&mut self,
|
||||
info: &T::TypeInfo,
|
||||
|
@ -1292,6 +1330,7 @@ where
|
|||
) -> InterfaceMeta<'r, S>
|
||||
where
|
||||
T: GraphQLType<S> + ?Sized,
|
||||
S: ScalarValue,
|
||||
{
|
||||
let name = T::name(info).expect("Interface types must be named. Implement name()");
|
||||
|
||||
|
@ -1300,24 +1339,27 @@ where
|
|||
InterfaceMeta::new(Cow::Owned(name.to_string()), &v)
|
||||
}
|
||||
|
||||
/// Create a union meta type
|
||||
/// Creates an [`UnionMeta`] type of the given `types`.
|
||||
pub fn build_union_type<T>(&mut self, info: &T::TypeInfo, types: &[Type<'r>]) -> UnionMeta<'r>
|
||||
where
|
||||
T: GraphQLType<S> + ?Sized,
|
||||
S: ScalarValue,
|
||||
{
|
||||
let name = T::name(info).expect("Union types must be named. Implement name()");
|
||||
|
||||
UnionMeta::new(Cow::Owned(name.to_string()), types)
|
||||
}
|
||||
|
||||
/// Create an input object meta type
|
||||
/// Creates an [`InputObjectMeta`] type with the given `args`.
|
||||
pub fn build_input_object_type<T>(
|
||||
&mut self,
|
||||
info: &T::TypeInfo,
|
||||
args: &[Argument<'r, S>],
|
||||
) -> InputObjectMeta<'r, S>
|
||||
where
|
||||
T: FromInputValue<S> + GraphQLType<S> + ?Sized,
|
||||
T: GraphQLType<S> + FromInputValue<S>,
|
||||
T::Error: IntoFieldError<S>,
|
||||
S: ScalarValue,
|
||||
{
|
||||
let name = T::name(info).expect("Input object types must be named. Implement name()");
|
||||
|
||||
|
|
|
@ -204,9 +204,9 @@ fn default_name_input_value() {
|
|||
"fieldTwo": "number two",
|
||||
});
|
||||
|
||||
let dv: Option<DefaultName> = FromInputValue::from_input_value(&iv);
|
||||
let dv = DefaultName::from_input_value(&iv);
|
||||
|
||||
assert!(dv.is_some());
|
||||
assert!(dv.is_ok(), "error: {}", dv.unwrap_err().message());
|
||||
|
||||
let dv = dv.unwrap();
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
graphql_interface, graphql_object, graphql_scalar, graphql_value, graphql_vars,
|
||||
schema::model::RootNode,
|
||||
types::scalars::{EmptyMutation, EmptySubscription},
|
||||
value::{ParseScalarResult, ParseScalarValue, ScalarValue, Value},
|
||||
value::{ParseScalarResult, ParseScalarValue, Value},
|
||||
GraphQLEnum,
|
||||
};
|
||||
|
||||
|
@ -28,8 +28,10 @@ impl<S: ScalarValue> GraphQLScalar for Scalar {
|
|||
Value::scalar(self.0)
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<Scalar> {
|
||||
v.as_scalar().and_then(ScalarValue::as_int).map(Scalar)
|
||||
fn from_input_value(v: &InputValue) -> Result<Scalar, String> {
|
||||
v.as_int_value()
|
||||
.map(Scalar)
|
||||
.ok_or_else(|| format!("Expected `Int`, found: {}", v))
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
|||
schema::model::RootNode,
|
||||
types::scalars::{EmptyMutation, EmptySubscription},
|
||||
validation::RuleError,
|
||||
value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, ScalarValue},
|
||||
value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue},
|
||||
GraphQLError::ValidationError,
|
||||
GraphQLInputObject,
|
||||
};
|
||||
|
@ -19,14 +19,11 @@ impl<S: ScalarValue> GraphQLScalar for TestComplexScalar {
|
|||
graphql_value!("SerializedValue")
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<TestComplexScalar> {
|
||||
if let Some(s) = v.as_scalar().and_then(ScalarValue::as_str) {
|
||||
if s == "SerializedValue" {
|
||||
return Some(TestComplexScalar);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
fn from_input_value(v: &InputValue) -> Result<TestComplexScalar, String> {
|
||||
v.as_string_value()
|
||||
.filter(|s| *s == "SerializedValue")
|
||||
.map(|_| TestComplexScalar)
|
||||
.ok_or_else(|| format!(r#"Expected "SerializedValue" string, found: {}"#, v))
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||
|
@ -1075,7 +1072,8 @@ mod integers {
|
|||
assert_eq!(
|
||||
error,
|
||||
ValidationError(vec![RuleError::new(
|
||||
r#"Variable "$var" got invalid value. Expected "Int"."#,
|
||||
"Variable \"$var\" got invalid value. Expected input scalar `Int`. \
|
||||
Got: `10`. Details: Expected `Int`, found: 10.",
|
||||
&[SourcePosition::new(8, 0, 8)],
|
||||
)]),
|
||||
);
|
||||
|
@ -1099,7 +1097,9 @@ mod integers {
|
|||
assert_eq!(
|
||||
error,
|
||||
ValidationError(vec![RuleError::new(
|
||||
r#"Variable "$var" got invalid value. Expected "Int"."#,
|
||||
"Variable \"$var\" got invalid value. \
|
||||
Expected input scalar `Int`. Got: `\"10\"`. \
|
||||
Details: Expected `Int`, found: \"10\".",
|
||||
&[SourcePosition::new(8, 0, 8)],
|
||||
)]),
|
||||
);
|
||||
|
@ -1157,7 +1157,9 @@ mod floats {
|
|||
assert_eq!(
|
||||
error,
|
||||
ValidationError(vec![RuleError::new(
|
||||
r#"Variable "$var" got invalid value. Expected "Float"."#,
|
||||
"Variable \"$var\" got invalid value. \
|
||||
Expected input scalar `Float`. Got: `\"10\"`. \
|
||||
Details: Expected `Float`, found: \"10\".",
|
||||
&[SourcePosition::new(8, 0, 8)],
|
||||
)]),
|
||||
);
|
||||
|
|
|
@ -19,8 +19,12 @@ where
|
|||
Value::scalar(self.to_hex())
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<ObjectId> {
|
||||
v.as_string_value().and_then(|s| Self::parse_str(s).ok())
|
||||
fn from_input_value(v: &InputValue) -> Result<ObjectId, String> {
|
||||
v.as_string_value()
|
||||
.ok_or_else(|| format!("Expected `String`, found: {}", v))
|
||||
.and_then(|s| {
|
||||
Self::parse_str(s).map_err(|e| format!("Failed to parse `ObjectId`: {}", e))
|
||||
})
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||
|
@ -41,9 +45,13 @@ where
|
|||
Value::scalar((*self).to_chrono().to_rfc3339())
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<UtcDateTime> {
|
||||
fn from_input_value(v: &InputValue) -> Result<UtcDateTime, String> {
|
||||
v.as_string_value()
|
||||
.and_then(|s| (s.parse::<DateTime<Utc>>().ok()))
|
||||
.ok_or_else(|| format!("Expected `String`, found: {}", v))
|
||||
.and_then(|s| {
|
||||
s.parse::<DateTime<Utc>>()
|
||||
.map_err(|e| format!("Failed to parse `UtcDateTime`: {}", e))
|
||||
})
|
||||
.map(Self::from_chrono)
|
||||
}
|
||||
|
||||
|
|
|
@ -36,9 +36,13 @@ where
|
|||
Value::scalar(self.to_rfc3339())
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<DateTime<FixedOffset>> {
|
||||
fn from_input_value(v: &InputValue) -> Result<DateTime<FixedOffset>, String> {
|
||||
v.as_string_value()
|
||||
.and_then(|s| DateTime::parse_from_rfc3339(s).ok())
|
||||
.ok_or_else(|| format!("Expected `String`, found: {}", v))
|
||||
.and_then(|s| {
|
||||
DateTime::parse_from_rfc3339(s)
|
||||
.map_err(|e| format!("Failed to parse `DateTimeFixedOffset`: {}", e))
|
||||
})
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||
|
@ -59,9 +63,13 @@ where
|
|||
Value::scalar(self.to_rfc3339())
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<DateTime<Utc>> {
|
||||
fn from_input_value(v: &InputValue) -> Result<DateTime<Utc>, String> {
|
||||
v.as_string_value()
|
||||
.and_then(|s| (s.parse::<DateTime<Utc>>().ok()))
|
||||
.ok_or_else(|| format!("Expected `String`, found: {}", v))
|
||||
.and_then(|s| {
|
||||
s.parse::<DateTime<Utc>>()
|
||||
.map_err(|e| format!("Failed to parse `DateTimeUtc`: {}", e))
|
||||
})
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||
|
@ -87,9 +95,13 @@ where
|
|||
Value::scalar(self.format("%Y-%m-%d").to_string())
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<NaiveDate> {
|
||||
fn from_input_value(v: &InputValue) -> Result<NaiveDate, String> {
|
||||
v.as_string_value()
|
||||
.and_then(|s| NaiveDate::parse_from_str(s, "%Y-%m-%d").ok())
|
||||
.ok_or_else(|| format!("Expected `String`, found: {}", v))
|
||||
.and_then(|s| {
|
||||
NaiveDate::parse_from_str(s, "%Y-%m-%d")
|
||||
.map_err(|e| format!("Failed to parse `NaiveDate`: {}", e))
|
||||
})
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||
|
@ -111,9 +123,13 @@ where
|
|||
Value::scalar(self.format("%H:%M:%S").to_string())
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<NaiveTime> {
|
||||
fn from_input_value(v: &InputValue) -> Result<NaiveTime, String> {
|
||||
v.as_string_value()
|
||||
.and_then(|s| NaiveTime::parse_from_str(s, "%H:%M:%S").ok())
|
||||
.ok_or_else(|| format!("Expected `String`, found: {}", v))
|
||||
.and_then(|s| {
|
||||
NaiveTime::parse_from_str(s, "%H:%M:%S")
|
||||
.map_err(|e| format!("Failed to parse `NaiveTime`: {}", e))
|
||||
})
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||
|
@ -136,9 +152,14 @@ where
|
|||
Value::scalar(self.timestamp() as f64)
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<NaiveDateTime> {
|
||||
fn from_input_value(v: &InputValue) -> Result<NaiveDateTime, String> {
|
||||
v.as_float_value()
|
||||
.and_then(|f| NaiveDateTime::from_timestamp_opt(f as i64, 0))
|
||||
.ok_or_else(|| format!("Expected `Float`, found: {}", v))
|
||||
.and_then(|f| {
|
||||
let secs = f as i64;
|
||||
NaiveDateTime::from_timestamp_opt(secs, 0)
|
||||
.ok_or_else(|| format!("Out-of-range number of seconds: {}", secs))
|
||||
})
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||
|
|
|
@ -21,8 +21,13 @@ where
|
|||
Value::scalar(self.name().to_owned())
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<Tz> {
|
||||
v.as_string_value().and_then(|s| s.parse::<Tz>().ok())
|
||||
fn from_input_value(v: &InputValue) -> Result<Tz, String> {
|
||||
v.as_string_value()
|
||||
.ok_or_else(|| format!("Expected `String`, found: {}", v))
|
||||
.and_then(|s| {
|
||||
s.parse::<Tz>()
|
||||
.map_err(|e| format!("Failed to parse `Tz`: {}", e))
|
||||
})
|
||||
}
|
||||
|
||||
fn from_str<'a>(val: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||
|
@ -37,25 +42,30 @@ where
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
mod from_input_value {
|
||||
use std::ops::Deref;
|
||||
|
||||
use chrono_tz::Tz;
|
||||
|
||||
use crate::{graphql_input_value, FromInputValue, InputValue};
|
||||
|
||||
fn tz_input_test(raw: &'static str, expected: Option<Tz>) {
|
||||
fn tz_input_test(raw: &'static str, expected: Result<Tz, &str>) {
|
||||
let input: InputValue = graphql_input_value!((raw));
|
||||
let parsed: Option<Tz> = FromInputValue::from_input_value(&input);
|
||||
let parsed = FromInputValue::from_input_value(&input);
|
||||
|
||||
assert_eq!(parsed, expected);
|
||||
assert_eq!(
|
||||
parsed.as_ref().map_err(Deref::deref),
|
||||
expected.as_ref().map_err(Deref::deref),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn europe_zone() {
|
||||
tz_input_test("Europe/London", Some(chrono_tz::Europe::London));
|
||||
tz_input_test("Europe/London", Ok(chrono_tz::Europe::London));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn etc_minus() {
|
||||
tz_input_test("Etc/GMT-3", Some(chrono_tz::Etc::GMTMinus3));
|
||||
tz_input_test("Etc/GMT-3", Ok(chrono_tz::Etc::GMTMinus3));
|
||||
}
|
||||
|
||||
mod invalid {
|
||||
|
@ -63,17 +73,26 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn forward_slash() {
|
||||
tz_input_test("Abc/Xyz", None);
|
||||
tz_input_test(
|
||||
"Abc/Xyz",
|
||||
Err("Failed to parse `Tz`: received invalid timezone"),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number() {
|
||||
tz_input_test("8086", None);
|
||||
tz_input_test(
|
||||
"8086",
|
||||
Err("Failed to parse `Tz`: received invalid timezone"),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_forward_slash() {
|
||||
tz_input_test("AbcXyz", None);
|
||||
tz_input_test(
|
||||
"AbcXyz",
|
||||
Err("Failed to parse `Tz`: received invalid timezone"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,10 @@ where
|
|||
Value::scalar(self.as_str().to_owned())
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<Url> {
|
||||
v.as_string_value().and_then(|s| Url::parse(s).ok())
|
||||
fn from_input_value(v: &InputValue) -> Result<Url, String> {
|
||||
v.as_string_value()
|
||||
.ok_or_else(|| format!("Expected `String`, found: {}", v))
|
||||
.and_then(|s| Url::parse(s).map_err(|e| format!("Failed to parse `Url`: {}", e)))
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||
|
|
|
@ -19,8 +19,10 @@ where
|
|||
Value::scalar(self.to_string())
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<Uuid> {
|
||||
v.as_string_value().and_then(|s| Uuid::parse_str(s).ok())
|
||||
fn from_input_value(v: &InputValue) -> Result<Uuid, String> {
|
||||
v.as_string_value()
|
||||
.ok_or_else(|| format!("Expected `String`, found: {}", v))
|
||||
.and_then(|s| Uuid::parse_str(s).map_err(|e| format!("Failed to parse `Uuid`: {}", e)))
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||
|
|
|
@ -116,8 +116,9 @@ pub use juniper_codegen::{
|
|||
GraphQLEnum, GraphQLInputObject, GraphQLObject, GraphQLScalarValue, GraphQLUnion,
|
||||
};
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
pub mod macros;
|
||||
mod ast;
|
||||
pub mod executor;
|
||||
mod introspection;
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
|
||||
pub mod subscription;
|
||||
|
||||
use crate::{DefaultScalarValue, DynGraphQLValue, DynGraphQLValueAsync, ScalarValue};
|
||||
use std::fmt;
|
||||
|
||||
use futures::future::{self, BoxFuture};
|
||||
|
||||
use crate::{DefaultScalarValue, DynGraphQLValue, DynGraphQLValueAsync, FieldError, ScalarValue};
|
||||
|
||||
/// Conversion of a [`GraphQLValue`] to its [trait object][1].
|
||||
///
|
||||
|
@ -32,3 +36,50 @@ pub trait AsDynGraphQLValue<S: ScalarValue = DefaultScalarValue> {
|
|||
}
|
||||
|
||||
crate::sa::assert_obj_safe!(AsDynGraphQLValue<Context = (), TypeInfo = ()>);
|
||||
|
||||
/// This trait is used by [`graphql_scalar!`] macro to retrieve [`Error`] type
|
||||
/// from a [`Result`].
|
||||
///
|
||||
/// [`Error`]: Result::Error
|
||||
/// [`graphql_scalar!`]: macro@crate::graphql_scalar
|
||||
pub trait ExtractError {
|
||||
/// Extracted [`Error`] type of this [`Result`].
|
||||
///
|
||||
/// [`Error`]: Result::Error
|
||||
type Error;
|
||||
}
|
||||
|
||||
impl<T, E> ExtractError for Result<T, E> {
|
||||
type Error = E;
|
||||
}
|
||||
|
||||
/// Wraps `msg` with [`Display`] implementation into opaque [`Send`] [`Future`]
|
||||
/// which immediately resolves into [`FieldError`].
|
||||
pub fn err_fut<'ok, D, Ok, S>(msg: D) -> BoxFuture<'ok, Result<Ok, FieldError<S>>>
|
||||
where
|
||||
D: fmt::Display,
|
||||
Ok: Send + 'ok,
|
||||
S: Send + 'static,
|
||||
{
|
||||
Box::pin(future::err(FieldError::from(msg)))
|
||||
}
|
||||
|
||||
/// Generates a [`FieldError`] for the given Rust type expecting to have
|
||||
/// [`GraphQLType::name`].
|
||||
///
|
||||
/// [`GraphQLType::name`]: crate::GraphQLType::name
|
||||
pub fn err_unnamed_type<S>(name: &str) -> FieldError<S> {
|
||||
FieldError::from(format!(
|
||||
"Expected `{}` type to implement `GraphQLType::name`",
|
||||
name,
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns a [`future::err`] wrapping the [`err_unnamed_type`].
|
||||
pub fn err_unnamed_type_fut<'ok, Ok, S>(name: &str) -> BoxFuture<'ok, Result<Ok, FieldError<S>>>
|
||||
where
|
||||
Ok: Send + 'ok,
|
||||
S: Send + 'static,
|
||||
{
|
||||
Box::pin(future::err(err_unnamed_type(name)))
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//! Declarative macros and helper definitions for procedural macros.
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod helper;
|
||||
|
||||
#[macro_use]
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
},
|
||||
types::scalars::{EmptyMutation, EmptySubscription},
|
||||
value::{DefaultScalarValue, ParseScalarValue, ScalarValue},
|
||||
GraphQLEnum, GraphQLInputObject,
|
||||
GraphQLEnum, GraphQLInputObject, IntoFieldError,
|
||||
};
|
||||
|
||||
#[derive(GraphQLEnum)]
|
||||
|
@ -52,9 +52,10 @@ impl Query {
|
|||
}
|
||||
}
|
||||
|
||||
fn scalar_meta<T>(name: &'static str) -> MetaType<DefaultScalarValue>
|
||||
fn scalar_meta<T>(name: &'static str) -> MetaType
|
||||
where
|
||||
T: FromInputValue<DefaultScalarValue> + ParseScalarValue<DefaultScalarValue> + 'static,
|
||||
T: FromInputValue<DefaultScalarValue> + ParseScalarValue<DefaultScalarValue>,
|
||||
T::Error: IntoFieldError,
|
||||
{
|
||||
MetaType::Scalar(ScalarMeta::new::<T>(name.into()))
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//! Types used to describe a `GraphQL` schema
|
||||
|
||||
use juniper::IntoFieldError;
|
||||
use std::{
|
||||
borrow::{Cow, ToOwned},
|
||||
fmt,
|
||||
|
@ -10,7 +11,8 @@ use crate::{
|
|||
parser::{ParseError, ScalarToken},
|
||||
schema::model::SchemaType,
|
||||
types::base::TypeKind,
|
||||
value::{DefaultScalarValue, ParseScalarValue, ScalarValue},
|
||||
value::{DefaultScalarValue, ParseScalarValue},
|
||||
FieldError,
|
||||
};
|
||||
|
||||
/// Whether an item is deprecated, with context.
|
||||
|
@ -46,7 +48,7 @@ pub struct ScalarMeta<'a, S> {
|
|||
pub name: Cow<'a, str>,
|
||||
#[doc(hidden)]
|
||||
pub description: Option<String>,
|
||||
pub(crate) try_parse_fn: for<'b> fn(&'b InputValue<S>) -> bool,
|
||||
pub(crate) try_parse_fn: for<'b> fn(&'b InputValue<S>) -> Result<(), FieldError<S>>,
|
||||
pub(crate) parse_fn: for<'b> fn(ScalarToken<'b>) -> Result<S, ParseError<'b>>,
|
||||
}
|
||||
|
||||
|
@ -88,7 +90,7 @@ pub struct EnumMeta<'a, S> {
|
|||
pub description: Option<String>,
|
||||
#[doc(hidden)]
|
||||
pub values: Vec<EnumValue>,
|
||||
pub(crate) try_parse_fn: for<'b> fn(&'b InputValue<S>) -> bool,
|
||||
pub(crate) try_parse_fn: for<'b> fn(&'b InputValue<S>) -> Result<(), FieldError<S>>,
|
||||
}
|
||||
|
||||
/// Interface type metadata
|
||||
|
@ -121,7 +123,7 @@ pub struct InputObjectMeta<'a, S> {
|
|||
pub description: Option<String>,
|
||||
#[doc(hidden)]
|
||||
pub input_fields: Vec<Argument<'a, S>>,
|
||||
pub(crate) try_parse_fn: for<'b> fn(&'b InputValue<S>) -> bool,
|
||||
pub(crate) try_parse_fn: for<'b> fn(&'b InputValue<S>) -> Result<(), FieldError<S>>,
|
||||
}
|
||||
|
||||
/// A placeholder for not-yet-registered types
|
||||
|
@ -323,7 +325,9 @@ impl<'a, S> MetaType<'a, S> {
|
|||
/// `true` if it can be parsed as the provided type.
|
||||
///
|
||||
/// Only scalars, enums, and input objects have parse functions.
|
||||
pub fn input_value_parse_fn(&self) -> Option<for<'b> fn(&'b InputValue<S>) -> bool> {
|
||||
pub fn input_value_parse_fn(
|
||||
&self,
|
||||
) -> Option<for<'b> fn(&'b InputValue<S>) -> Result<(), FieldError<S>>> {
|
||||
match *self {
|
||||
MetaType::Scalar(ScalarMeta {
|
||||
ref try_parse_fn, ..
|
||||
|
@ -407,16 +411,14 @@ impl<'a, S> MetaType<'a, S> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, S> ScalarMeta<'a, S>
|
||||
where
|
||||
S: ScalarValue + 'a,
|
||||
{
|
||||
/// Build a new scalar type metadata with the specified name
|
||||
impl<'a, S> ScalarMeta<'a, S> {
|
||||
/// Builds a new [`ScalarMeta`] type with the specified `name`.
|
||||
pub fn new<T>(name: Cow<'a, str>) -> Self
|
||||
where
|
||||
T: FromInputValue<S> + ParseScalarValue<S> + 'a,
|
||||
T: FromInputValue<S> + ParseScalarValue<S>,
|
||||
T::Error: IntoFieldError<S>,
|
||||
{
|
||||
ScalarMeta {
|
||||
Self {
|
||||
name,
|
||||
description: None,
|
||||
try_parse_fn: try_parse_fn::<S, T>,
|
||||
|
@ -424,22 +426,25 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Set the description for the given scalar type
|
||||
/// Sets the `description` of this [`ScalarMeta`] type.
|
||||
///
|
||||
/// If a description already was set prior to calling this method, it will be overwritten.
|
||||
pub fn description(mut self, description: &str) -> ScalarMeta<'a, S> {
|
||||
/// Overwrites any previously set description.
|
||||
pub fn description(mut self, description: &str) -> Self {
|
||||
self.description = Some(description.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Wrap the scalar in a generic meta type
|
||||
/// Wraps this [`ScalarMeta`] type into a generic [`MetaType`].
|
||||
pub fn into_meta(self) -> MetaType<'a, S> {
|
||||
MetaType::Scalar(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ListMeta<'a> {
|
||||
/// Build a new list type by wrapping the specified type
|
||||
/// Build a new [`ListMeta`] type by wrapping the specified [`Type`].
|
||||
///
|
||||
/// Specifying `expected_size` will be used to ensure that values of this
|
||||
/// type will always match it.
|
||||
pub fn new(of_type: Type<'a>, expected_size: Option<usize>) -> Self {
|
||||
Self {
|
||||
of_type,
|
||||
|
@ -447,31 +452,31 @@ impl<'a> ListMeta<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Wrap the list in a generic meta type
|
||||
/// Wraps this [`ListMeta`] type into a generic [`MetaType`].
|
||||
pub fn into_meta<S>(self) -> MetaType<'a, S> {
|
||||
MetaType::List(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> NullableMeta<'a> {
|
||||
/// Build a new nullable type by wrapping the specified type
|
||||
pub fn new(of_type: Type<'a>) -> NullableMeta<'a> {
|
||||
NullableMeta { of_type }
|
||||
/// Build a new [`NullableMeta`] type by wrapping the specified [`Type`].
|
||||
pub fn new(of_type: Type<'a>) -> Self {
|
||||
Self { of_type }
|
||||
}
|
||||
|
||||
/// Wrap the nullable type in a generic meta type
|
||||
/// Wraps this [`NullableMeta`] type into a generic [`MetaType`].
|
||||
pub fn into_meta<S>(self) -> MetaType<'a, S> {
|
||||
MetaType::Nullable(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S> ObjectMeta<'a, S>
|
||||
where
|
||||
S: ScalarValue,
|
||||
{
|
||||
/// Build a new object type with the specified name and fields
|
||||
pub fn new(name: Cow<'a, str>, fields: &[Field<'a, S>]) -> Self {
|
||||
ObjectMeta {
|
||||
impl<'a, S> ObjectMeta<'a, S> {
|
||||
/// Build a new [`ObjectMeta`] type with the specified `name` and `fields`.
|
||||
pub fn new(name: Cow<'a, str>, fields: &[Field<'a, S>]) -> Self
|
||||
where
|
||||
S: Clone,
|
||||
{
|
||||
Self {
|
||||
name,
|
||||
description: None,
|
||||
fields: fields.to_vec(),
|
||||
|
@ -479,19 +484,18 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Set the description for the object
|
||||
/// Sets the `description` of this [`ObjectMeta`] type.
|
||||
///
|
||||
/// If a description was provided prior to calling this method, it will be overwritten.
|
||||
pub fn description(mut self, description: &str) -> ObjectMeta<'a, S> {
|
||||
/// Overwrites any previously set description.
|
||||
pub fn description(mut self, description: &str) -> Self {
|
||||
self.description = Some(description.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the interfaces this type implements
|
||||
/// Set the `interfaces` this [`ObjectMeta`] type implements.
|
||||
///
|
||||
/// If a list of interfaces already was provided prior to calling this method, they will be
|
||||
/// overwritten.
|
||||
pub fn interfaces(mut self, interfaces: &[Type<'a>]) -> ObjectMeta<'a, S> {
|
||||
/// Overwrites any previously set list of interfaces.
|
||||
pub fn interfaces(mut self, interfaces: &[Type<'a>]) -> Self {
|
||||
self.interface_names = interfaces
|
||||
.iter()
|
||||
.map(|t| t.innermost_name().to_owned())
|
||||
|
@ -499,74 +503,75 @@ where
|
|||
self
|
||||
}
|
||||
|
||||
/// Wrap this object type in a generic meta type
|
||||
/// Wraps this [`ObjectMeta`] type into a generic [`MetaType`].
|
||||
pub fn into_meta(self) -> MetaType<'a, S> {
|
||||
MetaType::Object(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S> EnumMeta<'a, S>
|
||||
where
|
||||
S: ScalarValue + 'a,
|
||||
{
|
||||
/// Build a new enum type with the specified name and possible values
|
||||
impl<'a, S> EnumMeta<'a, S> {
|
||||
/// Build a new [`EnumMeta`] type with the specified `name` and possible
|
||||
/// `values`.
|
||||
pub fn new<T>(name: Cow<'a, str>, values: &[EnumValue]) -> Self
|
||||
where
|
||||
T: FromInputValue<S>,
|
||||
T::Error: IntoFieldError<S>,
|
||||
{
|
||||
EnumMeta {
|
||||
Self {
|
||||
name,
|
||||
description: None,
|
||||
values: values.to_vec(),
|
||||
values: values.to_owned(),
|
||||
try_parse_fn: try_parse_fn::<S, T>,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the description of the type
|
||||
/// Sets the `description` of this [`EnumMeta`] type.
|
||||
///
|
||||
/// If a description was provided prior to calling this method, it will be overwritten
|
||||
pub fn description(mut self, description: &str) -> EnumMeta<'a, S> {
|
||||
/// Overwrites any previously set description.
|
||||
pub fn description(mut self, description: &str) -> Self {
|
||||
self.description = Some(description.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Wrap this enum type in a generic meta type
|
||||
/// Wraps this [`EnumMeta`] type into a generic [`MetaType`].
|
||||
pub fn into_meta(self) -> MetaType<'a, S> {
|
||||
MetaType::Enum(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S> InterfaceMeta<'a, S>
|
||||
where
|
||||
S: ScalarValue,
|
||||
{
|
||||
/// Build a new interface type with the specified name and fields
|
||||
pub fn new(name: Cow<'a, str>, fields: &[Field<'a, S>]) -> InterfaceMeta<'a, S> {
|
||||
InterfaceMeta {
|
||||
impl<'a, S> InterfaceMeta<'a, S> {
|
||||
/// Builds a new [`InterfaceMeta`] type with the specified `name` and
|
||||
/// `fields`.
|
||||
pub fn new(name: Cow<'a, str>, fields: &[Field<'a, S>]) -> Self
|
||||
where
|
||||
S: Clone,
|
||||
{
|
||||
Self {
|
||||
name,
|
||||
description: None,
|
||||
fields: fields.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the description of the type
|
||||
/// Sets the `description` of this [`InterfaceMeta`] type.
|
||||
///
|
||||
/// If a description was provided prior to calling this method, it will be overwritten.
|
||||
pub fn description(mut self, description: &str) -> InterfaceMeta<'a, S> {
|
||||
/// Overwrites any previously set description.
|
||||
pub fn description(mut self, description: &str) -> Self {
|
||||
self.description = Some(description.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Wrap this interface type in a generic meta type
|
||||
/// Wraps this [`InterfaceMeta`] type into a generic [`MetaType`].
|
||||
pub fn into_meta(self) -> MetaType<'a, S> {
|
||||
MetaType::Interface(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> UnionMeta<'a> {
|
||||
/// Build a new union type with the specified name and possible types
|
||||
pub fn new(name: Cow<'a, str>, of_types: &[Type]) -> UnionMeta<'a> {
|
||||
UnionMeta {
|
||||
/// Build a new [`UnionMeta`] type with the specified `name` and possible
|
||||
/// [`Type`]s.
|
||||
pub fn new(name: Cow<'a, str>, of_types: &[Type]) -> Self {
|
||||
Self {
|
||||
name,
|
||||
description: None,
|
||||
of_type_names: of_types
|
||||
|
@ -576,30 +581,30 @@ impl<'a> UnionMeta<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Set the description of the type
|
||||
/// Sets the `description` of this [`UnionMeta`] type.
|
||||
///
|
||||
/// If a description was provided prior to calling this method, it will be overwritten.
|
||||
pub fn description(mut self, description: &str) -> UnionMeta<'a> {
|
||||
/// Overwrites any previously set description.
|
||||
pub fn description(mut self, description: &str) -> Self {
|
||||
self.description = Some(description.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Wrap this union type in a generic meta type
|
||||
/// Wraps this [`UnionMeta`] type into a generic [`MetaType`].
|
||||
pub fn into_meta<S>(self) -> MetaType<'a, S> {
|
||||
MetaType::Union(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S> InputObjectMeta<'a, S>
|
||||
where
|
||||
S: ScalarValue,
|
||||
{
|
||||
/// Build a new input type with the specified name and input fields
|
||||
impl<'a, S> InputObjectMeta<'a, S> {
|
||||
/// Builds a new [`InputObjectMeta`] type with the specified `name` and
|
||||
/// `input_fields`.
|
||||
pub fn new<T>(name: Cow<'a, str>, input_fields: &[Argument<'a, S>]) -> Self
|
||||
where
|
||||
T: FromInputValue<S> + ?Sized,
|
||||
T: FromInputValue<S>,
|
||||
T::Error: IntoFieldError<S>,
|
||||
S: Clone,
|
||||
{
|
||||
InputObjectMeta {
|
||||
Self {
|
||||
name,
|
||||
description: None,
|
||||
input_fields: input_fields.to_vec(),
|
||||
|
@ -607,30 +612,30 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Set the description of the type
|
||||
/// Set the `description` of this [`InputObjectMeta`] type.
|
||||
///
|
||||
/// If a description was provided prior to calling this method, it will be overwritten.
|
||||
pub fn description(mut self, description: &str) -> InputObjectMeta<'a, S> {
|
||||
/// Overwrites any previously set description.
|
||||
pub fn description(mut self, description: &str) -> Self {
|
||||
self.description = Some(description.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Wrap this union type in a generic meta type
|
||||
/// Wraps this [`InputObjectMeta`] type into a generic [`MetaType`].
|
||||
pub fn into_meta(self) -> MetaType<'a, S> {
|
||||
MetaType::InputObject(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S> Field<'a, S> {
|
||||
/// Set the description of the field
|
||||
/// Set the `description` of this [`Field`].
|
||||
///
|
||||
/// This overwrites the description if any was previously set.
|
||||
/// Overwrites any previously set description.
|
||||
pub fn description(mut self, description: &str) -> Self {
|
||||
self.description = Some(description.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Add an argument to the field
|
||||
/// Adds an `argument` to this [`Field`].
|
||||
///
|
||||
/// Arguments are unordered and can't contain duplicates by name.
|
||||
pub fn argument(mut self, argument: Argument<'a, S>) -> Self {
|
||||
|
@ -642,13 +647,12 @@ impl<'a, S> Field<'a, S> {
|
|||
args.push(argument);
|
||||
}
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the field to be deprecated with an optional reason.
|
||||
/// Sets this [`Field`] as deprecated with an optional `reason`.
|
||||
///
|
||||
/// This overwrites the deprecation reason if any was previously set.
|
||||
/// Overwrites any previously set deprecation reason.
|
||||
pub fn deprecated(mut self, reason: Option<&str>) -> Self {
|
||||
self.deprecation_status = DeprecationStatus::Deprecated(reason.map(ToOwned::to_owned));
|
||||
self
|
||||
|
@ -656,7 +660,7 @@ impl<'a, S> Field<'a, S> {
|
|||
}
|
||||
|
||||
impl<'a, S> Argument<'a, S> {
|
||||
#[doc(hidden)]
|
||||
/// Builds a new [`Argument`] of the given [`Type`] with the given `name`.
|
||||
pub fn new(name: &str, arg_type: Type<'a>) -> Self {
|
||||
Self {
|
||||
name: name.to_owned(),
|
||||
|
@ -666,44 +670,44 @@ impl<'a, S> Argument<'a, S> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Set the description of the argument
|
||||
/// Sets the `description` of this [`Argument`].
|
||||
///
|
||||
/// This overwrites the description if any was previously set.
|
||||
/// Overwrites any previously set description.
|
||||
pub fn description(mut self, description: &str) -> Self {
|
||||
self.description = Some(description.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the default value of the argument
|
||||
/// Set the default value of this [`Argument`].
|
||||
///
|
||||
/// This overwrites the default value if any was previously set.
|
||||
pub fn default_value(mut self, default_value: InputValue<S>) -> Self {
|
||||
self.default_value = Some(default_value);
|
||||
/// Overwrites any previously set default value.
|
||||
pub fn default_value(mut self, val: InputValue<S>) -> Self {
|
||||
self.default_value = Some(val);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl EnumValue {
|
||||
/// Construct a new enum value with the provided name
|
||||
pub fn new(name: &str) -> EnumValue {
|
||||
EnumValue {
|
||||
/// Constructs a new [`EnumValue`] with the provided `name`.
|
||||
pub fn new(name: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_owned(),
|
||||
description: None,
|
||||
deprecation_status: DeprecationStatus::Current,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the description of the enum value
|
||||
/// Sets the `description` of this [`EnumValue`].
|
||||
///
|
||||
/// This overwrites the description if any was previously set.
|
||||
pub fn description(mut self, description: &str) -> EnumValue {
|
||||
/// Overwrites any previously set description.
|
||||
pub fn description(mut self, description: &str) -> Self {
|
||||
self.description = Some(description.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the enum value to be deprecated with an optional reason.
|
||||
/// Sets this [`EnumValue`] as deprecated with an optional `reason`.
|
||||
///
|
||||
/// This overwrites the deprecation reason if any was previously set.
|
||||
/// Overwrites any previously set deprecation reason.
|
||||
pub fn deprecated(mut self, reason: Option<&str>) -> Self {
|
||||
self.deprecation_status = DeprecationStatus::Deprecated(reason.map(ToOwned::to_owned));
|
||||
self
|
||||
|
@ -739,9 +743,12 @@ impl<'a, S: fmt::Debug> fmt::Debug for InputObjectMeta<'a, S> {
|
|||
}
|
||||
}
|
||||
|
||||
fn try_parse_fn<S, T>(v: &InputValue<S>) -> bool
|
||||
fn try_parse_fn<S, T>(v: &InputValue<S>) -> Result<(), FieldError<S>>
|
||||
where
|
||||
T: FromInputValue<S>,
|
||||
T::Error: IntoFieldError<S>,
|
||||
{
|
||||
<T as FromInputValue<S>>::from_input_value(v).is_some()
|
||||
T::from_input_value(v)
|
||||
.map(drop)
|
||||
.map_err(T::Error::into_field_error)
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ where
|
|||
.replaced_context(&self.schema)
|
||||
.resolve(&(), &self.schema),
|
||||
"__type" => {
|
||||
let type_name: String = args.get("name").unwrap();
|
||||
let type_name: String = args.get("name")?.unwrap();
|
||||
executor
|
||||
.replaced_context(&self.schema)
|
||||
.resolve(&(), &self.schema.type_by_name(&type_name))
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
parser::Spanning,
|
||||
schema::meta::{Argument, MetaType},
|
||||
value::{DefaultScalarValue, Object, ScalarValue, Value},
|
||||
GraphQLEnum,
|
||||
FieldResult, GraphQLEnum, IntoFieldError,
|
||||
};
|
||||
|
||||
/// GraphQL type kind
|
||||
|
@ -73,47 +73,55 @@ pub struct Arguments<'a, S = DefaultScalarValue> {
|
|||
args: Option<IndexMap<&'a str, InputValue<S>>>,
|
||||
}
|
||||
|
||||
impl<'a, S> Arguments<'a, S>
|
||||
where
|
||||
S: ScalarValue,
|
||||
{
|
||||
impl<'a, S> Arguments<'a, S> {
|
||||
#[doc(hidden)]
|
||||
pub fn new(
|
||||
mut args: Option<IndexMap<&'a str, InputValue<S>>>,
|
||||
meta_args: &'a Option<Vec<Argument<S>>>,
|
||||
) -> Self {
|
||||
) -> Self
|
||||
where
|
||||
S: Clone,
|
||||
{
|
||||
if meta_args.is_some() && args.is_none() {
|
||||
args = Some(IndexMap::new());
|
||||
}
|
||||
|
||||
if let (&mut Some(ref mut args), &Some(ref meta_args)) = (&mut args, meta_args) {
|
||||
if let (Some(args), Some(meta_args)) = (&mut args, meta_args) {
|
||||
for arg in meta_args {
|
||||
if !args.contains_key(arg.name.as_str()) || args[arg.name.as_str()].is_null() {
|
||||
if let Some(ref default_value) = arg.default_value {
|
||||
args.insert(arg.name.as_str(), default_value.clone());
|
||||
let arg_name = arg.name.as_str();
|
||||
if args.get(arg_name).map_or(true, InputValue::is_null) {
|
||||
if let Some(val) = arg.default_value.as_ref() {
|
||||
args.insert(arg_name, val.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Arguments { args }
|
||||
Self { args }
|
||||
}
|
||||
|
||||
/// Get and convert an argument into the desired type.
|
||||
/// Gets an argument by the given `name` and converts it into the desired
|
||||
/// type.
|
||||
///
|
||||
/// If the argument is found, or a default argument has been provided,
|
||||
/// the `InputValue` will be converted into the type `T`.
|
||||
/// If the argument is found, or a default argument has been provided, the
|
||||
/// given [`InputValue`] will be converted into the type `T`.
|
||||
///
|
||||
/// Returns `Some` if the argument is present _and_ type conversion
|
||||
/// succeeds.
|
||||
pub fn get<T>(&self, key: &str) -> Option<T>
|
||||
/// Returns [`None`] if an argument with such `name` is not present.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If the [`FromInputValue`] conversion fails.
|
||||
pub fn get<T>(&self, name: &str) -> FieldResult<Option<T>, S>
|
||||
where
|
||||
T: FromInputValue<S>,
|
||||
T::Error: IntoFieldError<S>,
|
||||
{
|
||||
self.args
|
||||
.as_ref()
|
||||
.and_then(|args| args.get(key))
|
||||
.and_then(InputValue::convert)
|
||||
.and_then(|args| args.get(name))
|
||||
.map(InputValue::convert)
|
||||
.transpose()
|
||||
.map_err(IntoFieldError::into_field_error)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::{
|
|||
|
||||
use crate::{
|
||||
ast::{FromInputValue, InputValue, Selection, ToInputValue},
|
||||
executor::{ExecutionResult, Executor, Registry},
|
||||
executor::{ExecutionResult, Executor, FieldError, IntoFieldError, Registry},
|
||||
schema::meta::MetaType,
|
||||
types::{
|
||||
async_await::GraphQLValueAsync,
|
||||
|
@ -80,28 +80,22 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<S, T> FromInputValue<S> for Option<T>
|
||||
where
|
||||
T: FromInputValue<S>,
|
||||
S: ScalarValue,
|
||||
{
|
||||
fn from_input_value(v: &InputValue<S>) -> Option<Self> {
|
||||
impl<S, T: FromInputValue<S>> FromInputValue<S> for Option<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
fn from_input_value(v: &InputValue<S>) -> Result<Self, Self::Error> {
|
||||
match v {
|
||||
&InputValue::Null => Some(None),
|
||||
InputValue::Null => Ok(None),
|
||||
v => v.convert().map(Some),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, T> ToInputValue<S> for Option<T>
|
||||
where
|
||||
T: ToInputValue<S>,
|
||||
S: ScalarValue,
|
||||
{
|
||||
impl<S, T: ToInputValue<S>> ToInputValue<S> for Option<T> {
|
||||
fn to_input_value(&self) -> InputValue<S> {
|
||||
match *self {
|
||||
Some(ref v) => v.to_input_value(),
|
||||
None => InputValue::null(),
|
||||
match self {
|
||||
Some(v) => v.to_input_value(),
|
||||
None => InputValue::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -163,18 +157,17 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T, S> FromInputValue<S> for Vec<T>
|
||||
where
|
||||
T: FromInputValue<S>,
|
||||
S: ScalarValue,
|
||||
{
|
||||
fn from_input_value(v: &InputValue<S>) -> Option<Self> {
|
||||
match *v {
|
||||
InputValue::List(ref ls) => {
|
||||
let v: Vec<_> = ls.iter().filter_map(|i| i.item.convert()).collect();
|
||||
(v.len() == ls.len()).then(|| v)
|
||||
}
|
||||
ref other => other.convert().map(|e| vec![e]),
|
||||
impl<S, T: FromInputValue<S>> FromInputValue<S> for Vec<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
fn from_input_value(v: &InputValue<S>) -> Result<Self, Self::Error> {
|
||||
match v {
|
||||
InputValue::List(l) => l.iter().map(|i| i.item.convert()).collect(),
|
||||
// See "Input Coercion" on List types:
|
||||
// https://spec.graphql.org/June2018/#sec-Type-System.List
|
||||
// In reality is intercepted by `Option`.
|
||||
InputValue::Null => Ok(Vec::new()),
|
||||
other => other.convert().map(|e| vec![e]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -318,7 +311,9 @@ where
|
|||
T: FromInputValue<S>,
|
||||
S: ScalarValue,
|
||||
{
|
||||
fn from_input_value(v: &InputValue<S>) -> Option<Self> {
|
||||
type Error = FromInputValueArrayError<T, S>;
|
||||
|
||||
fn from_input_value(v: &InputValue<S>) -> Result<Self, Self::Error> {
|
||||
struct PartiallyInitializedArray<T, const N: usize> {
|
||||
arr: [MaybeUninit<T>; N],
|
||||
init_len: usize,
|
||||
|
@ -345,6 +340,22 @@ where
|
|||
|
||||
match *v {
|
||||
InputValue::List(ref ls) => {
|
||||
if ls.len() != N {
|
||||
return Err(FromInputValueArrayError::WrongCount {
|
||||
actual: ls.len(),
|
||||
expected: N,
|
||||
});
|
||||
}
|
||||
if N == 0 {
|
||||
// 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
|
||||
// check `N` to be `0`. It's no-op, actually.
|
||||
return Ok(unsafe { mem::transmute_copy::<[T; 0], Self>(&[]) });
|
||||
}
|
||||
|
||||
// SAFETY: The reason we're using a wrapper struct implementing
|
||||
// `Drop` here is to be panic safe:
|
||||
// `T: FromInputValue<S>` implementation is not
|
||||
|
@ -363,20 +374,17 @@ where
|
|||
no_drop: false,
|
||||
};
|
||||
|
||||
let mut items = ls.iter().filter_map(|i| i.item.convert());
|
||||
let mut items = ls.iter().map(|i| i.item.convert());
|
||||
for elem in &mut out.arr[..] {
|
||||
if let Some(i) = items.next() {
|
||||
if let Some(i) = items
|
||||
.next()
|
||||
.transpose()
|
||||
.map_err(FromInputValueArrayError::Scalar)?
|
||||
{
|
||||
*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.
|
||||
|
@ -391,29 +399,48 @@ where
|
|||
// 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) })
|
||||
Ok(unsafe { mem::transmute_copy::<_, Self>(&out.arr) })
|
||||
}
|
||||
// See "Input Coercion" on List types:
|
||||
// https://spec.graphql.org/June2018/#sec-Type-System.List
|
||||
// In reality is intercepted by `Option`.
|
||||
InputValue::Null if N == 0 => {
|
||||
// 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 check
|
||||
// `N` to be `0`. It's no-op, actually.
|
||||
Ok(unsafe { mem::transmute_copy::<[T; 0], Self>(&[]) })
|
||||
}
|
||||
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
|
||||
}
|
||||
})
|
||||
other
|
||||
.convert()
|
||||
.map_err(FromInputValueArrayError::Scalar)
|
||||
.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`.
|
||||
Ok(unsafe {
|
||||
mem::transmute_copy::<_, Self>(&[mem::ManuallyDrop::new(e)])
|
||||
})
|
||||
} else {
|
||||
Err(FromInputValueArrayError::WrongCount {
|
||||
actual: 1,
|
||||
expected: N,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -429,6 +456,44 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Error converting [`InputValue`] into exact-size [`array`](prim@array).
|
||||
pub enum FromInputValueArrayError<T, S>
|
||||
where
|
||||
T: FromInputValue<S>,
|
||||
S: ScalarValue,
|
||||
{
|
||||
/// Wrong count of elements.
|
||||
WrongCount {
|
||||
/// Actual count of elements.
|
||||
actual: usize,
|
||||
|
||||
/// Expected count of elements.
|
||||
expected: usize,
|
||||
},
|
||||
|
||||
/// Underlying [`ScalarValue`] conversion error.
|
||||
Scalar(T::Error),
|
||||
}
|
||||
|
||||
impl<T, S> IntoFieldError<S> for FromInputValueArrayError<T, S>
|
||||
where
|
||||
T: FromInputValue<S>,
|
||||
T::Error: IntoFieldError<S>,
|
||||
S: ScalarValue,
|
||||
{
|
||||
fn into_field_error(self) -> FieldError<S> {
|
||||
const ERROR_PREFIX: &str = "Failed to convert into exact-size array";
|
||||
match self {
|
||||
Self::WrongCount { actual, expected } => format!(
|
||||
"{}: wrong elements count: {} instead of {}",
|
||||
ERROR_PREFIX, actual, expected
|
||||
)
|
||||
.into(),
|
||||
Self::Scalar(s) => s.into_field_error(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_into_list<'t, S, T, I>(
|
||||
executor: &Executor<T::Context, S>,
|
||||
info: &T::TypeInfo,
|
||||
|
@ -492,3 +557,19 @@ where
|
|||
|
||||
Ok(Value::list(values))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod coercion {
|
||||
use crate::{graphql_input_value, FromInputValue as _, InputValue};
|
||||
|
||||
type V = InputValue;
|
||||
|
||||
// See "Input Coercion" examples on List types:
|
||||
// https://spec.graphql.org/June2018/#sec-Type-System.List
|
||||
#[test]
|
||||
fn vec() {
|
||||
let v: V = graphql_input_value!([1, 2, 3]);
|
||||
assert_eq!(<Vec<i32>>::from_input_value(&v), Ok(vec![1, 2, 3]),);
|
||||
// TODO: all examples
|
||||
}
|
||||
}
|
||||
|
|
|
@ -278,20 +278,18 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<S, T> FromInputValue<S> for Nullable<T>
|
||||
where
|
||||
T: FromInputValue<S>,
|
||||
S: ScalarValue,
|
||||
{
|
||||
fn from_input_value(v: &InputValue<S>) -> Option<Nullable<T>> {
|
||||
impl<S, T: FromInputValue<S>> FromInputValue<S> for Nullable<T> {
|
||||
type Error = <T as FromInputValue<S>>::Error;
|
||||
|
||||
fn from_input_value(v: &InputValue<S>) -> Result<Self, Self::Error> {
|
||||
match v {
|
||||
&InputValue::Null => Some(Self::ExplicitNull),
|
||||
&InputValue::Null => Ok(Self::ExplicitNull),
|
||||
v => v.convert().map(Self::Some),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_implicit_null() -> Option<Self> {
|
||||
Some(Self::ImplicitNull)
|
||||
fn from_implicit_null() -> Result<Self, Self::Error> {
|
||||
Ok(Self::ImplicitNull)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -93,7 +93,9 @@ where
|
|||
S: ScalarValue,
|
||||
T: FromInputValue<S>,
|
||||
{
|
||||
fn from_input_value(v: &InputValue<S>) -> Option<Box<T>> {
|
||||
type Error = T::Error;
|
||||
|
||||
fn from_input_value(v: &InputValue<S>) -> Result<Box<T>, Self::Error> {
|
||||
<T as FromInputValue<S>>::from_input_value(v).map(Box::new)
|
||||
}
|
||||
}
|
||||
|
@ -285,7 +287,9 @@ where
|
|||
S: ScalarValue,
|
||||
T: FromInputValue<S>,
|
||||
{
|
||||
fn from_input_value(v: &InputValue<S>) -> Option<Arc<T>> {
|
||||
type Error = T::Error;
|
||||
|
||||
fn from_input_value(v: &InputValue<S>) -> Result<Arc<T>, Self::Error> {
|
||||
<T as FromInputValue<S>>::from_input_value(v).map(Arc::new)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,14 +59,12 @@ where
|
|||
Value::scalar(self.0.clone())
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<ID> {
|
||||
match *v {
|
||||
InputValue::Scalar(ref s) => s
|
||||
.as_string()
|
||||
.or_else(|| s.as_int().map(|i| i.to_string()))
|
||||
.map(ID),
|
||||
_ => None,
|
||||
}
|
||||
fn from_input_value(v: &InputValue) -> Result<ID, String> {
|
||||
v.as_string_value()
|
||||
.map(str::to_owned)
|
||||
.or_else(|| v.as_int_value().map(|i| i.to_string()))
|
||||
.map(ID)
|
||||
.ok_or_else(|| format!("Expected `String` or `Int`, found: {}", v))
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||
|
@ -86,11 +84,10 @@ where
|
|||
Value::scalar(self.clone())
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<String> {
|
||||
match *v {
|
||||
InputValue::Scalar(ref s) => s.as_string(),
|
||||
_ => None,
|
||||
}
|
||||
fn from_input_value(v: &InputValue) -> Result<String, String> {
|
||||
v.as_string_value()
|
||||
.map(str::to_owned)
|
||||
.ok_or_else(|| format!("Expected `String`, found: {}", v))
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||
|
@ -275,11 +272,10 @@ where
|
|||
Value::scalar(*self)
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<bool> {
|
||||
match *v {
|
||||
InputValue::Scalar(ref b) => b.as_boolean(),
|
||||
_ => None,
|
||||
}
|
||||
fn from_input_value(v: &InputValue) -> Result<bool, String> {
|
||||
v.as_scalar_value()
|
||||
.and_then(ScalarValue::as_boolean)
|
||||
.ok_or_else(|| format!("Expected `Boolean`, found: {}", v))
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||
|
@ -297,11 +293,9 @@ where
|
|||
Value::scalar(*self)
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<i32> {
|
||||
match *v {
|
||||
InputValue::Scalar(ref i) => i.as_int(),
|
||||
_ => None,
|
||||
}
|
||||
fn from_input_value(v: &InputValue) -> Result<i32, String> {
|
||||
v.as_int_value()
|
||||
.ok_or_else(|| format!("Expected `Int`, found: {}", v))
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||
|
@ -324,11 +318,9 @@ where
|
|||
Value::scalar(*self)
|
||||
}
|
||||
|
||||
fn from_input_value(v: &InputValue) -> Option<f64> {
|
||||
match *v {
|
||||
InputValue::Scalar(ref s) => s.as_float(),
|
||||
_ => None,
|
||||
}
|
||||
fn from_input_value(v: &InputValue) -> Result<f64, String> {
|
||||
v.as_float_value()
|
||||
.ok_or_else(|| format!("Expected `Float`, found: {}", v))
|
||||
}
|
||||
|
||||
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||
|
|
|
@ -57,7 +57,7 @@ where
|
|||
InputValue::Null | InputValue::Variable(_) => true,
|
||||
ref v @ InputValue::Scalar(_) | ref v @ InputValue::Enum(_) => {
|
||||
if let Some(parse_fn) = t.input_value_parse_fn() {
|
||||
parse_fn(v)
|
||||
parse_fn(v).is_ok()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
|
@ -163,14 +163,17 @@ where
|
|||
if e.is_empty() {
|
||||
// All the fields didn't have errors, see if there is an
|
||||
// overall error when parsing the input value.
|
||||
if !(iom.try_parse_fn)(value) {
|
||||
if let Err(e) = (iom.try_parse_fn)(value) {
|
||||
errors.push(unification_error(
|
||||
var_name,
|
||||
var_pos,
|
||||
&path,
|
||||
&format!(
|
||||
r#"Expected input of type "{}". Got: "{}""#,
|
||||
iom.name, value
|
||||
"Expected input of type `{}`. Got: `{}`. \
|
||||
Details: {}",
|
||||
iom.name,
|
||||
value,
|
||||
e.message(),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
@ -193,16 +196,21 @@ fn unify_scalar<'a, S>(
|
|||
path: &Path<'a>,
|
||||
) -> Vec<RuleError>
|
||||
where
|
||||
S: fmt::Debug,
|
||||
S: ScalarValue,
|
||||
{
|
||||
let mut errors: Vec<RuleError> = vec![];
|
||||
|
||||
if !(meta.try_parse_fn)(value) {
|
||||
if let Err(e) = (meta.try_parse_fn)(value) {
|
||||
return vec![unification_error(
|
||||
var_name,
|
||||
var_pos,
|
||||
path,
|
||||
&format!(r#"Expected "{}""#, meta.name),
|
||||
&format!(
|
||||
"Expected input scalar `{}`. Got: `{}`. Details: {}",
|
||||
meta.name,
|
||||
value,
|
||||
e.message(),
|
||||
),
|
||||
)];
|
||||
}
|
||||
|
||||
|
|
|
@ -208,12 +208,14 @@ impl<S> FromInputValue<S> for DogCommand
|
|||
where
|
||||
S: ScalarValue,
|
||||
{
|
||||
fn from_input_value<'a>(v: &InputValue<S>) -> Option<DogCommand> {
|
||||
type Error = &'static str;
|
||||
|
||||
fn from_input_value<'a>(v: &InputValue<S>) -> Result<DogCommand, Self::Error> {
|
||||
match v.as_enum_value() {
|
||||
Some("SIT") => Some(DogCommand::Sit),
|
||||
Some("HEEL") => Some(DogCommand::Heel),
|
||||
Some("DOWN") => Some(DogCommand::Down),
|
||||
_ => None,
|
||||
Some("SIT") => Ok(DogCommand::Sit),
|
||||
Some("HEEL") => Ok(DogCommand::Heel),
|
||||
Some("DOWN") => Ok(DogCommand::Down),
|
||||
_ => Err("Unknown DogCommand"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -314,13 +316,15 @@ impl<S> FromInputValue<S> for FurColor
|
|||
where
|
||||
S: ScalarValue,
|
||||
{
|
||||
fn from_input_value<'a>(v: &InputValue<S>) -> Option<FurColor> {
|
||||
type Error = &'static str;
|
||||
|
||||
fn from_input_value<'a>(v: &InputValue<S>) -> Result<FurColor, Self::Error> {
|
||||
match v.as_enum_value() {
|
||||
Some("BROWN") => Some(FurColor::Brown),
|
||||
Some("BLACK") => Some(FurColor::Black),
|
||||
Some("TAN") => Some(FurColor::Tan),
|
||||
Some("SPOTTED") => Some(FurColor::Spotted),
|
||||
_ => None,
|
||||
Some("BROWN") => Ok(FurColor::Brown),
|
||||
Some("BLACK") => Ok(FurColor::Black),
|
||||
Some("TAN") => Ok(FurColor::Tan),
|
||||
Some("SPOTTED") => Ok(FurColor::Spotted),
|
||||
_ => Err("Unknown FurColor"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -612,21 +616,37 @@ impl<S> FromInputValue<S> for ComplexInput
|
|||
where
|
||||
S: ScalarValue,
|
||||
{
|
||||
fn from_input_value<'a>(v: &InputValue<S>) -> Option<ComplexInput> {
|
||||
let obj = match v.to_object_value() {
|
||||
Some(o) => o,
|
||||
None => return None,
|
||||
};
|
||||
type Error = String;
|
||||
|
||||
Some(ComplexInput {
|
||||
required_field: match obj.get("requiredField").and_then(|v| v.convert()) {
|
||||
Some(f) => f,
|
||||
None => return None,
|
||||
},
|
||||
int_field: obj.get("intField").and_then(|v| v.convert()),
|
||||
string_field: obj.get("stringField").and_then(|v| v.convert()),
|
||||
boolean_field: obj.get("booleanField").and_then(|v| v.convert()),
|
||||
string_list_field: obj.get("stringListField").and_then(|v| v.convert()),
|
||||
fn from_input_value<'a>(v: &InputValue<S>) -> Result<ComplexInput, Self::Error> {
|
||||
let obj = v.to_object_value().ok_or("Expected object")?;
|
||||
|
||||
Ok(ComplexInput {
|
||||
required_field: obj
|
||||
.get("requiredField")
|
||||
.map(|v| v.convert())
|
||||
.transpose()?
|
||||
.ok_or("Expected requiredField")?,
|
||||
int_field: obj
|
||||
.get("intField")
|
||||
.map(|v| v.convert())
|
||||
.transpose()?
|
||||
.ok_or("Expected intField")?,
|
||||
string_field: obj
|
||||
.get("stringField")
|
||||
.map(|v| v.convert())
|
||||
.transpose()?
|
||||
.ok_or("Expected stringField")?,
|
||||
boolean_field: obj
|
||||
.get("booleanField")
|
||||
.map(|v| v.convert())
|
||||
.transpose()?
|
||||
.ok_or("Expected booleanField")?,
|
||||
string_list_field: obj
|
||||
.get("stringListField")
|
||||
.map(|v| v.convert())
|
||||
.transpose()?
|
||||
.ok_or("Expected stringListField")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ pub use self::{
|
|||
/// values or variables. Also, lists and objects do not contain any location
|
||||
/// information since they are generated by resolving fields and values rather
|
||||
/// than parsing a source query.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Value<S = DefaultScalarValue> {
|
||||
Null,
|
||||
|
|
|
@ -347,18 +347,34 @@ impl OnMethod {
|
|||
///
|
||||
/// [`GraphQLValue::resolve_field`]: juniper::GraphQLValue::resolve_field
|
||||
#[must_use]
|
||||
pub(crate) fn method_resolve_field_tokens(&self, scalar: &scalar::Type) -> TokenStream {
|
||||
pub(crate) fn method_resolve_field_tokens(
|
||||
&self,
|
||||
scalar: &scalar::Type,
|
||||
for_async: bool,
|
||||
) -> TokenStream {
|
||||
match self {
|
||||
Self::Regular(arg) => {
|
||||
let (name, ty) = (&arg.name, &arg.ty);
|
||||
let err_text = format!(
|
||||
"Internal error: missing argument `{}` - validation must have failed",
|
||||
&name,
|
||||
);
|
||||
quote! {
|
||||
args.get::<#ty>(#name)
|
||||
.or_else(::juniper::FromInputValue::<#scalar>::from_implicit_null)
|
||||
.expect(#err_text)
|
||||
let err_text = format!("Missing argument `{}`: {{}}", &name);
|
||||
|
||||
let arg = quote! {
|
||||
args.get::<#ty>(#name).and_then(|opt| opt.map_or_else(|| {
|
||||
<#ty as ::juniper::FromInputValue<#scalar>>::from_implicit_null()
|
||||
.map_err(|e| {
|
||||
::juniper::IntoFieldError::<#scalar>::into_field_error(e)
|
||||
.map_message(|m| format!(#err_text, m))
|
||||
})
|
||||
}, Ok))
|
||||
};
|
||||
if for_async {
|
||||
quote! {
|
||||
match #arg {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Box::pin(async { Err(e) }),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! { #arg? }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -268,39 +268,45 @@ impl Definition {
|
|||
self.arguments.is_some()
|
||||
}
|
||||
|
||||
/// Returns generated code that panics about unknown [GraphQL field][1]
|
||||
/// Returns generated code that errors about unknown [GraphQL field][1]
|
||||
/// tried to be resolved in the [`GraphQLValue::resolve_field`] method.
|
||||
///
|
||||
/// [`GraphQLValue::resolve_field`]: juniper::GraphQLValue::resolve_field
|
||||
/// [1]: https://spec.graphql.org/June2018/#sec-Language.Fields
|
||||
#[must_use]
|
||||
pub(crate) fn method_resolve_field_panic_no_field_tokens(scalar: &scalar::Type) -> TokenStream {
|
||||
pub(crate) fn method_resolve_field_err_no_field_tokens(
|
||||
scalar: &scalar::Type,
|
||||
ty_name: &str,
|
||||
) -> TokenStream {
|
||||
quote! {
|
||||
panic!(
|
||||
return Err(::juniper::FieldError::from(format!(
|
||||
"Field `{}` not found on type `{}`",
|
||||
field,
|
||||
<Self as ::juniper::GraphQLType<#scalar>>::name(info).unwrap(),
|
||||
)
|
||||
<Self as ::juniper::GraphQLType<#scalar>>::name(info)
|
||||
.ok_or_else(|| ::juniper::macros::helper::err_unnamed_type(#ty_name))?,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns generated code that panics about [GraphQL fields][1] tried to be
|
||||
/// Returns generated code that errors about [GraphQL fields][1] tried to be
|
||||
/// resolved asynchronously in the [`GraphQLValue::resolve_field`] method
|
||||
/// (which is synchronous itself).
|
||||
///
|
||||
/// [`GraphQLValue::resolve_field`]: juniper::GraphQLValue::resolve_field
|
||||
/// [1]: https://spec.graphql.org/June2018/#sec-Language.Fields
|
||||
#[must_use]
|
||||
pub(crate) fn method_resolve_field_panic_async_field_tokens(
|
||||
pub(crate) fn method_resolve_field_err_async_field_tokens(
|
||||
field_names: &[&str],
|
||||
scalar: &scalar::Type,
|
||||
ty_name: &str,
|
||||
) -> TokenStream {
|
||||
quote! {
|
||||
#( #field_names )|* => panic!(
|
||||
#( #field_names )|* => return Err(::juniper::FieldError::from(format!(
|
||||
"Tried to resolve async field `{}` on type `{}` with a sync resolver",
|
||||
field,
|
||||
<Self as ::juniper::GraphQLType<#scalar>>::name(info).unwrap(),
|
||||
),
|
||||
<Self as ::juniper::GraphQLType<#scalar>>::name(info)
|
||||
.ok_or_else(|| ::juniper::macros::helper::err_unnamed_type(#ty_name))?,
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -409,7 +415,7 @@ impl Definition {
|
|||
.as_ref()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|arg| arg.method_resolve_field_tokens(scalar));
|
||||
.map(|arg| arg.method_resolve_field_tokens(scalar, false));
|
||||
|
||||
let rcv = self.has_receiver.then(|| {
|
||||
quote! { self, }
|
||||
|
@ -455,7 +461,7 @@ impl Definition {
|
|||
.as_ref()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|arg| arg.method_resolve_field_tokens(scalar));
|
||||
.map(|arg| arg.method_resolve_field_tokens(scalar, true));
|
||||
|
||||
let rcv = self.has_receiver.then(|| {
|
||||
quote! { self, }
|
||||
|
@ -504,7 +510,7 @@ impl Definition {
|
|||
.as_ref()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|arg| arg.method_resolve_field_tokens(scalar));
|
||||
.map(|arg| arg.method_resolve_field_tokens(scalar, false));
|
||||
|
||||
let rcv = self.has_receiver.then(|| {
|
||||
quote! { self, }
|
||||
|
|
|
@ -197,9 +197,13 @@ fn impl_scalar_struct(
|
|||
where
|
||||
#scalar: ::juniper::ScalarValue,
|
||||
{
|
||||
fn from_input_value(v: &::juniper::InputValue<#scalar>) -> Option<#ident> {
|
||||
type Error = <#inner_ty as ::juniper::FromInputValue<#scalar>>::Error;
|
||||
|
||||
fn from_input_value(
|
||||
v: &::juniper::InputValue<#scalar>
|
||||
) -> Result<#ident, <#inner_ty as ::juniper::FromInputValue<#scalar>>::Error> {
|
||||
let inner: #inner_ty = ::juniper::FromInputValue::<#scalar>::from_input_value(v)?;
|
||||
Some(#ident(inner))
|
||||
Ok(#ident(inner))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -572,23 +572,27 @@ impl Definition {
|
|||
|
||||
let (impl_generics, where_clause) = self.ty.impl_generics(false);
|
||||
let ty = self.ty.ty_tokens();
|
||||
let ty_name = ty.to_string();
|
||||
let trait_ty = self.ty.trait_ty();
|
||||
|
||||
let fields_resolvers = self
|
||||
.fields
|
||||
.iter()
|
||||
.filter_map(|f| f.method_resolve_field_tokens(scalar, Some(&trait_ty)));
|
||||
let async_fields_panic = {
|
||||
let async_fields_err = {
|
||||
let names = self
|
||||
.fields
|
||||
.iter()
|
||||
.filter_map(|f| f.is_async.then(|| f.name.as_str()))
|
||||
.collect::<Vec<_>>();
|
||||
(!names.is_empty()).then(|| {
|
||||
field::Definition::method_resolve_field_panic_async_field_tokens(&names, scalar)
|
||||
field::Definition::method_resolve_field_err_async_field_tokens(
|
||||
&names, scalar, &ty_name,
|
||||
)
|
||||
})
|
||||
};
|
||||
let no_field_panic = field::Definition::method_resolve_field_panic_no_field_tokens(scalar);
|
||||
let no_field_err =
|
||||
field::Definition::method_resolve_field_err_no_field_tokens(scalar, &ty_name);
|
||||
|
||||
let custom_downcast_checks = self
|
||||
.implementers
|
||||
|
@ -623,8 +627,8 @@ impl Definition {
|
|||
) -> ::juniper::ExecutionResult<#scalar> {
|
||||
match field {
|
||||
#( #fields_resolvers )*
|
||||
#async_fields_panic
|
||||
_ => #no_field_panic,
|
||||
#async_fields_err
|
||||
_ => #no_field_err,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -662,13 +666,15 @@ impl Definition {
|
|||
|
||||
let (impl_generics, where_clause) = self.ty.impl_generics(true);
|
||||
let ty = self.ty.ty_tokens();
|
||||
let ty_name = ty.to_string();
|
||||
let trait_ty = self.ty.trait_ty();
|
||||
|
||||
let fields_resolvers = self
|
||||
.fields
|
||||
.iter()
|
||||
.map(|f| f.method_resolve_field_async_tokens(scalar, Some(&trait_ty)));
|
||||
let no_field_panic = field::Definition::method_resolve_field_panic_no_field_tokens(scalar);
|
||||
let no_field_err =
|
||||
field::Definition::method_resolve_field_err_no_field_tokens(scalar, &ty_name);
|
||||
|
||||
let custom_downcasts = self
|
||||
.implementers
|
||||
|
@ -690,7 +696,7 @@ impl Definition {
|
|||
) -> ::juniper::BoxFuture<'b, ::juniper::ExecutionResult<#scalar>> {
|
||||
match field {
|
||||
#( #fields_resolvers )*
|
||||
_ => #no_field_panic,
|
||||
_ => Box::pin(async move { #no_field_err }),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -840,6 +846,7 @@ impl Implementer {
|
|||
self.downcast.as_ref()?;
|
||||
|
||||
let ty = &self.ty;
|
||||
let ty_name = ty.to_token_stream().to_string();
|
||||
let scalar = &self.scalar;
|
||||
|
||||
let downcast = self.downcast_call_tokens(trait_ty, None);
|
||||
|
@ -847,7 +854,9 @@ impl Implementer {
|
|||
let resolving_code = gen::sync_resolving_code();
|
||||
|
||||
Some(quote! {
|
||||
if type_name == <#ty as ::juniper::GraphQLType<#scalar>>::name(info).unwrap() {
|
||||
if type_name == <#ty as ::juniper::GraphQLType<#scalar>>::name(info)
|
||||
.ok_or_else(|| ::juniper::macros::helper::err_unnamed_type(#ty_name))?
|
||||
{
|
||||
let res = #downcast;
|
||||
return #resolving_code;
|
||||
}
|
||||
|
@ -868,6 +877,7 @@ impl Implementer {
|
|||
self.downcast.as_ref()?;
|
||||
|
||||
let ty = &self.ty;
|
||||
let ty_name = ty.to_token_stream().to_string();
|
||||
let scalar = &self.scalar;
|
||||
|
||||
let downcast = self.downcast_call_tokens(trait_ty, None);
|
||||
|
@ -875,9 +885,14 @@ impl Implementer {
|
|||
let resolving_code = gen::async_resolving_code(None);
|
||||
|
||||
Some(quote! {
|
||||
if type_name == <#ty as ::juniper::GraphQLType<#scalar>>::name(info).unwrap() {
|
||||
let fut = ::juniper::futures::future::ready(#downcast);
|
||||
return #resolving_code;
|
||||
match <#ty as ::juniper::GraphQLType<#scalar>>::name(info) {
|
||||
Some(name) => {
|
||||
if type_name == name {
|
||||
let fut = ::juniper::futures::future::ready(#downcast);
|
||||
return #resolving_code;
|
||||
}
|
||||
}
|
||||
None => return ::juniper::macros::helper::err_unnamed_type_fut(#ty_name),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -486,6 +486,7 @@ impl Definition<Query> {
|
|||
|
||||
let (impl_generics, where_clause) = self.impl_generics(false);
|
||||
let ty = &self.ty;
|
||||
let ty_name = ty.to_token_stream().to_string();
|
||||
|
||||
let name = &self.name;
|
||||
|
||||
|
@ -493,17 +494,20 @@ impl Definition<Query> {
|
|||
.fields
|
||||
.iter()
|
||||
.filter_map(|f| f.method_resolve_field_tokens(scalar, None));
|
||||
let async_fields_panic = {
|
||||
let async_fields_err = {
|
||||
let names = self
|
||||
.fields
|
||||
.iter()
|
||||
.filter_map(|f| f.is_async.then(|| f.name.as_str()))
|
||||
.collect::<Vec<_>>();
|
||||
(!names.is_empty()).then(|| {
|
||||
field::Definition::method_resolve_field_panic_async_field_tokens(&names, scalar)
|
||||
field::Definition::method_resolve_field_err_async_field_tokens(
|
||||
&names, scalar, &ty_name,
|
||||
)
|
||||
})
|
||||
};
|
||||
let no_field_panic = field::Definition::method_resolve_field_panic_no_field_tokens(scalar);
|
||||
let no_field_err =
|
||||
field::Definition::method_resolve_field_err_no_field_tokens(scalar, &ty_name);
|
||||
|
||||
quote! {
|
||||
#[allow(deprecated)]
|
||||
|
@ -526,8 +530,8 @@ impl Definition<Query> {
|
|||
) -> ::juniper::ExecutionResult<#scalar> {
|
||||
match field {
|
||||
#( #fields_resolvers )*
|
||||
#async_fields_panic
|
||||
_ => #no_field_panic,
|
||||
#async_fields_err
|
||||
_ => #no_field_err,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -553,12 +557,14 @@ impl Definition<Query> {
|
|||
|
||||
let (impl_generics, where_clause) = self.impl_generics(true);
|
||||
let ty = &self.ty;
|
||||
let ty_name = ty.to_token_stream().to_string();
|
||||
|
||||
let fields_resolvers = self
|
||||
.fields
|
||||
.iter()
|
||||
.map(|f| f.method_resolve_field_async_tokens(scalar, None));
|
||||
let no_field_panic = field::Definition::method_resolve_field_panic_no_field_tokens(scalar);
|
||||
let no_field_err =
|
||||
field::Definition::method_resolve_field_err_no_field_tokens(scalar, &ty_name);
|
||||
|
||||
quote! {
|
||||
#[allow(deprecated, non_snake_case)]
|
||||
|
@ -574,7 +580,7 @@ impl Definition<Query> {
|
|||
) -> ::juniper::BoxFuture<'b, ::juniper::ExecutionResult<#scalar>> {
|
||||
match field {
|
||||
#( #fields_resolvers )*
|
||||
_ => #no_field_panic,
|
||||
_ => Box::pin(async move { #no_field_err }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,9 @@ impl Definition<Subscription> {
|
|||
_: &::juniper::Arguments<#scalar>,
|
||||
_: &::juniper::Executor<Self::Context, #scalar>,
|
||||
) -> ::juniper::ExecutionResult<#scalar> {
|
||||
panic!("Called `resolve_field` on subscription object");
|
||||
Err(::juniper::FieldError::from(
|
||||
"Called `resolve_field` on subscription object",
|
||||
))
|
||||
}
|
||||
|
||||
fn concrete_type_name(
|
||||
|
@ -95,12 +97,14 @@ impl Definition<Subscription> {
|
|||
.push(parse_quote! { #scalar: Send + Sync });
|
||||
}
|
||||
let ty = &self.ty;
|
||||
let ty_name = ty.to_token_stream().to_string();
|
||||
|
||||
let fields_resolvers = self
|
||||
.fields
|
||||
.iter()
|
||||
.map(|f| f.method_resolve_field_into_stream_tokens(scalar));
|
||||
let no_field_panic = field::Definition::method_resolve_field_panic_no_field_tokens(scalar);
|
||||
let no_field_err =
|
||||
field::Definition::method_resolve_field_err_no_field_tokens(scalar, &ty_name);
|
||||
|
||||
quote! {
|
||||
#[allow(deprecated)]
|
||||
|
@ -130,7 +134,7 @@ impl Definition<Subscription> {
|
|||
{
|
||||
match field {
|
||||
#( #fields_resolvers )*
|
||||
_ => #no_field_panic,
|
||||
_ => Box::pin(async move { #no_field_err }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -558,11 +558,11 @@ impl Definition {
|
|||
) -> ::juniper::ExecutionResult<#scalar> {
|
||||
let context = executor.context();
|
||||
#( #variant_resolvers )*
|
||||
panic!(
|
||||
return Err(::juniper::FieldError::from(format!(
|
||||
"Concrete type `{}` is not handled by instance \
|
||||
resolvers on GraphQL union `{}`",
|
||||
type_name, #name,
|
||||
);
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -600,11 +600,11 @@ impl Definition {
|
|||
) -> ::juniper::BoxFuture<'b, ::juniper::ExecutionResult<#scalar>> {
|
||||
let context = executor.context();
|
||||
#( #variant_async_resolvers )*
|
||||
panic!(
|
||||
return ::juniper::macros::helper::err_fut(format!(
|
||||
"Concrete type `{}` is not handled by instance \
|
||||
resolvers on GraphQL union `{}`",
|
||||
type_name, #name,
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -670,11 +670,14 @@ impl VariantDefinition {
|
|||
#[must_use]
|
||||
fn method_resolve_into_type_tokens(&self, scalar: &scalar::Type) -> TokenStream {
|
||||
let ty = &self.ty;
|
||||
let ty_name = ty.to_token_stream().to_string();
|
||||
let expr = &self.resolver_code;
|
||||
let resolving_code = gen::sync_resolving_code();
|
||||
|
||||
quote! {
|
||||
if type_name == <#ty as ::juniper::GraphQLType<#scalar>>::name(info).unwrap() {
|
||||
if type_name == <#ty as ::juniper::GraphQLType<#scalar>>::name(info)
|
||||
.ok_or_else(|| ::juniper::macros::helper::err_unnamed_type(#ty_name))?
|
||||
{
|
||||
let res = { #expr };
|
||||
return #resolving_code;
|
||||
}
|
||||
|
@ -690,13 +693,19 @@ impl VariantDefinition {
|
|||
#[must_use]
|
||||
fn method_resolve_into_type_async_tokens(&self, scalar: &scalar::Type) -> TokenStream {
|
||||
let ty = &self.ty;
|
||||
let ty_name = ty.to_token_stream().to_string();
|
||||
let expr = &self.resolver_code;
|
||||
let resolving_code = gen::async_resolving_code(None);
|
||||
|
||||
quote! {
|
||||
if type_name == <#ty as ::juniper::GraphQLType<#scalar>>::name(info).unwrap() {
|
||||
let fut = ::juniper::futures::future::ready({ #expr });
|
||||
return #resolving_code;
|
||||
match <#ty as ::juniper::GraphQLType<#scalar>>::name(info) {
|
||||
Some(name) => {
|
||||
if type_name == name {
|
||||
let fut = ::juniper::futures::future::ready({ #expr });
|
||||
return #resolving_code;
|
||||
}
|
||||
}
|
||||
None => return ::juniper::macros::helper::err_unnamed_type_fut(#ty_name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -309,6 +309,8 @@ pub fn build_scalar(
|
|||
impl#generic_type_decl ::juniper::FromInputValue<#generic_type> for #impl_for_type
|
||||
#generic_type_bound
|
||||
{
|
||||
type Error = <#from_input_value_result as ::juniper::macros::helper::ExtractError>::Error;
|
||||
|
||||
fn from_input_value(#from_input_value_arg: &::juniper::InputValue<#generic_type>) -> #from_input_value_result {
|
||||
#from_input_value_body
|
||||
}
|
||||
|
|
|
@ -230,8 +230,11 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
|
|||
/// juniper::Value::scalar(self.0.to_owned())
|
||||
/// }
|
||||
///
|
||||
/// fn from_input_value(value: &juniper::InputValue) -> Option<UserID> {
|
||||
/// value.as_string_value().map(|s| UserID(s.to_owned()))
|
||||
/// // NOTE: The error type should implement `IntoFieldError<S>`.
|
||||
/// fn from_input_value(value: &juniper::InputValue) -> Result<UserID, String> {
|
||||
/// value.as_string_value()
|
||||
/// .map(|s| UserID(s.to_owned()))
|
||||
/// .ok_or_else(|| format!("Expected `String`, found: {}", value))
|
||||
/// }
|
||||
///
|
||||
/// fn from_str<'a>(value: juniper::ScalarToken<'a>) -> juniper::ParseScalarResult<'a, S> {
|
||||
|
|
|
@ -752,7 +752,7 @@ impl GraphQLTypeDefiniton {
|
|||
let resolver_code = &variant.resolver_code;
|
||||
|
||||
quote!(
|
||||
Some(#variant_name) => Some(#resolver_code),
|
||||
Some(#variant_name) => Ok(#resolver_code),
|
||||
)
|
||||
});
|
||||
|
||||
|
@ -860,13 +860,14 @@ impl GraphQLTypeDefiniton {
|
|||
impl#impl_generics ::juniper::FromInputValue<#scalar> for #ty
|
||||
#where_clause
|
||||
{
|
||||
fn from_input_value(v: &::juniper::InputValue<#scalar>) -> Option<#ty>
|
||||
{
|
||||
match v.as_enum_value().or_else(|| {
|
||||
v.as_string_value()
|
||||
}) {
|
||||
type Error = ::std::string::String;
|
||||
|
||||
fn from_input_value(
|
||||
v: &::juniper::InputValue<#scalar>
|
||||
) -> Result<#ty, Self::Error> {
|
||||
match v.as_enum_value().or_else(|| v.as_string_value()) {
|
||||
#( #from_inputs )*
|
||||
_ => None,
|
||||
_ => Err(format!("Unknown enum value: {}", v)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -980,8 +981,14 @@ impl GraphQLTypeDefiniton {
|
|||
#field_ident: {
|
||||
match obj.get(#field_name) {
|
||||
#from_input_default
|
||||
Some(ref v) => ::juniper::FromInputValue::from_input_value(v)?,
|
||||
None => ::juniper::FromInputValue::<#scalar>::from_implicit_null()?,
|
||||
Some(ref v) => {
|
||||
::juniper::FromInputValue::<#scalar>::from_input_value(v)
|
||||
.map_err(::juniper::IntoFieldError::into_field_error)?
|
||||
},
|
||||
None => {
|
||||
::juniper::FromInputValue::<#scalar>::from_implicit_null()
|
||||
.map_err(::juniper::IntoFieldError::into_field_error)?
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -1096,10 +1103,17 @@ impl GraphQLTypeDefiniton {
|
|||
impl#impl_generics ::juniper::FromInputValue<#scalar> for #ty #type_generics_tokens
|
||||
#where_clause
|
||||
{
|
||||
fn from_input_value(value: &::juniper::InputValue<#scalar>) -> Option<Self>
|
||||
{
|
||||
let obj = value.to_object_value()?;
|
||||
Some(#ty {
|
||||
type Error = ::juniper::FieldError<#scalar>;
|
||||
|
||||
fn from_input_value(
|
||||
value: &::juniper::InputValue<#scalar>
|
||||
) -> Result<Self, Self::Error> {
|
||||
let obj = value
|
||||
.to_object_value()
|
||||
.ok_or_else(|| ::juniper::FieldError::<#scalar>::from(
|
||||
format!("Expected input object, found: {}", value))
|
||||
)?;
|
||||
Ok(#ty {
|
||||
#( #from_inputs )*
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue