From 0cb9248b2336b7a46bc0b5a79348975d9f5c0f02 Mon Sep 17 00:00:00 2001 From: tyranron Date: Fri, 15 Oct 2021 13:05:03 +0300 Subject: [PATCH 01/25] Change `FromInputValue` signature and boostrap refactoring --- juniper/src/ast.rs | 39 ++-- juniper/src/executor/mod.rs | 163 ++++++++-------- .../introspection/input_object.rs | 4 +- juniper/src/parser/tests/value.rs | 2 +- juniper/src/schema/meta.rs | 180 +++++++++--------- juniper/src/schema/schema.rs | 2 +- juniper/src/types/base.rs | 41 ++-- juniper/src/types/nullable.rs | 16 +- juniper/src/value/mod.rs | 2 +- 9 files changed, 233 insertions(+), 216 deletions(-) diff --git a/juniper/src/ast.rs b/juniper/src/ast.rs index 3ad050240..cb456a87d 100644 --- a/juniper/src/ast.rs +++ b/juniper/src/ast.rs @@ -152,20 +152,34 @@ pub type OwnedDocument<'a, S> = Vec>; /// Parse 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: Sized { + /// Type of this conversion error. + /// + /// Thus not restricted, it should be convertible with [`IntoFieldError`] to + /// fit well into the library machinery. + type Error; + /// Performs the conversion. - fn from_input_value(v: &InputValue) -> Option; + fn from_input_value(v: &InputValue) -> Result; - /// 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. + /// Performs the conversion from an absent value (e.g. to distinguish + /// between implicit and explicit `null`). /// - /// This conversion must not fail. - fn from_implicit_null() -> Option { + /// The default implementation just calls + /// [`FromInputValue::from_input_value()`] as if an explicit `null` was + /// provided. + fn from_implicit_null() -> Result { Self::from_input_value(&InputValue::::Null) } } @@ -298,12 +312,9 @@ impl InputValue { } } - /// Shorthand form of invoking [`FromInputValue::from()`]. - pub fn convert(&self) -> Option - where - T: FromInputValue, - { - >::from_input_value(self) + /// Shorthand form of invoking [`FromInputValue::from_input_value()`]. + pub fn convert>(&self) -> Result { + T::from_input_value(self) } /// Does the value represent a `null`? diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index c27cd843e..719ce7abc 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -149,47 +149,38 @@ where /// Ok(s) /// } /// ``` -#[derive(Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct FieldError { message: String, extensions: Value, } -impl From for FieldError -where - S: crate::value::ScalarValue, -{ - fn from(e: T) -> FieldError { - FieldError { - message: format!("{}", e), - extensions: Value::null(), +impl From for FieldError { + fn from(e: T) -> Self { + Self { + message: e.to_string(), + extensions: Value::Null, } } } impl FieldError { - /// 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 to construct an error: /// ```rust - /// use juniper::FieldError; /// # use juniper::DefaultScalarValue; - /// use juniper::graphql_value; + /// use juniper::{graphql_value, FieldError}; /// - /// # fn sample() { /// # 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 +193,34 @@ impl FieldError { /// } /// ``` /// - /// If the argument is `Value::null()`, no extra data will be included. - pub fn new(e: T, extensions: Value) -> FieldError { - 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(e: T, extensions: Value) -> 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 none `"extensions"` then [`Value::Null`] will be returned. + #[must_use] pub fn extensions(&self) -> &Value { &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(self) -> FieldError where S: ScalarValue, @@ -246,17 +246,18 @@ pub type ValuesStream<'a, S = DefaultScalarValue> = /// The map of variables used for substitution during query execution pub type Variables = HashMap>; -/// 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 to a +/// [`FieldError`]. pub trait IntoFieldError { - #[doc(hidden)] + /// Performs the custom conversion into a [`FieldError`]. + #[must_use] fn into_field_error(self) -> FieldError; } impl IntoFieldError for FieldError { - #[inline] fn into_field_error(self) -> FieldError { self.map_scalar_value() } @@ -1125,22 +1126,21 @@ where Ok((value, errors)) } -impl<'r, S> Registry<'r, S> -where - S: ScalarValue + 'r, -{ - /// Construct a new registry - pub fn new(types: FnvHashMap>) -> 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>) -> 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(&mut self, info: &T::TypeInfo) -> Type<'r> where T: GraphQLType + ?Sized, + S: ScalarValue, { if let Some(name) = T::name(info) { let validated_name = name.parse::().unwrap(); @@ -1158,10 +1158,11 @@ where } } - /// Create a field with the provided name + /// Creates a [`Field`] with the provided `name`. pub fn field(&mut self, name: &str, info: &T::TypeInfo) -> Field<'r, S> where T: GraphQLType + ?Sized, + S: ScalarValue, { Field { name: smartstring::SmartString::from(name), @@ -1180,6 +1181,7 @@ where ) -> Field<'r, S> where I: GraphQLType, + S: ScalarValue, { Field { name: smartstring::SmartString::from(name), @@ -1190,18 +1192,19 @@ where } } - /// Create an argument with the provided name + /// Creates an [`Argument`] with the provided `name`. pub fn arg(&mut self, name: &str, info: &T::TypeInfo) -> Argument<'r, S> where - T: GraphQLType + FromInputValue + ?Sized, + T: GraphQLType + FromInputValue, + S: ScalarValue, { Argument::new(name, self.get_type::(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`. + /// When called with type `T`, the actual [`Argument`] will be given the + /// type `Option`. pub fn arg_with_default( &mut self, name: &str, @@ -1209,7 +1212,8 @@ where info: &T::TypeInfo, ) -> Argument<'r, S> where - T: GraphQLType + ToInputValue + FromInputValue + ?Sized, + T: GraphQLType + ToInputValue + FromInputValue, + S: ScalarValue, { Argument::new(name, self.get_type::>(info)).default_value(value.to_input_value()) } @@ -1220,40 +1224,45 @@ 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(&mut self, info: &T::TypeInfo) -> ScalarMeta<'r, S> where - T: FromInputValue + GraphQLType + ParseScalarValue + ?Sized + 'r, + T: GraphQLType + FromInputValue + ParseScalarValue + 'r, + 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::(Cow::Owned(name.to_string())) } - /// Create a list meta type - pub fn build_list_type + ?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( &mut self, info: &T::TypeInfo, expected_size: Option, - ) -> ListMeta<'r> { + ) -> ListMeta<'r> + where + T: GraphQLType + ?Sized, + S: ScalarValue, + { let of_type = self.get_type::(info); ListMeta::new(of_type, expected_size) } - /// Create a nullable meta type - pub fn build_nullable_type + ?Sized>( - &mut self, - info: &T::TypeInfo, - ) -> NullableMeta<'r> { + /// Creates a [`NullableMeta`] type. + pub fn build_nullable_type(&mut self, info: &T::TypeInfo) -> NullableMeta<'r> + where + T: GraphQLType + ?Sized, + S: ScalarValue, + { let of_type = self.get_type::(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( &mut self, info: &T::TypeInfo, @@ -1261,6 +1270,7 @@ where ) -> ObjectMeta<'r, S> where T: GraphQLType + ?Sized, + S: ScalarValue, { let name = T::name(info).expect("Object types must be named. Implement name()"); @@ -1269,22 +1279,22 @@ 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( &mut self, info: &T::TypeInfo, values: &[EnumValue], ) -> EnumMeta<'r, S> where - T: FromInputValue + GraphQLType + ?Sized, + T: GraphQLType + FromInputValue, + 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::(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( &mut self, info: &T::TypeInfo, @@ -1292,6 +1302,7 @@ where ) -> InterfaceMeta<'r, S> where T: GraphQLType + ?Sized, + S: ScalarValue, { let name = T::name(info).expect("Interface types must be named. Implement name()"); @@ -1300,24 +1311,26 @@ 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(&mut self, info: &T::TypeInfo, types: &[Type<'r>]) -> UnionMeta<'r> where T: GraphQLType + ?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( &mut self, info: &T::TypeInfo, args: &[Argument<'r, S>], ) -> InputObjectMeta<'r, S> where - T: FromInputValue + GraphQLType + ?Sized, + T: GraphQLType + FromInputValue, + S: ScalarValue, { let name = T::name(info).expect("Input object types must be named. Implement name()"); diff --git a/juniper/src/executor_tests/introspection/input_object.rs b/juniper/src/executor_tests/introspection/input_object.rs index d9c48bda5..c924c3cad 100644 --- a/juniper/src/executor_tests/introspection/input_object.rs +++ b/juniper/src/executor_tests/introspection/input_object.rs @@ -209,9 +209,9 @@ fn default_name_input_value() { .collect(), ); - let dv: Option = FromInputValue::from_input_value(&iv); + let dv = DefaultName::from_input_value(&iv); - assert!(dv.is_some()); + assert!(dv.is_ok(), "error: {}", dv.unwrap_err()); let dv = dv.unwrap(); diff --git a/juniper/src/parser/tests/value.rs b/juniper/src/parser/tests/value.rs index c8b1ac9b9..e3f4fe81c 100644 --- a/juniper/src/parser/tests/value.rs +++ b/juniper/src/parser/tests/value.rs @@ -55,7 +55,7 @@ impl Query { fn scalar_meta(name: &'static str) -> MetaType where - T: FromInputValue + ParseScalarValue + 'static, + T: FromInputValue + ParseScalarValue, { MetaType::Scalar(ScalarMeta::new::(name.into())) } diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs index 6566df910..e7f4e8cf6 100644 --- a/juniper/src/schema/meta.rs +++ b/juniper/src/schema/meta.rs @@ -407,16 +407,13 @@ 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(name: Cow<'a, str>) -> Self where - T: FromInputValue + ParseScalarValue + 'a, + T: FromInputValue + ParseScalarValue, { - ScalarMeta { + Self { name, description: None, try_parse_fn: try_parse_fn::, @@ -424,22 +421,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) -> Self { Self { of_type, @@ -447,31 +447,28 @@ impl<'a> ListMeta<'a> { } } - /// Wrap the list in a generic meta type + /// Wraps this [`ListMeta`] type into a generic [`MetaType`]. pub fn into_meta(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(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 +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 { - ObjectMeta { + Self { name, description: None, fields: fields.to_vec(), @@ -479,19 +476,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 +495,71 @@ 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(name: Cow<'a, str>, values: &[EnumValue]) -> Self where T: FromInputValue, { - EnumMeta { + Self { name, description: None, - values: values.to_vec(), + values: values.to_owned(), try_parse_fn: try_parse_fn::, } } - /// 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 { + Self { name, description: None, - fields: fields.to_vec(), + fields: fields.to_owned(), } } - /// 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,61 +569,59 @@ 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(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(name: Cow<'a, str>, input_fields: &[Argument<'a, S>]) -> Self where - T: FromInputValue + ?Sized, + T: FromInputValue, { - InputObjectMeta { + Self { name, description: None, - input_fields: input_fields.to_vec(), + input_fields: input_fields.to_owned(), try_parse_fn: try_parse_fn::, } } - /// 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 +633,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 +646,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 +656,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) -> Self { - self.default_value = Some(default_value); + /// Overwrites any previously set default value. + pub fn default_value(mut self, val: InputValue) -> 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 @@ -743,5 +733,5 @@ fn try_parse_fn(v: &InputValue) -> bool where T: FromInputValue, { - >::from_input_value(v).is_some() + T::from_input_value(v).is_ok() } diff --git a/juniper/src/schema/schema.rs b/juniper/src/schema/schema.rs index 5651ccff5..ee947fee5 100644 --- a/juniper/src/schema/schema.rs +++ b/juniper/src/schema/schema.rs @@ -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)) diff --git a/juniper/src/types/base.rs b/juniper/src/types/base.rs index 45d6b703b..e06d5e943 100644 --- a/juniper/src/types/base.rs +++ b/juniper/src/types/base.rs @@ -6,7 +6,7 @@ use crate::{ parser::Spanning, schema::meta::{Argument, MetaType}, value::{DefaultScalarValue, Object, ScalarValue, Value}, - GraphQLEnum, + GraphQLEnum, IntoFieldError, FieldResult }; /// GraphQL type kind @@ -73,10 +73,7 @@ pub struct Arguments<'a, S = DefaultScalarValue> { args: Option>>, } -impl<'a, S> Arguments<'a, S> -where - S: ScalarValue, -{ +impl<'a, S> Arguments<'a, S> { #[doc(hidden)] pub fn new( mut args: Option>>, @@ -86,34 +83,42 @@ where 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 { + 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 + /// given [`InputValue`] will be converted into the type `T`. + /// + /// Returns [`None`] if an argument with such `name` is not present. /// - /// If the argument is found, or a default argument has been provided, - /// the `InputValue` will be converted into the type `T`. + /// # Errors /// - /// Returns `Some` if the argument is present _and_ type conversion - /// succeeds. - pub fn get(&self, key: &str) -> Option + /// If the [`FromInputValue`] conversion fails. + pub fn get(&self, name: &str) -> FieldResult, S> where T: FromInputValue, + T::Error: IntoFieldError, { 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) } } diff --git a/juniper/src/types/nullable.rs b/juniper/src/types/nullable.rs index e463f74d2..81341c597 100644 --- a/juniper/src/types/nullable.rs +++ b/juniper/src/types/nullable.rs @@ -278,20 +278,18 @@ where } } -impl FromInputValue for Nullable -where - T: FromInputValue, - S: ScalarValue, -{ - fn from_input_value(v: &InputValue) -> Option> { +impl> FromInputValue for Nullable { + type Error = >::Error; + + fn from_input_value(v: &InputValue) -> Result { match v { - &InputValue::Null => Some(Self::ExplicitNull), + &InputValue::Null => Ok(Self::ExplicitNull), v => v.convert().map(Self::Some), } } - fn from_implicit_null() -> Option { - Some(Self::ImplicitNull) + fn from_implicit_null() -> Result { + Ok(Self::ImplicitNull) } } diff --git a/juniper/src/value/mod.rs b/juniper/src/value/mod.rs index ce37ad001..3ca6c4137 100644 --- a/juniper/src/value/mod.rs +++ b/juniper/src/value/mod.rs @@ -26,7 +26,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 { Null, From 584f9ae3403d4289f3ed5abec6381578e3219b15 Mon Sep 17 00:00:00 2001 From: tyranron Date: Fri, 15 Oct 2021 13:05:23 +0300 Subject: [PATCH 02/25] Refactor primitive types, vol.1 --- juniper/src/types/containers.rs | 66 +++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/juniper/src/types/containers.rs b/juniper/src/types/containers.rs index e4977fb0b..9aa542bda 100644 --- a/juniper/src/types/containers.rs +++ b/juniper/src/types/containers.rs @@ -80,28 +80,22 @@ where } } -impl FromInputValue for Option -where - T: FromInputValue, - S: ScalarValue, -{ - fn from_input_value(v: &InputValue) -> Option { +impl> FromInputValue for Option { + type Error = T::Error; + + fn from_input_value(v: &InputValue) -> Result { match v { - &InputValue::Null => Some(None), + &InputValue::Null => Ok(None), v => v.convert().map(Some), } } } -impl ToInputValue for Option -where - T: ToInputValue, - S: ScalarValue, -{ +impl> ToInputValue for Option { fn to_input_value(&self) -> InputValue { - 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,16 @@ where } } -impl FromInputValue for Vec -where - T: FromInputValue, - S: ScalarValue, -{ - fn from_input_value(v: &InputValue) -> Option { - 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> FromInputValue for Vec { + type Error = T::Error; + + fn from_input_value(v: &InputValue) -> Result { + 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 + InputValue::Null => Ok(Vec::new()), + other => other.convert().map(|e| vec![e]), } } } @@ -492,3 +484,23 @@ where Ok(Value::list(values)) } + +#[cfg(test)] +mod coercion { + use crate::{FromInputValue as _, InputValue}; + + // See "Input Coercion" examples on List types: + // https://spec.graphql.org/June2018/#sec-Type-System.List + #[test] + fn vec() { + assert_eq!( + >::from_input_value(&InputValue::list(vec![ + InputValue::scalar(1), + InputValue::scalar(2), + InputValue::scalar(3) + ])), + Ok(vec![1, 2, 3]), + ); + // TODO: all examples + } +} From e5481a0f8e15ca9bdf05eee32f93d5155d9ea078 Mon Sep 17 00:00:00 2001 From: ilslv Date: Tue, 23 Nov 2021 10:55:41 +0300 Subject: [PATCH 03/25] Update actix beta --- docs/book/content/advanced/dataloaders.md | 2 +- examples/actix_subscriptions/Cargo.toml | 4 +- juniper_actix/Cargo.toml | 12 ++-- juniper_actix/src/lib.rs | 67 ++++++++++++----------- 4 files changed, 44 insertions(+), 41 deletions(-) diff --git a/docs/book/content/advanced/dataloaders.md b/docs/book/content/advanced/dataloaders.md index 8dd2f858a..fe172d1db 100644 --- a/docs/book/content/advanced/dataloaders.md +++ b/docs/book/content/advanced/dataloaders.md @@ -48,7 +48,7 @@ DataLoader caching does not replace Redis, Memcache, or any other shared applica ```toml [dependencies] -actix-identity = "0.4.0-beta.2" +actix-identity = "0.4.0-beta.4" actix-rt = "1.0" actix-web = {version = "2.0", features = []} juniper = { git = "https://github.com/graphql-rust/juniper" } diff --git a/examples/actix_subscriptions/Cargo.toml b/examples/actix_subscriptions/Cargo.toml index 95bf4898f..3cf886146 100644 --- a/examples/actix_subscriptions/Cargo.toml +++ b/examples/actix_subscriptions/Cargo.toml @@ -6,8 +6,8 @@ authors = ["Mihai Dinculescu "] publish = false [dependencies] -actix-web = "4.0.0-beta.8" -actix-cors = "0.6.0-beta.2" +actix-web = "4.0.0-beta.12" +actix-cors = "0.6.0-beta.4" futures = "0.3" env_logger = "0.9" serde = "1.0" diff --git a/juniper_actix/Cargo.toml b/juniper_actix/Cargo.toml index fb01ee657..d5730bd7c 100644 --- a/juniper_actix/Cargo.toml +++ b/juniper_actix/Cargo.toml @@ -13,10 +13,10 @@ subscriptions = ["juniper_graphql_ws", "tokio"] [dependencies] actix = "0.12" -actix-http = "3.0.0-beta.8" +actix-http = "3.0.0-beta.13" http = "0.2.4" -actix-web = "4.0.0-beta.8" -actix-web-actors = "4.0.0-beta.6" +actix-web = "4.0.0-beta.12" +actix-web-actors = "4.0.0-beta.7" juniper = { version = "0.15.7", path = "../juniper", default-features = false } juniper_graphql_ws = { version = "0.3.0", path = "../juniper_graphql_ws", optional = true } @@ -30,11 +30,11 @@ tokio = { version = "1.0", features = ["sync"], optional = true } [dev-dependencies] actix-rt = "2" -actix-cors = "0.6.0-beta.2" -actix-identity = "0.4.0-beta.2" +actix-cors = "0.6.0-beta.4" +actix-identity = "0.4.0-beta.4" tokio = "1.0" async-stream = "0.3" -actix-test = "0.1.0-beta.3" +actix-test = "0.1.0-beta.6" juniper = { version = "0.15.7", path = "../juniper", features = ["expose-test-schema"] } diff --git a/juniper_actix/src/lib.rs b/juniper_actix/src/lib.rs index 279f246a6..ec299adef 100644 --- a/juniper_actix/src/lib.rs +++ b/juniper_actix/src/lib.rs @@ -467,7 +467,14 @@ pub mod subscriptions { #[cfg(test)] mod tests { use actix_http::body::AnyBody; - use actix_web::{dev::ServiceResponse, http, http::header::CONTENT_TYPE, test, web::Data, App}; + use actix_web::{ + dev::ServiceResponse, + http, + http::header::CONTENT_TYPE, + test::{call_service, init_service, TestRequest}, + web::Data, + App, + }; use juniper::{ http::tests::{run_http_test_suite, HttpIntegration, TestResponse}, tests::fixtures::starwars::schema::{Database, Query}, @@ -507,14 +514,13 @@ mod tests { async fn graphql_handler() -> Result { graphiql_handler("/abcd", None).await } - let mut app = - test::init_service(App::new().route("/", web::get().to(graphql_handler))).await; - let req = test::TestRequest::get() + let mut app = init_service(App::new().route("/", web::get().to(graphql_handler))).await; + let req = TestRequest::get() .uri("/") .append_header((ACCEPT, "text/html")) .to_request(); - let resp = test::call_service(&mut app, req).await; + let resp = call_service(&mut app, req).await; assert_eq!(resp.status(), http::StatusCode::OK); } @@ -523,14 +529,13 @@ mod tests { async fn graphql_handler() -> Result { graphiql_handler("/dogs-api/graphql", Some("/dogs-api/subscriptions")).await } - let mut app = - test::init_service(App::new().route("/", web::get().to(graphql_handler))).await; - let req = test::TestRequest::get() + let mut app = init_service(App::new().route("/", web::get().to(graphql_handler))).await; + let req = TestRequest::get() .uri("/") .append_header((ACCEPT, "text/html")) .to_request(); - let mut resp = test::call_service(&mut app, req).await; + let mut resp = call_service(&mut app, req).await; let body = take_response_body_string(&mut resp).await; assert_eq!(resp.status(), http::StatusCode::OK); assert_eq!( @@ -548,14 +553,13 @@ mod tests { async fn graphql_handler() -> Result { playground_handler("/abcd", None).await } - let mut app = - test::init_service(App::new().route("/", web::get().to(graphql_handler))).await; - let req = test::TestRequest::get() + let mut app = init_service(App::new().route("/", web::get().to(graphql_handler))).await; + let req = TestRequest::get() .uri("/") .append_header((ACCEPT, "text/html")) .to_request(); - let resp = test::call_service(&mut app, req).await; + let resp = call_service(&mut app, req).await; assert_eq!(resp.status(), http::StatusCode::OK); } @@ -564,14 +568,13 @@ mod tests { async fn graphql_handler() -> Result { playground_handler("/dogs-api/graphql", Some("/dogs-api/subscriptions")).await } - let mut app = - test::init_service(App::new().route("/", web::get().to(graphql_handler))).await; - let req = test::TestRequest::get() + let mut app = init_service(App::new().route("/", web::get().to(graphql_handler))).await; + let req = TestRequest::get() .uri("/") .append_header((ACCEPT, "text/html")) .to_request(); - let mut resp = test::call_service(&mut app, req).await; + let mut resp = call_service(&mut app, req).await; let body = take_response_body_string(&mut resp).await; assert_eq!(resp.status(), http::StatusCode::OK); assert_eq!( @@ -589,7 +592,7 @@ mod tests { EmptySubscription::::new(), ); - let req = test::TestRequest::post() + let req = TestRequest::post() .append_header(("content-type", "application/json; charset=utf-8")) .set_payload( r##"{ "variables": null, "query": "{ hero(episode: NEW_HOPE) { name } }" }"##, @@ -597,14 +600,14 @@ mod tests { .uri("/") .to_request(); - let mut app = test::init_service( + let mut app = init_service( App::new() .app_data(Data::new(schema)) .route("/", web::post().to(index)), ) .await; - let mut resp = test::call_service(&mut app, req).await; + let mut resp = call_service(&mut app, req).await; dbg!(take_response_body_string(&mut resp).await); assert_eq!(resp.status(), http::StatusCode::OK); assert_eq!( @@ -625,19 +628,19 @@ mod tests { EmptySubscription::::new(), ); - let req = test::TestRequest::get() + let req = TestRequest::get() .append_header(("content-type", "application/json")) .uri("/?query=%7B%20hero%28episode%3A%20NEW_HOPE%29%20%7B%20name%20%7D%20%7D&variables=null") .to_request(); - let mut app = test::init_service( + let mut app = init_service( App::new() .app_data(Data::new(schema)) .route("/", web::get().to(index)), ) .await; - let mut resp = test::call_service(&mut app, req).await; + let mut resp = call_service(&mut app, req).await; assert_eq!(resp.status(), http::StatusCode::OK); assert_eq!( @@ -663,7 +666,7 @@ mod tests { EmptySubscription::::new(), ); - let req = test::TestRequest::post() + let req = TestRequest::post() .append_header(("content-type", "application/json")) .set_payload( r##"[ @@ -674,14 +677,14 @@ mod tests { .uri("/") .to_request(); - let mut app = test::init_service( + let mut app = init_service( App::new() .app_data(Data::new(schema)) .route("/", web::post().to(index)), ) .await; - let mut resp = test::call_service(&mut app, req).await; + let mut resp = call_service(&mut app, req).await; assert_eq!(resp.status(), http::StatusCode::OK); assert_eq!( @@ -705,7 +708,7 @@ mod tests { pub struct TestActixWebIntegration; impl TestActixWebIntegration { - fn make_request(&self, req: test::TestRequest) -> TestResponse { + fn make_request(&self, req: TestRequest) -> TestResponse { actix_web::rt::System::new().block_on(async move { let schema = Schema::new( Query, @@ -713,14 +716,14 @@ mod tests { EmptySubscription::::new(), ); - let mut app = test::init_service( + let mut app = init_service( App::new() .app_data(Data::new(schema)) .route("/", web::to(index)), ) .await; - let resp = test::call_service(&mut app, req.to_request()).await; + let resp = call_service(&mut app, req.to_request()).await; make_test_response(resp).await }) } @@ -728,12 +731,12 @@ mod tests { impl HttpIntegration for TestActixWebIntegration { fn get(&self, url: &str) -> TestResponse { - self.make_request(test::TestRequest::get().uri(url)) + self.make_request(TestRequest::get().uri(url)) } fn post_json(&self, url: &str, body: &str) -> TestResponse { self.make_request( - test::TestRequest::post() + TestRequest::post() .append_header(("content-type", "application/json")) .set_payload(body.to_string()) .uri(url), @@ -742,7 +745,7 @@ mod tests { fn post_graphql(&self, url: &str, body: &str) -> TestResponse { self.make_request( - test::TestRequest::post() + TestRequest::post() .append_header(("content-type", "application/graphql")) .set_payload(body.to_string()) .uri(url), From acbdf9751480b000fcd44989c1814e8cdf38b2f1 Mon Sep 17 00:00:00 2001 From: ilslv Date: Mon, 29 Nov 2021 16:11:53 +0300 Subject: [PATCH 04/25] Support FromInputValue for arrays --- juniper/src/types/containers.rs | 88 ++++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 17 deletions(-) diff --git a/juniper/src/types/containers.rs b/juniper/src/types/containers.rs index 9aa542bda..43b1f5490 100644 --- a/juniper/src/types/containers.rs +++ b/juniper/src/types/containers.rs @@ -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, @@ -305,12 +305,63 @@ where } } +/// Error converting [`InputValue`] into exact-size [`array`](prim@array). +pub enum FromInputValueArrayError +where + T: FromInputValue, + S: ScalarValue, +{ + /// Not enough elements. + NotEnough(usize), + + /// Too many elements. Value is [`Vec`] of __all__ [`InputValue`] elements. + TooMuch(Vec), + + /// Underlying [`ScalarValue`] conversion error. + Scalar(T::Error), +} + +impl From for FromInputValueArrayError +where + T: FromInputValue, + S: ScalarValue, +{ + fn from(err: T::Error) -> Self { + Self::Scalar(err) + } +} + +impl IntoFieldError for FromInputValueArrayError +where + T: FromInputValue, + T::Error: IntoFieldError, + S: ScalarValue, +{ + fn into_field_error(self) -> FieldError { + const ERROR_PREFIX: &str = "Failed to convert into exact-size array"; + + match self { + Self::NotEnough(len) => FieldError::new( + format!("{}: required {} more elements", ERROR_PREFIX, len), + graphql_value!(null), + ), + Self::TooMuch(el) => FieldError::new( + format!("{}: too much elements: {}", ERROR_PREFIX, el.len()), + graphql_value!(null), + ), + Self::Scalar(s) => s.into_field_error(), + } + } +} + impl FromInputValue for [T; N] where T: FromInputValue, S: ScalarValue, { - fn from_input_value(v: &InputValue) -> Option { + type Error = FromInputValueArrayError; + + fn from_input_value(v: &InputValue) -> Result { struct PartiallyInitializedArray { arr: [MaybeUninit; N], init_len: usize, @@ -355,20 +406,15 @@ where no_drop: false, }; - let mut items = ls.iter().filter_map(|i| i.item.convert()); - for elem in &mut out.arr[..] { - if let Some(i) = items.next() { + let mut items = ls.iter().map(|i| i.item.convert()); + for (id, elem) in out.arr.iter_mut().enumerate() { + if let Some(i) = items.next().transpose()? { *elem = MaybeUninit::new(i); out.init_len += 1; } else { - // There is not enough `items` to fill the array. - return None; + return Err(FromInputValueArrayError::NotEnough(N - id)); } } - 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. @@ -383,10 +429,18 @@ 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) }) + let arr = unsafe { mem::transmute_copy::<_, Self>(&out.arr) }; + + if !items.is_empty() { + return Err(FromInputValueArrayError::TooMuch( + arr.into_iter().map(Ok).chain(items).collect()?, + )); + } + + Ok(arr) } ref other => { - other.convert().and_then(|e: T| { + other.convert().map_err(Into::into).and_then(|e: T| { // TODO: Use `mem::transmute` instead of // `mem::transmute_copy` below, once it's allowed for // const generics: @@ -399,11 +453,11 @@ where // `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)]) - }) + Ok(unsafe { mem::transmute_copy::<_, Self>(&[mem::ManuallyDrop::new(e)]) }) + } else if N == 0 { + Err(FromInputValueArrayError::TooMuch(vec![e])) } else { - None + Err(FromInputValueArrayError::NotEnough(N - 1)) } }) } From 7092bf2d117e0b6d2001d749e5c629e1d2da9325 Mon Sep 17 00:00:00 2001 From: ilslv Date: Wed, 1 Dec 2021 13:32:50 +0300 Subject: [PATCH 05/25] Fix existing tests --- integration_tests/async_await/src/main.rs | 5 +- .../juniper_tests/src/codegen/derive_enum.rs | 4 +- .../src/codegen/derive_input_object.rs | 10 +- .../juniper_tests/src/codegen/impl_scalar.rs | 53 ++++++---- .../juniper_tests/src/custom_scalar.rs | 14 +-- juniper/src/executor/mod.rs | 7 +- .../introspection/input_object.rs | 2 +- .../src/executor_tests/introspection/mod.rs | 11 ++- juniper/src/executor_tests/variables.rs | 16 ++- juniper/src/integrations/bson.rs | 20 +++- juniper/src/integrations/chrono.rs | 49 +++++++--- juniper/src/integrations/chrono_tz.rs | 40 +++++--- juniper/src/integrations/url.rs | 9 +- juniper/src/integrations/uuid.rs | 9 +- juniper/src/lib.rs | 2 +- juniper/src/macros/helper/mod.rs | 15 +++ juniper/src/parser/tests/document.rs | 5 +- juniper/src/schema/meta.rs | 17 +++- juniper/src/types/base.rs | 9 +- juniper/src/types/containers.rs | 98 +++++++++---------- juniper/src/types/pointers.rs | 8 +- juniper/src/types/scalars.rs | 55 +++++------ juniper/src/validation/test_harness.rs | 74 +++++++++----- juniper_codegen/src/common/field/arg.rs | 61 ++++++++++-- juniper_codegen/src/common/field/mod.rs | 26 ++--- juniper_codegen/src/derive_scalar_value.rs | 8 +- juniper_codegen/src/graphql_interface/mod.rs | 10 +- juniper_codegen/src/graphql_object/mod.rs | 10 +- .../src/graphql_subscription/mod.rs | 4 +- juniper_codegen/src/impl_scalar.rs | 2 + juniper_codegen/src/lib.rs | 6 +- juniper_codegen/src/util/mod.rs | 37 +++++-- 32 files changed, 451 insertions(+), 245 deletions(-) diff --git a/integration_tests/async_await/src/main.rs b/integration_tests/async_await/src/main.rs index ce185ba8c..cf58f90f0 100644 --- a/integration_tests/async_await/src/main.rs +++ b/integration_tests/async_await/src/main.rs @@ -1,4 +1,7 @@ -use juniper::{graphql_object, GraphQLEnum}; +use juniper::{ + graphql_object, graphql_value, EmptyMutation, EmptySubscription, GraphQLEnum, GraphQLError, + RootNode, Value, +}; #[derive(GraphQLEnum)] enum UserKind { diff --git a/integration_tests/juniper_tests/src/codegen/derive_enum.rs b/integration_tests/juniper_tests/src/codegen/derive_enum.rs index 45e90c9be..e3b5c7699 100644 --- a/integration_tests/juniper_tests/src/codegen/derive_enum.rs +++ b/integration_tests/juniper_tests/src/codegen/derive_enum.rs @@ -84,7 +84,7 @@ fn test_derived_enum() { ); assert_eq!( FromInputValue::::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::::from_input_value(&graphql_input_value!(FULL)), - Some(SomeEnum::Full) + Ok(SomeEnum::Full) ); } diff --git a/integration_tests/juniper_tests/src/codegen/derive_input_object.rs b/integration_tests/juniper_tests/src/codegen/derive_input_object.rs index 90b283452..fc9065fa1 100644 --- a/integration_tests/juniper_tests/src/codegen/derive_input_object.rs +++ b/integration_tests/juniper_tests/src/codegen/derive_input_object.rs @@ -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 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(FieldError::from("This is fake")) } } diff --git a/integration_tests/juniper_tests/src/codegen/impl_scalar.rs b/integration_tests/juniper_tests/src/codegen/impl_scalar.rs index 3432c2f6a..4c036b53d 100644 --- a/integration_tests/juniper_tests/src/codegen/impl_scalar.rs +++ b/integration_tests/juniper_tests/src/codegen/impl_scalar.rs @@ -1,6 +1,7 @@ use juniper::{ execute, graphql_object, graphql_scalar, graphql_value, graphql_vars, DefaultScalarValue, - EmptyMutation, EmptySubscription, Object, ParseScalarResult, ParseScalarValue, RootNode, Value, + EmptyMutation, EmptySubscription, FieldError, Object, ParseScalarResult, ParseScalarValue, + RootNode, Value, }; use crate::custom_scalar::MyScalarValue; @@ -31,10 +32,11 @@ where Value::scalar(self.0) } - fn from_input_value(v: &InputValue) -> Option { - v.as_scalar_value() - .and_then(|s| s.as_int()) - .map(|i| DefaultName(i)) + fn from_input_value(v: &InputValue) -> Result { + v.as_int_value() + .map(DefaultName) + .ok_or_else(|| format!("Expected Int, found: {}", v)) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -48,8 +50,11 @@ impl GraphQLScalar for OtherOrder { Value::scalar(self.0) } - fn from_input_value(v: &InputValue) -> Option { - v.as_scalar_value::().map(|i| OtherOrder(*i)) + fn from_input_value(v: &InputValue) -> Result { + v.as_int_value() + .map(OtherOrder) + .ok_or_else(|| format!("Expected Int, found: {}", v)) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { @@ -63,8 +68,11 @@ impl GraphQLScalar for Named { Value::scalar(self.0) } - fn from_input_value(v: &InputValue) -> Option { - v.as_scalar_value::().map(|i| Named(*i)) + fn from_input_value(v: &InputValue) -> Result { + v.as_int_value() + .map(Named) + .ok_or_else(|| format!("Expected Int, found: {}", v)) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { @@ -78,8 +86,11 @@ impl GraphQLScalar for ScalarDescription { Value::scalar(self.0) } - fn from_input_value(v: &InputValue) -> Option { - v.as_scalar_value::().map(|i| ScalarDescription(*i)) + fn from_input_value(v: &InputValue) -> Result { + v.as_int_value() + .map(ScalarDescription) + .ok_or_else(|| format!("Expected Int, found: {}", v)) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { @@ -98,10 +109,11 @@ macro_rules! impl_scalar { Value::scalar(self.0.clone()) } - fn from_input_value(v: &InputValue) -> Option { + fn from_input_value(v: &InputValue) -> Result> { v.as_scalar_value() .and_then(|v| v.as_str()) .and_then(|s| Some(Self(s.to_owned()))) + .ok_or_else(|| "Expected String".into()) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -140,9 +152,13 @@ impl GraphQLScalar for WithCustomScalarValue { Value::scalar(self.0) } - fn from_input_value(v: &InputValue) -> Option { - v.as_scalar_value::() - .map(|i| WithCustomScalarValue(*i)) + fn from_input_value( + v: &InputValue, + ) -> Result> { + v.as_int_value() + .map(WithCustomScalarValue) + .ok_or_else(|| format!("Expected Int, found: {}", v)) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, MyScalarValue> { @@ -198,8 +214,11 @@ fn path_in_resolve_return_type() { Value::scalar(self.0) } - fn from_input_value(v: &InputValue) -> Option { - v.as_scalar_value::().map(|i| ResolvePath(*i)) + fn from_input_value(v: &InputValue) -> Result { + v.as_int_value() + .map(ResolvePath) + .ok_or_else(|| format!("Expected Int, found: {}", v)) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { diff --git a/integration_tests/juniper_tests/src/custom_scalar.rs b/integration_tests/juniper_tests/src/custom_scalar.rs index 64fe39980..a2c12bbac 100644 --- a/integration_tests/juniper_tests/src/custom_scalar.rs +++ b/integration_tests/juniper_tests/src/custom_scalar.rs @@ -6,8 +6,8 @@ use juniper::{ graphql_vars, parser::{ParseError, ScalarToken, Token}, serde::{de, Deserialize, Deserializer, Serialize}, - EmptyMutation, FieldResult, GraphQLScalarValue, InputValue, Object, ParseScalarResult, - RootNode, ScalarValue, Value, Variables, + EmptyMutation, FieldError, FieldResult, GraphQLScalarValue, InputValue, Object, + ParseScalarResult, RootNode, ScalarValue, Value, Variables, }; #[derive(GraphQLScalarValue, Clone, Debug, PartialEq, Serialize)] @@ -138,11 +138,11 @@ impl GraphQLScalar for i64 { Value::scalar(*self) } - fn from_input_value(v: &InputValue) -> Option { - match *v { - InputValue::Scalar(MyScalarValue::Long(i)) => Some(i), - _ => None, - } + fn from_input_value(v: &InputValue) -> Result { + v.as_scalar_value::() + .copied() + .ok_or_else(|| format!("Expected MyScalarValue::Long(), found: {}", v)) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, MyScalarValue> { diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 719ce7abc..2bda95b2d 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -151,8 +151,11 @@ where /// ``` #[derive(Clone, Debug, PartialEq)] pub struct FieldError { - message: String, - extensions: Value, + /// Error message. + pub message: String, + + /// [`Value`] containing additional information. + pub extensions: Value, } impl From for FieldError { diff --git a/juniper/src/executor_tests/introspection/input_object.rs b/juniper/src/executor_tests/introspection/input_object.rs index b3451463c..d433681a5 100644 --- a/juniper/src/executor_tests/introspection/input_object.rs +++ b/juniper/src/executor_tests/introspection/input_object.rs @@ -206,7 +206,7 @@ fn default_name_input_value() { let dv = DefaultName::from_input_value(&iv); - assert!(dv.is_ok(), "error: {}", dv.unwrap_err()); + assert!(dv.is_ok(), "error: {:?}", dv.unwrap_err()); let dv = dv.unwrap(); diff --git a/juniper/src/executor_tests/introspection/mod.rs b/juniper/src/executor_tests/introspection/mod.rs index 5778d8cb6..b02d7c2f8 100644 --- a/juniper/src/executor_tests/introspection/mod.rs +++ b/juniper/src/executor_tests/introspection/mod.rs @@ -9,8 +9,8 @@ use crate::{ graphql_interface, graphql_object, graphql_scalar, graphql_value, graphql_vars, schema::model::RootNode, types::scalars::{EmptyMutation, EmptySubscription}, - value::{ParseScalarResult, ParseScalarValue, ScalarValue, Value}, - GraphQLEnum, + value::{ParseScalarResult, ParseScalarValue, Value}, + FieldError, GraphQLEnum, }; #[derive(GraphQLEnum)] @@ -28,8 +28,11 @@ impl GraphQLScalar for Scalar { Value::scalar(self.0) } - fn from_input_value(v: &InputValue) -> Option { - v.as_scalar().and_then(ScalarValue::as_int).map(Scalar) + fn from_input_value(v: &InputValue) -> Result { + v.as_int_value() + .map(Scalar) + .ok_or_else(|| format!("Expected Int, found: {}", v)) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { diff --git a/juniper/src/executor_tests/variables.rs b/juniper/src/executor_tests/variables.rs index d4266e0f5..be895dc95 100644 --- a/juniper/src/executor_tests/variables.rs +++ b/juniper/src/executor_tests/variables.rs @@ -5,7 +5,8 @@ use crate::{ schema::model::RootNode, types::scalars::{EmptyMutation, EmptySubscription}, validation::RuleError, - value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, ScalarValue}, + value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue}, + FieldError, GraphQLError::ValidationError, GraphQLInputObject, }; @@ -19,14 +20,11 @@ impl GraphQLScalar for TestComplexScalar { graphql_value!("SerializedValue") } - fn from_input_value(v: &InputValue) -> Option { - 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 { + v.as_string_value() + .filter(|s| *s == "SerializedValue") + .map(|_| TestComplexScalar) + .ok_or_else(|| format!("Expected SerializedValue String, found: {}", v).into()) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { diff --git a/juniper/src/integrations/bson.rs b/juniper/src/integrations/bson.rs index 4dd38dfc9..ee502c1f7 100644 --- a/juniper/src/integrations/bson.rs +++ b/juniper/src/integrations/bson.rs @@ -7,7 +7,7 @@ use crate::{ graphql_scalar, parser::{ParseError, ScalarToken, Token}, value::ParseScalarResult, - Value, + FieldError, Value, }; #[graphql_scalar(description = "ObjectId")] @@ -19,8 +19,13 @@ where Value::scalar(self.to_hex()) } - fn from_input_value(v: &InputValue) -> Option { - v.as_string_value().and_then(|s| Self::parse_str(s).ok()) + fn from_input_value(v: &InputValue) -> Result { + 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 UtcDateTime: {}", e)) + }) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -41,10 +46,15 @@ where Value::scalar((*self).to_chrono().to_rfc3339()) } - fn from_input_value(v: &InputValue) -> Option { + fn from_input_value(v: &InputValue) -> Result { v.as_string_value() - .and_then(|s| (s.parse::>().ok())) + .ok_or_else(|| format!("Expected String, found: {}", v)) + .and_then(|s| { + s.parse::>() + .map_err(|e| format!("Failed to parse UtcDateTime: {}", e)) + }) .map(Self::from_chrono) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { diff --git a/juniper/src/integrations/chrono.rs b/juniper/src/integrations/chrono.rs index eb8fcbb42..e20d1262c 100644 --- a/juniper/src/integrations/chrono.rs +++ b/juniper/src/integrations/chrono.rs @@ -21,7 +21,7 @@ use chrono::prelude::*; use crate::{ parser::{ParseError, ScalarToken, Token}, value::{ParseScalarResult, ParseScalarValue}, - Value, + FieldError, Value, }; #[doc(hidden)] @@ -36,9 +36,13 @@ where Value::scalar(self.to_rfc3339()) } - fn from_input_value(v: &InputValue) -> Option> { - v.as_string_value() - .and_then(|s| DateTime::parse_from_rfc3339(s).ok()) + fn from_input_value(v: &InputValue) -> Result, FieldError> { + Ok(v.as_string_value() + .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,14 @@ where Value::scalar(self.to_rfc3339()) } - fn from_input_value(v: &InputValue) -> Option> { + fn from_input_value(v: &InputValue) -> Result, FieldError> { v.as_string_value() - .and_then(|s| (s.parse::>().ok())) + .ok_or_else(|| format!("Expected String, found: {}", v)) + .and_then(|s| { + s.parse::>() + .map_err(|e| format!("Failed to parse DateTime: {}", e)) + }) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -87,9 +96,14 @@ where Value::scalar(self.format("%Y-%m-%d").to_string()) } - fn from_input_value(v: &InputValue) -> Option { + fn from_input_value(v: &InputValue) -> Result { 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)) + }) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -111,9 +125,14 @@ where Value::scalar(self.format("%H:%M:%S").to_string()) } - fn from_input_value(v: &InputValue) -> Option { + fn from_input_value(v: &InputValue) -> Result { 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)) + }) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -136,9 +155,15 @@ where Value::scalar(self.timestamp() as f64) } - fn from_input_value(v: &InputValue) -> Option { + fn from_input_value(v: &InputValue) -> Result { 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)) + }) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { diff --git a/juniper/src/integrations/chrono_tz.rs b/juniper/src/integrations/chrono_tz.rs index 14963e83b..c7f383a01 100644 --- a/juniper/src/integrations/chrono_tz.rs +++ b/juniper/src/integrations/chrono_tz.rs @@ -9,7 +9,7 @@ use crate::{ graphql_scalar, parser::{ParseError, ScalarToken, Token}, value::ParseScalarResult, - Value, + FieldError, Value, }; #[graphql_scalar(name = "Tz", description = "Timezone")] @@ -21,8 +21,14 @@ where Value::scalar(self.name().to_owned()) } - fn from_input_value(v: &InputValue) -> Option { - v.as_string_value().and_then(|s| s.parse::().ok()) + fn from_input_value(v: &InputValue) -> Result { + v.as_string_value() + .ok_or_else(|| format!("Expected String, found: {}", v)) + .and_then(|s| { + s.parse::() + .map_err(|e| format!("Failed to parse Timezone: {}", e)) + }) + .map_err(Into::into) } fn from_str<'a>(val: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -39,23 +45,24 @@ mod test { mod from_input_value { use chrono_tz::Tz; - use crate::{graphql_input_value, FromInputValue, InputValue}; + use crate::{graphql_input_value, FieldError, FromInputValue, InputValue}; - fn tz_input_test(raw: &'static str, expected: Option) { + fn tz_input_test(raw: &'static str, expected: Result) { let input: InputValue = graphql_input_value!((raw)); - let parsed: Option = FromInputValue::from_input_value(&input); + let parsed = + FromInputValue::from_input_value(&input).map_err(|e: FieldError| e.message); - assert_eq!(parsed, expected); + assert_eq!(parsed, expected.map_err(str::to_owned)); } #[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 +70,26 @@ mod test { #[test] fn forward_slash() { - tz_input_test("Abc/Xyz", None); + tz_input_test( + "Abc/Xyz", + Err("Failed to parse Timezone: received invalid timezone"), + ); } #[test] fn number() { - tz_input_test("8086", None); + tz_input_test( + "8086", + Err("Failed to parse Timezone: received invalid timezone"), + ); } #[test] fn no_forward_slash() { - tz_input_test("AbcXyz", None); + tz_input_test( + "AbcXyz", + Err("Failed to parse Timezone: received invalid timezone"), + ); } } } diff --git a/juniper/src/integrations/url.rs b/juniper/src/integrations/url.rs index 659cba1f2..b3ae25196 100644 --- a/juniper/src/integrations/url.rs +++ b/juniper/src/integrations/url.rs @@ -4,7 +4,7 @@ use url::Url; use crate::{ value::{ParseScalarResult, ParseScalarValue}, - Value, + FieldError, Value, }; #[crate::graphql_scalar(description = "Url")] @@ -16,8 +16,11 @@ where Value::scalar(self.as_str().to_owned()) } - fn from_input_value(v: &InputValue) -> Option { - v.as_string_value().and_then(|s| Url::parse(s).ok()) + fn from_input_value(v: &InputValue) -> Result { + 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))) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { diff --git a/juniper/src/integrations/uuid.rs b/juniper/src/integrations/uuid.rs index 43f716b6f..5dfa7a7bb 100644 --- a/juniper/src/integrations/uuid.rs +++ b/juniper/src/integrations/uuid.rs @@ -7,7 +7,7 @@ use uuid::Uuid; use crate::{ parser::{ParseError, ScalarToken, Token}, value::ParseScalarResult, - Value, + FieldError, Value, }; #[crate::graphql_scalar(description = "Uuid")] @@ -19,8 +19,11 @@ where Value::scalar(self.to_string()) } - fn from_input_value(v: &InputValue) -> Option { - v.as_string_value().and_then(|s| Uuid::parse_str(s).ok()) + fn from_input_value(v: &InputValue) -> Result { + 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))) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index d17f1b399..365261e2e 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -163,7 +163,7 @@ pub use crate::{ introspection::IntrospectionFormat, macros::helper::{ subscription::{ExtractTypeFromStream, IntoFieldResult}, - AsDynGraphQLValue, + AsDynGraphQLValue, ExtractErrorFromResult, }, parser::{ParseError, Spanning}, schema::{ diff --git a/juniper/src/macros/helper/mod.rs b/juniper/src/macros/helper/mod.rs index 8162b7a9c..01caaf562 100644 --- a/juniper/src/macros/helper/mod.rs +++ b/juniper/src/macros/helper/mod.rs @@ -32,3 +32,18 @@ pub trait AsDynGraphQLValue { } crate::sa::assert_obj_safe!(AsDynGraphQLValue); + +/// This trait is used in `juniper::graphql_scalar` macro to [`Result`]s +/// [`Error`]. +/// +/// [`Error`]: Result::Error +pub trait ExtractErrorFromResult { + /// [`Result`]s [`Error`]. + /// + /// [`Error`]: Result::Error + type Error; +} + +impl ExtractErrorFromResult for Result { + type Error = E; +} diff --git a/juniper/src/parser/tests/document.rs b/juniper/src/parser/tests/document.rs index 29959265f..bb28074b0 100644 --- a/juniper/src/parser/tests/document.rs +++ b/juniper/src/parser/tests/document.rs @@ -1,8 +1,5 @@ use crate::{ - ast::{ - Arguments, Definition, Field, InputValue, Operation, OperationType, OwnedDocument, - Selection, - }, + ast::{Arguments, Definition, Field, Operation, OperationType, OwnedDocument, Selection}, graphql_input_value, parser::{document::parse_document_source, ParseError, SourcePosition, Spanning, Token}, schema::model::SchemaType, diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs index e7f4e8cf6..c20e21e52 100644 --- a/juniper/src/schema/meta.rs +++ b/juniper/src/schema/meta.rs @@ -10,7 +10,7 @@ use crate::{ parser::{ParseError, ScalarToken}, schema::model::SchemaType, types::base::TypeKind, - value::{DefaultScalarValue, ParseScalarValue, ScalarValue}, + value::{DefaultScalarValue, ParseScalarValue}, }; /// Whether an item is deprecated, with context. @@ -467,7 +467,10 @@ impl<'a> NullableMeta<'a> { 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 { + pub fn new(name: Cow<'a, str>, fields: &[Field<'a, S>]) -> Self + where + S: Clone, + { Self { name, description: None, @@ -533,11 +536,14 @@ impl<'a, S> EnumMeta<'a, S> { 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 { + pub fn new(name: Cow<'a, str>, fields: &[Field<'a, S>]) -> Self + where + S: Clone, + { Self { name, description: None, - fields: fields.to_owned(), + fields: fields.to_vec(), } } @@ -589,11 +595,12 @@ impl<'a, S> InputObjectMeta<'a, S> { pub fn new(name: Cow<'a, str>, input_fields: &[Argument<'a, S>]) -> Self where T: FromInputValue, + S: Clone, { Self { name, description: None, - input_fields: input_fields.to_owned(), + input_fields: input_fields.to_vec(), try_parse_fn: try_parse_fn::, } } diff --git a/juniper/src/types/base.rs b/juniper/src/types/base.rs index e06d5e943..175be9293 100644 --- a/juniper/src/types/base.rs +++ b/juniper/src/types/base.rs @@ -6,7 +6,7 @@ use crate::{ parser::Spanning, schema::meta::{Argument, MetaType}, value::{DefaultScalarValue, Object, ScalarValue, Value}, - GraphQLEnum, IntoFieldError, FieldResult + FieldResult, GraphQLEnum, IntoFieldError, }; /// GraphQL type kind @@ -78,7 +78,10 @@ impl<'a, S> Arguments<'a, S> { pub fn new( mut args: Option>>, meta_args: &'a Option>>, - ) -> Self { + ) -> Self + where + S: Clone, + { if meta_args.is_some() && args.is_none() { args = Some(IndexMap::new()); } @@ -87,7 +90,7 @@ impl<'a, S> Arguments<'a, S> { for arg in meta_args { 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 { + if let Some(val) = arg.default_value.as_ref() { args.insert(arg_name, val.clone()); } } diff --git a/juniper/src/types/containers.rs b/juniper/src/types/containers.rs index 43b1f5490..053219a4d 100644 --- a/juniper/src/types/containers.rs +++ b/juniper/src/types/containers.rs @@ -1,4 +1,5 @@ use std::{ + array, mem::{self, MaybeUninit}, ptr, }; @@ -315,40 +316,28 @@ where NotEnough(usize), /// Too many elements. Value is [`Vec`] of __all__ [`InputValue`] elements. - TooMuch(Vec), + TooMuch(Vec), /// Underlying [`ScalarValue`] conversion error. Scalar(T::Error), } -impl From for FromInputValueArrayError +impl IntoFieldError for FromInputValueArrayError where T: FromInputValue, - S: ScalarValue, -{ - fn from(err: T::Error) -> Self { - Self::Scalar(err) - } -} - -impl IntoFieldError for FromInputValueArrayError -where - T: FromInputValue, - T::Error: IntoFieldError, + T::Error: IntoFieldError, S: ScalarValue, { fn into_field_error(self) -> FieldError { const ERROR_PREFIX: &str = "Failed to convert into exact-size array"; match self { - Self::NotEnough(len) => FieldError::new( - format!("{}: required {} more elements", ERROR_PREFIX, len), - graphql_value!(null), - ), - Self::TooMuch(el) => FieldError::new( - format!("{}: too much elements: {}", ERROR_PREFIX, el.len()), - graphql_value!(null), - ), + Self::NotEnough(len) => { + FieldError::::from(format!("{}: required {} more elements", ERROR_PREFIX, len)) + } + Self::TooMuch(el) => { + FieldError::::from(format!("{}: too much elements: {}", ERROR_PREFIX, el.len())) + } Self::Scalar(s) => s.into_field_error(), } } @@ -408,7 +397,11 @@ where let mut items = ls.iter().map(|i| i.item.convert()); for (id, elem) in out.arr.iter_mut().enumerate() { - if let Some(i) = items.next().transpose()? { + if let Some(i) = items + .next() + .transpose() + .map_err(FromInputValueArrayError::Scalar)? + { *elem = MaybeUninit::new(i); out.init_len += 1; } else { @@ -431,35 +424,44 @@ where // do nothing on `Drop`. let arr = unsafe { mem::transmute_copy::<_, Self>(&out.arr) }; - if !items.is_empty() { + if items.len() > 0 { return Err(FromInputValueArrayError::TooMuch( - arr.into_iter().map(Ok).chain(items).collect()?, + array::IntoIter::new(arr) + .map(Ok) + .chain(items) + .collect::>() + .map_err(FromInputValueArrayError::Scalar)?, )); } Ok(arr) } ref other => { - other.convert().map_err(Into::into).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 if N == 0 { - Err(FromInputValueArrayError::TooMuch(vec![e])) - } else { - Err(FromInputValueArrayError::NotEnough(N - 1)) - } - }) + 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 if N == 0 { + Err(FromInputValueArrayError::TooMuch(vec![e])) + } else { + Err(FromInputValueArrayError::NotEnough(N - 1)) + } + }) } } } @@ -543,16 +545,14 @@ where mod coercion { use crate::{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() { assert_eq!( - >::from_input_value(&InputValue::list(vec![ - InputValue::scalar(1), - InputValue::scalar(2), - InputValue::scalar(3) - ])), + >::from_input_value(&V::list(vec![V::scalar(1), V::scalar(2), V::scalar(3)])), Ok(vec![1, 2, 3]), ); // TODO: all examples diff --git a/juniper/src/types/pointers.rs b/juniper/src/types/pointers.rs index 857209229..0726ef525 100644 --- a/juniper/src/types/pointers.rs +++ b/juniper/src/types/pointers.rs @@ -93,7 +93,9 @@ where S: ScalarValue, T: FromInputValue, { - fn from_input_value(v: &InputValue) -> Option> { + type Error = T::Error; + + fn from_input_value(v: &InputValue) -> Result, Self::Error> { >::from_input_value(v).map(Box::new) } } @@ -285,7 +287,9 @@ where S: ScalarValue, T: FromInputValue, { - fn from_input_value(v: &InputValue) -> Option> { + type Error = T::Error; + + fn from_input_value(v: &InputValue) -> Result, Self::Error> { >::from_input_value(v).map(Arc::new) } } diff --git a/juniper/src/types/scalars.rs b/juniper/src/types/scalars.rs index 3ebac39c6..a5c6eee35 100644 --- a/juniper/src/types/scalars.rs +++ b/juniper/src/types/scalars.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use crate::{ ast::{InputValue, Selection, ToInputValue}, - executor::{ExecutionResult, Executor, Registry}, + executor::{ExecutionResult, Executor, FieldError, Registry}, parser::{LexerError, ParseError, ScalarToken, Token}, schema::meta::MetaType, types::{ @@ -59,14 +59,13 @@ where Value::scalar(self.0.clone()) } - fn from_input_value(v: &InputValue) -> Option { - 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 { + v.as_string_value() + .map(str::to_owned) + .or_else(|| v.as_int_value().map(|i| i.to_string())) + .ok_or_else(|| format!("Expected String or Int, found: {}", v)) + .map(ID) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -86,11 +85,11 @@ where Value::scalar(self.clone()) } - fn from_input_value(v: &InputValue) -> Option { - match *v { - InputValue::Scalar(ref s) => s.as_string(), - _ => None, - } + fn from_input_value(v: &InputValue) -> Result { + v.as_string_value() + .map(str::to_owned) + .ok_or_else(|| format!("Expected String, found: {}", v)) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -275,11 +274,11 @@ where Value::scalar(*self) } - fn from_input_value(v: &InputValue) -> Option { - match *v { - InputValue::Scalar(ref b) => b.as_boolean(), - _ => None, - } + fn from_input_value(v: &InputValue) -> Result { + v.as_scalar_value() + .and_then(ScalarValue::as_boolean) + .ok_or_else(|| format!("Expected Boolean, found: {}", v)) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -297,11 +296,10 @@ where Value::scalar(*self) } - fn from_input_value(v: &InputValue) -> Option { - match *v { - InputValue::Scalar(ref i) => i.as_int(), - _ => None, - } + fn from_input_value(v: &InputValue) -> Result { + v.as_int_value() + .ok_or_else(|| format!("Expected Int, found: {}", v)) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -324,11 +322,10 @@ where Value::scalar(*self) } - fn from_input_value(v: &InputValue) -> Option { - match *v { - InputValue::Scalar(ref s) => s.as_float(), - _ => None, - } + fn from_input_value(v: &InputValue) -> Result { + v.as_float_value() + .ok_or_else(|| format!("Expected Float, found: {}", v)) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { diff --git a/juniper/src/validation/test_harness.rs b/juniper/src/validation/test_harness.rs index 7bd75f7b0..e626a2ce5 100644 --- a/juniper/src/validation/test_harness.rs +++ b/juniper/src/validation/test_harness.rs @@ -2,7 +2,7 @@ use std::mem; use crate::{ ast::{FromInputValue, InputValue}, - executor::Registry, + executor::{FieldError, Registry}, parser::parse_document_source, schema::{ meta::{EnumValue, MetaType}, @@ -208,12 +208,14 @@ impl FromInputValue for DogCommand where S: ScalarValue, { - fn from_input_value<'a>(v: &InputValue) -> Option { + type Error = FieldError; + + fn from_input_value<'a>(v: &InputValue) -> Result { 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 command".into()), } } } @@ -314,13 +316,15 @@ impl FromInputValue for FurColor where S: ScalarValue, { - fn from_input_value<'a>(v: &InputValue) -> Option { + type Error = FieldError; + + fn from_input_value<'a>(v: &InputValue) -> Result { 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 color".into()), } } } @@ -612,21 +616,37 @@ impl FromInputValue for ComplexInput where S: ScalarValue, { - fn from_input_value<'a>(v: &InputValue) -> Option { - let obj = match v.to_object_value() { - Some(o) => o, - None => return None, - }; - - 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()), + type Error = FieldError; + + fn from_input_value<'a>(v: &InputValue) -> Result { + let obj = v.to_object_value().ok_or("Expected object")?; + + Ok(ComplexInput { + required_field: obj + .get("requiredField") + .map(|v| v.convert().map_err(FieldError::map_scalar_value)) + .transpose()? + .ok_or("Expected requiredField")?, + int_field: obj + .get("intField") + .map(|v| v.convert().map_err(FieldError::map_scalar_value)) + .transpose()? + .ok_or("Expected requiredField")?, + string_field: obj + .get("stringField") + .map(|v| v.convert().map_err(FieldError::map_scalar_value)) + .transpose()? + .ok_or("Expected requiredField")?, + boolean_field: obj + .get("booleanField") + .map(|v| v.convert().map_err(FieldError::map_scalar_value)) + .transpose()? + .ok_or("Expected requiredField")?, + string_list_field: obj + .get("stringListField") + .map(|v| v.convert().map_err(FieldError::map_scalar_value)) + .transpose()? + .ok_or("Expected requiredField")?, }) } } diff --git a/juniper_codegen/src/common/field/arg.rs b/juniper_codegen/src/common/field/arg.rs index 5dfaa904a..56e01afca 100644 --- a/juniper_codegen/src/common/field/arg.rs +++ b/juniper_codegen/src/common/field/arg.rs @@ -351,14 +351,63 @@ impl OnMethod { 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, - ); + let err_text = format!("Missing argument `{}`: {{}}", &name); quote! { args.get::<#ty>(#name) - .or_else(::juniper::FromInputValue::<#scalar>::from_implicit_null) - .expect(#err_text) + .and_then(|opt| { + opt.map_or_else( + || { + <#ty as ::juniper::FromInputValue<#scalar>>::from_implicit_null() + .map_err(|e| { + let mut e = ::juniper::IntoFieldError::into_field_error(e); + e.message = format!(#err_text, e.message); + e + }) + }, + Ok, + ) + })? + } + } + + Self::Context(_) => quote! { + ::juniper::FromContext::from(executor.context()) + }, + + Self::Executor => quote! { &executor }, + } + } + + /// Returns generated code for the [`GraphQLValue::resolve_field`] method, + /// which provides the value of this [`OnMethod`] argument to be passed into + /// a trait method call. + /// + /// [`GraphQLValue::resolve_field`]: juniper::GraphQLValue::resolve_field + #[must_use] + pub(crate) fn method_resolve_field_async_tokens(&self, scalar: &scalar::Type) -> TokenStream { + match self { + Self::Regular(arg) => { + let (name, ty) = (&arg.name, &arg.ty); + let err_text = format!("Missing argument `{}`: {{}}", &name); + quote! { + match args.get::<#ty>(#name) + .and_then(|opt| { + opt.map_or_else( + || { + <#ty as ::juniper::FromInputValue<#scalar>>::from_implicit_null() + .map_err(|e| { + let mut e = ::juniper::IntoFieldError::into_field_error(e); + e.message = format!(#err_text, e.message); + e + }) + }, + Ok, + ) + }) + { + Ok(ok) => ok, + Err(err) => return Box::pin(async { Err(err) }), + } } } diff --git a/juniper_codegen/src/common/field/mod.rs b/juniper_codegen/src/common/field/mod.rs index a627e444b..360cb28bb 100644 --- a/juniper_codegen/src/common/field/mod.rs +++ b/juniper_codegen/src/common/field/mod.rs @@ -274,13 +274,14 @@ impl Definition { /// [`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) -> TokenStream { quote! { - panic!( + return Err(::juniper::FieldError::<#scalar>::from(format!( "Field `{}` not found on type `{}`", field, - >::name(info).unwrap(), - ) + >::name(info) + .ok_or_else(|| ::juniper::FieldError::<#scalar>::from("No name for GraphQLType"))?, + ))) } } @@ -291,16 +292,19 @@ impl Definition { /// [`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, ) -> TokenStream { quote! { - #( #field_names )|* => panic!( - "Tried to resolve async field `{}` on type `{}` with a sync resolver", - field, - >::name(info).unwrap(), - ), + #( #field_names )|* => return Err(::juniper::FieldError::<#scalar>::from( + format!( + "Tried to resolve async field `{}` on type `{}` with a sync resolver", + field, + >::name(info) + .ok_or_else(|| ::juniper::FieldError::<#scalar>::from("No name for GraphQLType"))?, + ) + )), } } @@ -455,7 +459,7 @@ impl Definition { .as_ref() .unwrap() .iter() - .map(|arg| arg.method_resolve_field_tokens(scalar)); + .map(|arg| arg.method_resolve_field_async_tokens(scalar)); let rcv = self.has_receiver.then(|| { quote! { self, } diff --git a/juniper_codegen/src/derive_scalar_value.rs b/juniper_codegen/src/derive_scalar_value.rs index 12a5136b6..a3c87c674 100644 --- a/juniper_codegen/src/derive_scalar_value.rs +++ b/juniper_codegen/src/derive_scalar_value.rs @@ -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)) } } diff --git a/juniper_codegen/src/graphql_interface/mod.rs b/juniper_codegen/src/graphql_interface/mod.rs index 32d01cfeb..60c64db68 100644 --- a/juniper_codegen/src/graphql_interface/mod.rs +++ b/juniper_codegen/src/graphql_interface/mod.rs @@ -585,10 +585,10 @@ impl Definition { .filter_map(|f| f.is_async.then(|| f.name.as_str())) .collect::>(); (!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) }) }; - 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); let custom_downcast_checks = self .implementers @@ -624,7 +624,7 @@ impl Definition { match field { #( #fields_resolvers )* #async_fields_panic - _ => #no_field_panic, + _ => #no_field_err, } } @@ -668,7 +668,7 @@ impl Definition { .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); let custom_downcasts = self .implementers @@ -690,7 +690,7 @@ impl Definition { ) -> ::juniper::BoxFuture<'b, ::juniper::ExecutionResult<#scalar>> { match field { #( #fields_resolvers )* - _ => #no_field_panic, + _ => Box::pin(async move { #no_field_err }), } } diff --git a/juniper_codegen/src/graphql_object/mod.rs b/juniper_codegen/src/graphql_object/mod.rs index 267fde795..0157bcca5 100644 --- a/juniper_codegen/src/graphql_object/mod.rs +++ b/juniper_codegen/src/graphql_object/mod.rs @@ -500,10 +500,10 @@ impl Definition { .filter_map(|f| f.is_async.then(|| f.name.as_str())) .collect::>(); (!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) }) }; - 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); quote! { #[allow(deprecated)] @@ -527,7 +527,7 @@ impl Definition { match field { #( #fields_resolvers )* #async_fields_panic - _ => #no_field_panic, + _ => #no_field_err, } } @@ -558,7 +558,7 @@ impl Definition { .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); quote! { #[allow(deprecated, non_snake_case)] @@ -574,7 +574,7 @@ impl Definition { ) -> ::juniper::BoxFuture<'b, ::juniper::ExecutionResult<#scalar>> { match field { #( #fields_resolvers )* - _ => #no_field_panic, + _ => Box::pin(async move { #no_field_err }), } } } diff --git a/juniper_codegen/src/graphql_subscription/mod.rs b/juniper_codegen/src/graphql_subscription/mod.rs index 8f6b0e06f..01b03702d 100644 --- a/juniper_codegen/src/graphql_subscription/mod.rs +++ b/juniper_codegen/src/graphql_subscription/mod.rs @@ -100,7 +100,7 @@ impl Definition { .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); quote! { #[allow(deprecated)] @@ -130,7 +130,7 @@ impl Definition { { match field { #( #fields_resolvers )* - _ => #no_field_panic, + _ => Box::pin(async move { #no_field_err }), } } } diff --git a/juniper_codegen/src/impl_scalar.rs b/juniper_codegen/src/impl_scalar.rs index 8109f147f..f05ea91ba 100644 --- a/juniper_codegen/src/impl_scalar.rs +++ b/juniper_codegen/src/impl_scalar.rs @@ -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::ExtractErrorFromResult>::Error; + fn from_input_value(#from_input_value_arg: &::juniper::InputValue<#generic_type>) -> #from_input_value_result { #from_input_value_body } diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs index e0b0729c9..f4f3cc475 100644 --- a/juniper_codegen/src/lib.rs +++ b/juniper_codegen/src/lib.rs @@ -230,8 +230,10 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream { /// juniper::Value::scalar(self.0.to_owned()) /// } /// -/// fn from_input_value(value: &juniper::InputValue) -> Option { -/// value.as_string_value().map(|s| UserID(s.to_owned())) +/// fn from_input_value(value: &juniper::InputValue) -> Result { +/// Ok(value.as_string_value() +/// .map(|s| UserID(s.to_owned())) +/// .ok_or_else(|| format!("Expected String, found: {}", v))?) /// } /// /// fn from_str<'a>(value: juniper::ScalarToken<'a>) -> juniper::ParseScalarResult<'a, S> { diff --git a/juniper_codegen/src/util/mod.rs b/juniper_codegen/src/util/mod.rs index ae84fa65d..bb9bcbf44 100644 --- a/juniper_codegen/src/util/mod.rs +++ b/juniper_codegen/src/util/mod.rs @@ -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,18 @@ impl GraphQLTypeDefiniton { impl#impl_generics ::juniper::FromInputValue<#scalar> for #ty #where_clause { - fn from_input_value(v: &::juniper::InputValue<#scalar>) -> Option<#ty> - { + type Error = ::juniper::FieldError<#scalar>; + + fn from_input_value( + v: &::juniper::InputValue<#scalar> + ) -> Result<#ty, ::juniper::FieldError<#scalar>> { match v.as_enum_value().or_else(|| { v.as_string_value() }) { #( #from_inputs )* - _ => None, + _ => Err(::juniper::FieldError::<#scalar>::from( + format!("Unknown Enum value: {}", v)) + ), } } } @@ -980,8 +985,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::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 +1107,16 @@ impl GraphQLTypeDefiniton { impl#impl_generics ::juniper::FromInputValue<#scalar> for #ty #type_generics_tokens #where_clause { - fn from_input_value(value: &::juniper::InputValue<#scalar>) -> Option - { - let obj = value.to_object_value()?; - Some(#ty { + type Error = ::juniper::FieldError<#scalar>; + + fn from_input_value( + value: &::juniper::InputValue<#scalar> + ) -> Result> { + let obj = value.to_object_value() + .ok_or_else(|| ::juniper::FieldError::<#scalar>::from( + format!("Expected Object, found: {}", value)) + )?; + Ok(#ty { #( #from_inputs )* }) } From 111e464d5844847dd75502d45ebd1449d6992c75 Mon Sep 17 00:00:00 2001 From: ilslv Date: Wed, 1 Dec 2021 14:27:56 +0300 Subject: [PATCH 06/25] Remove more unwraps and panics --- juniper_codegen/src/graphql_interface/mod.rs | 21 +++++++---- juniper_codegen/src/graphql_object/mod.rs | 4 +-- .../src/graphql_subscription/mod.rs | 4 ++- juniper_codegen/src/graphql_union/mod.rs | 35 +++++++++++++------ 4 files changed, 44 insertions(+), 20 deletions(-) diff --git a/juniper_codegen/src/graphql_interface/mod.rs b/juniper_codegen/src/graphql_interface/mod.rs index 60c64db68..a509d3cf1 100644 --- a/juniper_codegen/src/graphql_interface/mod.rs +++ b/juniper_codegen/src/graphql_interface/mod.rs @@ -578,7 +578,7 @@ impl Definition { .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() @@ -623,7 +623,7 @@ impl Definition { ) -> ::juniper::ExecutionResult<#scalar> { match field { #( #fields_resolvers )* - #async_fields_panic + #async_fields_err _ => #no_field_err, } } @@ -847,7 +847,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::FieldError::from("This GraphQLType has no name"))? + { let res = #downcast; return #resolving_code; } @@ -875,9 +877,16 @@ 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 Box::pin(::juniper::futures::future::ready( + Err(::juniper::FieldError::from("This GraphQLType has no name")) + )), } }) } diff --git a/juniper_codegen/src/graphql_object/mod.rs b/juniper_codegen/src/graphql_object/mod.rs index 0157bcca5..dd9a1b3a6 100644 --- a/juniper_codegen/src/graphql_object/mod.rs +++ b/juniper_codegen/src/graphql_object/mod.rs @@ -493,7 +493,7 @@ impl Definition { .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() @@ -526,7 +526,7 @@ impl Definition { ) -> ::juniper::ExecutionResult<#scalar> { match field { #( #fields_resolvers )* - #async_fields_panic + #async_fields_err _ => #no_field_err, } } diff --git a/juniper_codegen/src/graphql_subscription/mod.rs b/juniper_codegen/src/graphql_subscription/mod.rs index 01b03702d..288547485 100644 --- a/juniper_codegen/src/graphql_subscription/mod.rs +++ b/juniper_codegen/src/graphql_subscription/mod.rs @@ -60,7 +60,9 @@ impl Definition { _: &::juniper::Arguments<#scalar>, _: &::juniper::Executor, ) -> ::juniper::ExecutionResult<#scalar> { - panic!("Called `resolve_field` on subscription object"); + Err(::juniper::FieldError::from( + "Called `resolve_field` on subscription object" + )) } fn concrete_type_name( diff --git a/juniper_codegen/src/graphql_union/mod.rs b/juniper_codegen/src/graphql_union/mod.rs index c882c24ba..040ce97f4 100644 --- a/juniper_codegen/src/graphql_union/mod.rs +++ b/juniper_codegen/src/graphql_union/mod.rs @@ -558,11 +558,12 @@ 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 +601,14 @@ impl Definition { ) -> ::juniper::BoxFuture<'b, ::juniper::ExecutionResult<#scalar>> { let context = executor.context(); #( #variant_async_resolvers )* - panic!( - "Concrete type `{}` is not handled by instance \ - resolvers on GraphQL union `{}`", - type_name, #name, - ); + + return Box::pin(::juniper::futures::future::ready( + Err(::juniper::FieldError::from(format!( + "Concrete type `{}` is not handled by instance \ + resolvers on GraphQL union `{}`", + type_name, #name, + ))) + )); } } } @@ -674,7 +678,9 @@ impl VariantDefinition { 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::FieldError::from("This GraphQLType has no name"))? + { let res = { #expr }; return #resolving_code; } @@ -694,9 +700,16 @@ impl VariantDefinition { 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 Box::pin(::juniper::futures::future::ready( + Err(::juniper::FieldError::from("This GraphQLType has no name")) + )), } } } From 555db09585d6f9f2f20850c9b1df07b8732a1b99 Mon Sep 17 00:00:00 2001 From: ilslv Date: Wed, 1 Dec 2021 15:21:37 +0300 Subject: [PATCH 07/25] Fix ui codegen tests --- err.txt | 836 ++++++++++++++++++ .../fail/enum/derive_no_fields.stderr | 9 +- .../derive_incompatible_object.stderr | 40 +- .../fail/input-object/derive_no_fields.stderr | 9 +- .../input-object/derive_no_underscore.stderr | 7 +- .../input-object/derive_unique_name.stderr | 12 +- .../argument_double_underscored.stderr | 5 +- .../interface/argument_non_input_type.stderr | 20 +- ...conflicts_with_external_downcast_fn.stderr | 9 +- .../downcast_method_wrong_input_args.stderr | 7 +- .../downcast_method_wrong_return_type.stderr | 13 +- .../interface/field_double_underscored.stderr | 11 +- .../field_non_output_return_type.stderr | 18 +- .../fail/interface/fields_duplicate.stderr | 21 +- .../implementer_non_object_type.stderr | 26 +- .../interface/name_double_underscored.stderr | 5 +- .../fail/interface/no_fields.stderr | 7 +- .../object/argument_double_underscored.stderr | 7 +- .../object/argument_non_input_type.stderr | 20 +- .../attr_field_non_output_return_type.stderr | 18 +- .../fail/object/attr_fields_duplicate.stderr | 7 +- .../attr_name_double_underscored.stderr | 7 +- .../fail/object/attr_no_fields.stderr | 7 +- .../derive_field_double_underscored.stderr | 7 +- ...derive_field_non_output_return_type.stderr | 18 +- .../object/derive_fields_duplicate.stderr | 15 +- .../derive_name_double_underscored.stderr | 7 +- .../fail/object/derive_no_fields.stderr | 9 +- .../fail/object/derive_wrong_item.stderr | 4 +- .../argument_double_underscored.stderr | 7 +- .../argument_non_input_type.stderr | 20 +- .../field_non_output_return_type.stderr | 18 +- .../fail/subscription/field_not_async.stderr | 11 +- .../fail/subscription/fields_duplicate.stderr | 7 +- .../name_double_underscored.stderr | 7 +- .../fail/subscription/no_fields.stderr | 7 +- .../fail/union/derive_wrong_item.stderr | 4 +- ...s_with_variant_external_resolver_fn.stderr | 7 +- .../union/enum_name_double_underscored.stderr | 7 +- .../fail/union/enum_no_fields.stderr | 9 +- .../fail/union/enum_non_object_variant.stderr | 18 +- .../fail/union/enum_same_type_pretty.stderr | 14 +- .../union/enum_wrong_variant_field.stderr | 14 +- .../struct_name_double_underscored.stderr | 7 +- .../fail/union/struct_no_fields.stderr | 9 +- .../union/struct_non_object_variant.stderr | 18 +- .../union/trait_fail_infer_context.stderr | 20 +- ...conflicts_with_external_resolver_fn.stderr | 11 +- .../trait_name_double_underscored.stderr | 7 +- .../fail/union/trait_no_fields.stderr | 9 +- .../union/trait_non_object_variant.stderr | 18 +- .../fail/union/trait_same_type_pretty.stderr | 14 +- .../union/trait_with_attr_on_method.stderr | 9 +- .../trait_wrong_method_input_args.stderr | 9 +- .../trait_wrong_method_return_type.stderr | 9 +- juniper_codegen/src/lib.rs | 2 +- 56 files changed, 1196 insertions(+), 277 deletions(-) create mode 100644 err.txt diff --git a/err.txt b/err.txt new file mode 100644 index 000000000..e397bf1d6 --- /dev/null +++ b/err.txt @@ -0,0 +1,836 @@ +/Users/work/.cargo/bin/cargo +stable test --color=always -p juniper_codegen_tests --all-features --no-fail-fast -- --format=json -Z unstable-options --show-output +Testing started at 15:02 ... + Finished test [unoptimized + debuginfo] target(s) in 0.20s + Running unittests (/Users/work/Work/juniper/target/debug/deps/juniper_codegen_tests-0ab20148dc3374c3) + Checking juniper_codegen_tests-tests v0.0.0 (/Users/work/Work/juniper/target/tests/juniper_codegen_tests) + Finished dev [unoptimized + debuginfo] target(s) in 0.28s + + +test fail/interface/field_double_underscored.rs ... ok +test fail/interface/implementers_duplicate_pretty.rs ... ok +test fail/interface/downcast_method_wrong_input_args.rs ... ok +test fail/interface/argument_wrong_default_array.rs ... ok +test fail/interface/wrong_item.rs ... ok +test fail/interface/name_double_underscored.rs ... ok +test fail/interface/argument_double_underscored.rs ... ok +test fail/interface/argument_non_input_type.rs ... ok +test fail/interface/downcast_method_conflicts_with_external_downcast_fn.rs ... ok +test fail/interface/no_fields.rs ... ok +test fail/interface/field_non_output_return_type.rs ... ok +test fail/interface/implementers_duplicate_ugly.rs ... ok +test fail/interface/downcast_method_wrong_return_type.rs ... ok +test fail/interface/fields_duplicate.rs ... ok +test fail/interface/implementer_non_object_type.rs ... ok +test fail/input-object/derive_no_fields.rs ... ok +test fail/input-object/derive_unique_name.rs ... ok +test fail/input-object/derive_no_underscore.rs ... ok +test fail/input-object/derive_incompatible_object.rs ... ok +test fail/enum/derive_no_fields.rs ... ok +test fail/subscription/field_double_underscored.rs ... ok +test fail/subscription/field_not_async.rs ... ok +test fail/subscription/argument_wrong_default_array.rs ... ok +test fail/subscription/wrong_item.rs ... ok +test fail/subscription/name_double_underscored.rs ... ok +test fail/subscription/argument_double_underscored.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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 + + --> fail/subscription/argument_double_underscored.rs:11:24 + | +11 | async fn id(&self, __num: i32) -> Stream<'static, &'static str> { + | +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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 + + --> fail/subscription/argument_double_underscored.rs:11:24 + | +11 | async fn id(&self, __num: i32) -> Stream<'static, &'static str> { + | ^^^^^ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/subscription/argument_non_input_type.rs ... ok +test fail/subscription/no_fields.rs ... ok +test fail/subscription/field_non_output_return_type.rs ... ok +test fail/subscription/fields_duplicate.rs ... ok +test fail/object/derive_field_double_underscored.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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 + | +5 | __test: String, + | ^^^^^^ + | + = note: https://spec.graphql.org/June2018/#sec-Schema +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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 + + --> fail/object/derive_field_double_underscored.rs:5:5 + | +5 | __test: String, + | ^^^^^^ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/object/derive_no_fields.rs ... ok +test fail/object/derive_wrong_item.rs ... ok +test fail/object/attr_fields_duplicate.rs ... ok +test fail/object/argument_wrong_default_array.rs ... ok +test fail/object/argument_double_underscored.rs ... ok +test fail/object/argument_non_input_type.rs ... ok +test fail/object/attr_no_fields.rs ... { "type": "test", "event": "timeout", "name": "test_failing_compiliation" } +ok +test fail/object/attr_name_double_underscored.rs ... ok +test fail/object/derive_name_double_underscored.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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 + + --> fail/object/derive_field_double_underscored.rs:5:5 + | +5 | __test: String, + | ^^^^^^ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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 + + --> fail/object/derive_name_double_underscored.rs:4:8 + | +4 | struct __Obj { + | ^^^^^ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/object/derive_fields_duplicate.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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 +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL object must have a different name for each field + + = note: https://spec.graphql.org/June2018/#sec-Objects + + --> fail/object/derive_fields_duplicate.rs:4:1 + | +4 | struct ObjA { + | ^^^^^^ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/object/attr_wrong_item.rs ... ok +test fail/object/attr_field_non_output_return_type.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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) +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error[E0277]: the trait bound `ObjB: IsOutputType<__S>` is not satisfied + --> 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) +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/object/attr_field_double_underscored.rs ... ok +test fail/object/derive_field_non_output_return_type.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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) +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error[E0277]: the trait bound `ObjB: IsOutputType<__S>` is not satisfied + --> 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) +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/union/enum_wrong_variant_field.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union enum allows only unnamed variants with a single field, e.g. `Some(T)` + --> $DIR/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 + | +10 | A(Human, u8), + | ^^^^^^^^^^^ + | + = note: https://spec.graphql.org/June2018/#sec-Unions +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union enum allows only unnamed variants with a single field, e.g. `Some(T)` + + = note: https://spec.graphql.org/June2018/#sec-Unions + + --> fail/union/enum_wrong_variant_field.rs:5:5 + | +5 | A { human: Human }, + | ^ + +error: GraphQL union enum allows only unnamed variants with a single field, e.g. `Some(T)` + + = note: https://spec.graphql.org/June2018/#sec-Unions + + --> fail/union/enum_wrong_variant_field.rs:10:6 + | +10 | A(Human, u8), + | ^^^^^^^^^^^ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/union/struct_non_object_variant.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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) +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error[E0277]: the trait bound `Test: GraphQLObject<__S>` is not satisfied + --> 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) +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/union/struct_same_type_pretty.rs ... ok +test fail/union/trait_fail_infer_context.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error[E0277]: the trait bound `CustomContext: FromContext` is not satisfied + --> $DIR/trait_fail_infer_context.rs:3:1 + | +3 | #[graphql_union] + | ^^^^^^^^^^^^^^^^ expected an implementor of trait `FromContext` + | + = 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) + +error[E0308]: mismatched types + --> $DIR/trait_fail_infer_context.rs:3:1 + | +3 | #[graphql_union] + | ^^^^^^^^^^^^^^^^ expected struct `CustomContext`, found struct `SubContext` + | + = note: expected reference `&CustomContext` + found reference `&SubContext` + = note: this error originates in the attribute macro `graphql_union` (in Nightly builds, run with -Z macro-backtrace for more info) +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error[E0277]: the trait bound `CustomContext: FromContext` is not satisfied + --> fail/union/trait_fail_infer_context.rs:3:1 + | +3 | #[graphql_union] + | ^^^^^^^^^^^^^^^^ expected an implementor of trait `FromContext` + | +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 + --> fail/union/trait_fail_infer_context.rs:3:1 + | +3 | #[graphql_union] + | ^^^^^^^^^^^^^^^^ expected struct `CustomContext`, found struct `SubContext` + | + = note: expected reference `&CustomContext` + found reference `&SubContext` + = note: this error originates in the attribute macro `graphql_union` (in Nightly builds, run with -Z macro-backtrace for more info) +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/union/derive_wrong_item.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union can only be derived for enums and structs + --> $DIR/derive_wrong_item.rs:4:1 + | +4 | union Character { id: i32 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union can only be derived for enums and structs + --> fail/union/derive_wrong_item.rs:4:1 + | +4 | union Character { id: i32 } + | ^^^^^ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/union/enum_no_fields.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union expects at least one union variant + --> $DIR/enum_no_fields.rs:4:1 + | +4 | enum Character {} + | ^^^^^^^^^^^^^^^^^ + | + = note: https://spec.graphql.org/June2018/#sec-Unions +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union expects at least one union variant + + = note: https://spec.graphql.org/June2018/#sec-Unions + + --> fail/union/enum_no_fields.rs:4:1 + | +4 | enum Character {} + | ^^^^ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/union/enum_same_type_pretty.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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 +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union must have a different type for each union variant + + = note: https://spec.graphql.org/June2018/#sec-Unions + + --> fail/union/enum_same_type_pretty.rs:4:1 + | +4 | enum Character { + | ^^^^ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/union/trait_no_fields.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union expects at least one union variant + --> $DIR/trait_no_fields.rs:4:1 + | +4 | trait Character {} + | ^^^^^^^^^^^^^^^^^^ + | + = note: https://spec.graphql.org/June2018/#sec-Unions +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union expects at least one union variant + + = note: https://spec.graphql.org/June2018/#sec-Unions + + --> fail/union/trait_no_fields.rs:4:1 + | +4 | trait Character {} + | ^^^^^ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/union/enum_same_type_ugly.rs ... ok +test fail/union/trait_same_type_ugly.rs ... ok +test fail/union/enum_non_object_variant.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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) +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error[E0277]: the trait bound `Test: GraphQLObject<__S>` is not satisfied + --> 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) +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/union/trait_with_attr_on_method.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union cannot use #[graphql(with = ...)] attribute on a trait method + --> $DIR/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 +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union cannot use #[graphql(with = ...)] attribute on a trait method + + = 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: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/union/trait_wrong_method_input_args.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union expects trait method to accept `&self` only and, optionally, `&Context` + --> $DIR/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 +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union expects trait method to accept `&self` only and, optionally, `&Context` + + = 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: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/union/trait_name_double_underscored.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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 + | +4 | trait __Character { + | ^^^^^^^^^^^ + | + = note: https://spec.graphql.org/June2018/#sec-Schema +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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 + + --> fail/union/trait_name_double_underscored.rs:4:7 + | +4 | trait __Character { + | ^^^^^^^^^^^ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/union/enum_external_resolver_fn_conflicts_with_variant_external_resolver_fn.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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 + | +6 | #[graphql(with = resolve_fn2)] + | ^^^^ + | + = note: https://spec.graphql.org/June2018/#sec-Unions +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union variant `Human` already has external resolver function `resolve_fn1` declared on the enum + + = 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: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/union/trait_same_type_pretty.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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 +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union must have a different type for each union variant + + = note: https://spec.graphql.org/June2018/#sec-Unions + + --> fail/union/trait_same_type_pretty.rs:4:1 + | +4 | trait Character { + | ^^^^^ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/union/attr_wrong_item.rs ... ok +test fail/union/struct_no_fields.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union expects at least one union variant + --> $DIR/struct_no_fields.rs:4:1 + | +4 | struct Character; + | ^^^^^^^^^^^^^^^^^ + | + = note: https://spec.graphql.org/June2018/#sec-Unions +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union expects at least one union variant + + = note: https://spec.graphql.org/June2018/#sec-Unions + + --> fail/union/struct_no_fields.rs:4:1 + | +4 | struct Character; + | ^^^^^^ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/union/struct_name_double_underscored.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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 + | +5 | struct __Character; + | ^^^^^^^^^^^ + | + = note: https://spec.graphql.org/June2018/#sec-Schema +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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 + + --> fail/union/struct_name_double_underscored.rs:5:8 + | +5 | struct __Character; + | ^^^^^^^^^^^ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/union/trait_method_conflicts_with_external_resolver_fn.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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 +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union trait method `a` conflicts with the external resolver function `some_fn` declared on the trait to resolve the variant type `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>; + | ^^ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/union/enum_name_double_underscored.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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 + | +4 | enum __Character { + | ^^^^^^^^^^^ + | + = note: https://spec.graphql.org/June2018/#sec-Schema +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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 + + --> fail/union/enum_name_double_underscored.rs:4:6 + | +4 | enum __Character { + | ^^^^^^^^^^^ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/union/trait_non_object_variant.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +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) +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error[E0277]: the trait bound `Test: GraphQLObject<__S>` is not satisfied + --> 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) +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + +test fail/union/struct_same_type_ugly.rs ... ok +test fail/union/trait_wrong_method_return_type.rs ... mismatch + +EXPECTED: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union expects trait method return type to be `Option<&VariantType>` only + --> $DIR/trait_wrong_method_return_type.rs:5:20 + | +5 | fn a(&self) -> &Human; + | ^^^^^^ + | + = note: https://spec.graphql.org/June2018/#sec-Unions +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ + +ACTUAL OUTPUT: +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +error: GraphQL union expects trait method return type to be `Option<&VariantType>` only + + = note: https://spec.graphql.org/June2018/#sec-Unions + + --> fail/union/trait_wrong_method_return_type.rs:5:20 + | +5 | fn a(&self) -> &Human; + | ^ +┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ +note: If the actual output is the correct output you can bless it by rerunning + your test with the environment variable TRYBUILD=overwrite + + + + +25 of 69 tests failed +thread 'test_failing_compiliation' panicked at '25 of 69 tests failed', /Users/work/.cargo/registry/src/git.colasdn.top-1ecc6299db9ec823/trybuild-1.0.52/src/run.rs:74:13 +stack backtrace: + 0: rust_begin_unwind + at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/std/src/panicking.rs:517:5 + 1: std::panicking::begin_panic_fmt + at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/std/src/panicking.rs:460:5 + 2: trybuild::run::::run + at /Users/work/.cargo/registry/src/git.colasdn.top-1ecc6299db9ec823/trybuild-1.0.52/src/run.rs:74:13 + 3: ::drop + at /Users/work/.cargo/registry/src/git.colasdn.top-1ecc6299db9ec823/trybuild-1.0.52/src/lib.rs:306:13 + 4: core::ptr::drop_in_place + at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/core/src/ptr/mod.rs:188:1 + 5: juniper_codegen_tests::test_failing_compiliation + at ./src/lib.rs:38:1 + 6: juniper_codegen_tests::test_failing_compiliation::{{closure}} + at ./src/lib.rs:28:1 + 7: core::ops::function::FnOnce::call_once + at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/core/src/ops/function.rs:227:5 + 8: core::ops::function::FnOnce::call_once + at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/core/src/ops/function.rs:227:5 +note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. + + Doc-tests juniper_codegen_tests +error: test failed, to rerun pass '-p juniper_codegen_tests --lib' + +Process finished with exit code 101 diff --git a/integration_tests/codegen_fail/fail/enum/derive_no_fields.stderr b/integration_tests/codegen_fail/fail/enum/derive_no_fields.stderr index ccfc49515..8c7fc3604 100644 --- a/integration_tests/codegen_fail/fail/enum/derive_no_fields.stderr +++ b/integration_tests/codegen_fail/fail/enum/derive_no_fields.stderr @@ -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 + | ^^^ diff --git a/integration_tests/codegen_fail/fail/input-object/derive_incompatible_object.stderr b/integration_tests/codegen_fail/fail/input-object/derive_incompatible_object.stderr index 988fbab5c..6c4e0ed60 100644 --- a/integration_tests/codegen_fail/fail/input-object/derive_incompatible_object.stderr +++ b/integration_tests/codegen_fail/fail/input-object/derive_incompatible_object.stderr @@ -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) -> Result; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = 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 diff --git a/integration_tests/codegen_fail/fail/input-object/derive_no_fields.stderr b/integration_tests/codegen_fail/fail/input-object/derive_no_fields.stderr index 883292328..edb0aa99e 100644 --- a/integration_tests/codegen_fail/fail/input-object/derive_no_fields.stderr +++ b/integration_tests/codegen_fail/fail/input-object/derive_no_fields.stderr @@ -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 + | ^^^^^^ diff --git a/integration_tests/codegen_fail/fail/input-object/derive_no_underscore.stderr b/integration_tests/codegen_fail/fail/input-object/derive_no_underscore.stderr index eb7eb67b5..08e367306 100644 --- a/integration_tests/codegen_fail/fail/input-object/derive_no_underscore.stderr +++ b/integration_tests/codegen_fail/fail/input-object/derive_no_underscore.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/input-object/derive_unique_name.stderr b/integration_tests/codegen_fail/fail/input-object/derive_unique_name.stderr index ccecaf48d..019df45d4 100644 --- a/integration_tests/codegen_fail/fail/input-object/derive_unique_name.stderr +++ b/integration_tests/codegen_fail/fail/input-object/derive_unique_name.stderr @@ -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")] + | ^ diff --git a/integration_tests/codegen_fail/fail/interface/argument_double_underscored.stderr b/integration_tests/codegen_fail/fail/interface/argument_double_underscored.stderr index f06c385cc..b36017e02 100644 --- a/integration_tests/codegen_fail/fail/interface/argument_double_underscored.stderr +++ b/integration_tests/codegen_fail/fail/interface/argument_double_underscored.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/interface/argument_non_input_type.stderr b/integration_tests/codegen_fail/fail/interface/argument_non_input_type.stderr index 131e43e2d..44ae096b8 100644 --- a/integration_tests/codegen_fail/fail/interface/argument_non_input_type.stderr +++ b/integration_tests/codegen_fail/fail/interface/argument_non_input_type.stderr @@ -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` diff --git a/integration_tests/codegen_fail/fail/interface/downcast_method_conflicts_with_external_downcast_fn.stderr b/integration_tests/codegen_fail/fail/interface/downcast_method_conflicts_with_external_downcast_fn.stderr index a8abef2df..ce90439b4 100644 --- a/integration_tests/codegen_fail/fail/interface/downcast_method_conflicts_with_external_downcast_fn.stderr +++ b/integration_tests/codegen_fail/fail/interface/downcast_method_conflicts_with_external_downcast_fn.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_input_args.stderr b/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_input_args.stderr index 718a0a251..543c8cfff 100644 --- a/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_input_args.stderr +++ b/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_input_args.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_return_type.stderr b/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_return_type.stderr index 8c4594950..164c78ccf 100644 --- a/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_return_type.stderr +++ b/integration_tests/codegen_fail/fail/interface/downcast_method_wrong_return_type.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/interface/field_double_underscored.stderr b/integration_tests/codegen_fail/fail/interface/field_double_underscored.stderr index adebb09f2..fdd492f50 100644 --- a/integration_tests/codegen_fail/fail/interface/field_double_underscored.stderr +++ b/integration_tests/codegen_fail/fail/interface/field_double_underscored.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/interface/field_non_output_return_type.stderr b/integration_tests/codegen_fail/fail/interface/field_non_output_return_type.stderr index 0ebe0fa2c..19f45f691 100644 --- a/integration_tests/codegen_fail/fail/interface/field_non_output_return_type.stderr +++ b/integration_tests/codegen_fail/fail/interface/field_non_output_return_type.stderr @@ -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) diff --git a/integration_tests/codegen_fail/fail/interface/fields_duplicate.stderr b/integration_tests/codegen_fail/fail/interface/fields_duplicate.stderr index db1333863..ff07dbcbb 100644 --- a/integration_tests/codegen_fail/fail/interface/fields_duplicate.stderr +++ b/integration_tests/codegen_fail/fail/interface/fields_duplicate.stderr @@ -1,25 +1,20 @@ error: GraphQL interface must have a different name for each field - --> $DIR/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 + + --> fail/interface/fields_duplicate.rs:13:1 | - = 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 diff --git a/integration_tests/codegen_fail/fail/interface/implementer_non_object_type.stderr b/integration_tests/codegen_fail/fail/interface/implementer_non_object_type.stderr index 597171164..d3c55953a 100644 --- a/integration_tests/codegen_fail/fail/interface/implementer_non_object_type.stderr +++ b/integration_tests/codegen_fail/fail/interface/implementer_non_object_type.stderr @@ -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) diff --git a/integration_tests/codegen_fail/fail/interface/name_double_underscored.stderr b/integration_tests/codegen_fail/fail/interface/name_double_underscored.stderr index de8c6715f..ba46779fc 100644 --- a/integration_tests/codegen_fail/fail/interface/name_double_underscored.stderr +++ b/integration_tests/codegen_fail/fail/interface/name_double_underscored.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/interface/no_fields.stderr b/integration_tests/codegen_fail/fail/interface/no_fields.stderr index 367a5e9bc..3d2e4d371 100644 --- a/integration_tests/codegen_fail/fail/interface/no_fields.stderr +++ b/integration_tests/codegen_fail/fail/interface/no_fields.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/object/argument_double_underscored.stderr b/integration_tests/codegen_fail/fail/object/argument_double_underscored.stderr index 433c024e0..f93a398fc 100644 --- a/integration_tests/codegen_fail/fail/object/argument_double_underscored.stderr +++ b/integration_tests/codegen_fail/fail/object/argument_double_underscored.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/object/argument_non_input_type.stderr b/integration_tests/codegen_fail/fail/object/argument_non_input_type.stderr index 1a054e5ef..976f8e089 100644 --- a/integration_tests/codegen_fail/fail/object/argument_non_input_type.stderr +++ b/integration_tests/codegen_fail/fail/object/argument_non_input_type.stderr @@ -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` diff --git a/integration_tests/codegen_fail/fail/object/attr_field_non_output_return_type.stderr b/integration_tests/codegen_fail/fail/object/attr_field_non_output_return_type.stderr index 9bc4c6bda..4339c20ef 100644 --- a/integration_tests/codegen_fail/fail/object/attr_field_non_output_return_type.stderr +++ b/integration_tests/codegen_fail/fail/object/attr_field_non_output_return_type.stderr @@ -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) diff --git a/integration_tests/codegen_fail/fail/object/attr_fields_duplicate.stderr b/integration_tests/codegen_fail/fail/object/attr_fields_duplicate.stderr index 468e4cde5..f22b6155d 100644 --- a/integration_tests/codegen_fail/fail/object/attr_fields_duplicate.stderr +++ b/integration_tests/codegen_fail/fail/object/attr_fields_duplicate.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/object/attr_name_double_underscored.stderr b/integration_tests/codegen_fail/fail/object/attr_name_double_underscored.stderr index e7bedddbf..323003ab7 100644 --- a/integration_tests/codegen_fail/fail/object/attr_name_double_underscored.stderr +++ b/integration_tests/codegen_fail/fail/object/attr_name_double_underscored.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/object/attr_no_fields.stderr b/integration_tests/codegen_fail/fail/object/attr_no_fields.stderr index f783cc91a..35a77ead6 100644 --- a/integration_tests/codegen_fail/fail/object/attr_no_fields.stderr +++ b/integration_tests/codegen_fail/fail/object/attr_no_fields.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/object/derive_field_double_underscored.stderr b/integration_tests/codegen_fail/fail/object/derive_field_double_underscored.stderr index a6f3cb8db..09be8d8e2 100644 --- a/integration_tests/codegen_fail/fail/object/derive_field_double_underscored.stderr +++ b/integration_tests/codegen_fail/fail/object/derive_field_double_underscored.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/object/derive_field_non_output_return_type.stderr b/integration_tests/codegen_fail/fail/object/derive_field_non_output_return_type.stderr index d112debd2..0a24fe8e9 100644 --- a/integration_tests/codegen_fail/fail/object/derive_field_non_output_return_type.stderr +++ b/integration_tests/codegen_fail/fail/object/derive_field_non_output_return_type.stderr @@ -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) diff --git a/integration_tests/codegen_fail/fail/object/derive_fields_duplicate.stderr b/integration_tests/codegen_fail/fail/object/derive_fields_duplicate.stderr index 74004561b..3615a35ca 100644 --- a/integration_tests/codegen_fail/fail/object/derive_fields_duplicate.stderr +++ b/integration_tests/codegen_fail/fail/object/derive_fields_duplicate.stderr @@ -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 { + | ^^^^^^ diff --git a/integration_tests/codegen_fail/fail/object/derive_name_double_underscored.stderr b/integration_tests/codegen_fail/fail/object/derive_name_double_underscored.stderr index 11eafcef6..eb9dfb6cf 100644 --- a/integration_tests/codegen_fail/fail/object/derive_name_double_underscored.stderr +++ b/integration_tests/codegen_fail/fail/object/derive_name_double_underscored.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/object/derive_no_fields.stderr b/integration_tests/codegen_fail/fail/object/derive_no_fields.stderr index 1975c3611..96038236f 100644 --- a/integration_tests/codegen_fail/fail/object/derive_no_fields.stderr +++ b/integration_tests/codegen_fail/fail/object/derive_no_fields.stderr @@ -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 + | ^^^^^^ diff --git a/integration_tests/codegen_fail/fail/object/derive_wrong_item.stderr b/integration_tests/codegen_fail/fail/object/derive_wrong_item.stderr index 50680bec9..71b8d0e58 100644 --- a/integration_tests/codegen_fail/fail/object/derive_wrong_item.stderr +++ b/integration_tests/codegen_fail/fail/object/derive_wrong_item.stderr @@ -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 {} - | ^^^^^^^^^^^^^^^^^ + | ^^^^ diff --git a/integration_tests/codegen_fail/fail/subscription/argument_double_underscored.stderr b/integration_tests/codegen_fail/fail/subscription/argument_double_underscored.stderr index 97d9d6df1..7cc24a2d3 100644 --- a/integration_tests/codegen_fail/fail/subscription/argument_double_underscored.stderr +++ b/integration_tests/codegen_fail/fail/subscription/argument_double_underscored.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/subscription/argument_non_input_type.stderr b/integration_tests/codegen_fail/fail/subscription/argument_non_input_type.stderr index 87d4a9ee7..59f35de39 100644 --- a/integration_tests/codegen_fail/fail/subscription/argument_non_input_type.stderr +++ b/integration_tests/codegen_fail/fail/subscription/argument_non_input_type.stderr @@ -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` diff --git a/integration_tests/codegen_fail/fail/subscription/field_non_output_return_type.stderr b/integration_tests/codegen_fail/fail/subscription/field_non_output_return_type.stderr index 84d4cf63b..a4adbdd62 100644 --- a/integration_tests/codegen_fail/fail/subscription/field_non_output_return_type.stderr +++ b/integration_tests/codegen_fail/fail/subscription/field_non_output_return_type.stderr @@ -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) diff --git a/integration_tests/codegen_fail/fail/subscription/field_not_async.stderr b/integration_tests/codegen_fail/fail/subscription/field_not_async.stderr index 3e5209c8e..344b56735 100644 --- a/integration_tests/codegen_fail/fail/subscription/field_not_async.stderr +++ b/integration_tests/codegen_fail/fail/subscription/field_not_async.stderr @@ -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()` + | ^^ diff --git a/integration_tests/codegen_fail/fail/subscription/fields_duplicate.stderr b/integration_tests/codegen_fail/fail/subscription/fields_duplicate.stderr index d9156450c..733dd6dda 100644 --- a/integration_tests/codegen_fail/fail/subscription/fields_duplicate.stderr +++ b/integration_tests/codegen_fail/fail/subscription/fields_duplicate.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/subscription/name_double_underscored.stderr b/integration_tests/codegen_fail/fail/subscription/name_double_underscored.stderr index ac1081e35..b93010eac 100644 --- a/integration_tests/codegen_fail/fail/subscription/name_double_underscored.stderr +++ b/integration_tests/codegen_fail/fail/subscription/name_double_underscored.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/subscription/no_fields.stderr b/integration_tests/codegen_fail/fail/subscription/no_fields.stderr index 93b5487f5..8bb828020 100644 --- a/integration_tests/codegen_fail/fail/subscription/no_fields.stderr +++ b/integration_tests/codegen_fail/fail/subscription/no_fields.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/union/derive_wrong_item.stderr b/integration_tests/codegen_fail/fail/union/derive_wrong_item.stderr index fdeb15b40..85b57e2c9 100644 --- a/integration_tests/codegen_fail/fail/union/derive_wrong_item.stderr +++ b/integration_tests/codegen_fail/fail/union/derive_wrong_item.stderr @@ -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 } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ diff --git a/integration_tests/codegen_fail/fail/union/enum_external_resolver_fn_conflicts_with_variant_external_resolver_fn.stderr b/integration_tests/codegen_fail/fail/union/enum_external_resolver_fn_conflicts_with_variant_external_resolver_fn.stderr index 12957f131..c0f378c36 100644 --- a/integration_tests/codegen_fail/fail/union/enum_external_resolver_fn_conflicts_with_variant_external_resolver_fn.stderr +++ b/integration_tests/codegen_fail/fail/union/enum_external_resolver_fn_conflicts_with_variant_external_resolver_fn.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/union/enum_name_double_underscored.stderr b/integration_tests/codegen_fail/fail/union/enum_name_double_underscored.stderr index df5db23fd..f952d0f83 100644 --- a/integration_tests/codegen_fail/fail/union/enum_name_double_underscored.stderr +++ b/integration_tests/codegen_fail/fail/union/enum_name_double_underscored.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/union/enum_no_fields.stderr b/integration_tests/codegen_fail/fail/union/enum_no_fields.stderr index 85ea25850..4d7d4f07b 100644 --- a/integration_tests/codegen_fail/fail/union/enum_no_fields.stderr +++ b/integration_tests/codegen_fail/fail/union/enum_no_fields.stderr @@ -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 + | ^^^^ diff --git a/integration_tests/codegen_fail/fail/union/enum_non_object_variant.stderr b/integration_tests/codegen_fail/fail/union/enum_non_object_variant.stderr index fc7f13fa1..ccfb6325d 100644 --- a/integration_tests/codegen_fail/fail/union/enum_non_object_variant.stderr +++ b/integration_tests/codegen_fail/fail/union/enum_non_object_variant.stderr @@ -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) diff --git a/integration_tests/codegen_fail/fail/union/enum_same_type_pretty.stderr b/integration_tests/codegen_fail/fail/union/enum_same_type_pretty.stderr index 65cac9c36..8a6988bf5 100644 --- a/integration_tests/codegen_fail/fail/union/enum_same_type_pretty.stderr +++ b/integration_tests/codegen_fail/fail/union/enum_same_type_pretty.stderr @@ -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 { + | ^^^^ diff --git a/integration_tests/codegen_fail/fail/union/enum_wrong_variant_field.stderr b/integration_tests/codegen_fail/fail/union/enum_wrong_variant_field.stderr index 4f5def6a9..26497520d 100644 --- a/integration_tests/codegen_fail/fail/union/enum_wrong_variant_field.stderr +++ b/integration_tests/codegen_fail/fail/union/enum_wrong_variant_field.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/union/struct_name_double_underscored.stderr b/integration_tests/codegen_fail/fail/union/struct_name_double_underscored.stderr index 48054c637..c5d3df1e4 100644 --- a/integration_tests/codegen_fail/fail/union/struct_name_double_underscored.stderr +++ b/integration_tests/codegen_fail/fail/union/struct_name_double_underscored.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/union/struct_no_fields.stderr b/integration_tests/codegen_fail/fail/union/struct_no_fields.stderr index 3d6aaeb2b..d77299544 100644 --- a/integration_tests/codegen_fail/fail/union/struct_no_fields.stderr +++ b/integration_tests/codegen_fail/fail/union/struct_no_fields.stderr @@ -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 + | ^^^^^^ diff --git a/integration_tests/codegen_fail/fail/union/struct_non_object_variant.stderr b/integration_tests/codegen_fail/fail/union/struct_non_object_variant.stderr index 200aeecdf..96df4a97f 100644 --- a/integration_tests/codegen_fail/fail/union/struct_non_object_variant.stderr +++ b/integration_tests/codegen_fail/fail/union/struct_non_object_variant.stderr @@ -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) diff --git a/integration_tests/codegen_fail/fail/union/trait_fail_infer_context.stderr b/integration_tests/codegen_fail/fail/union/trait_fail_infer_context.stderr index 9279a9b37..b4eb24816 100644 --- a/integration_tests/codegen_fail/fail/union/trait_fail_infer_context.stderr +++ b/integration_tests/codegen_fail/fail/union/trait_fail_infer_context.stderr @@ -1,14 +1,18 @@ error[E0277]: the trait bound `CustomContext: FromContext` is not satisfied - --> $DIR/trait_fail_infer_context.rs:3:1 - | -3 | #[graphql_union] - | ^^^^^^^^^^^^^^^^ expected an implementor of trait `FromContext` - | - = 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` + | +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` diff --git a/integration_tests/codegen_fail/fail/union/trait_method_conflicts_with_external_resolver_fn.stderr b/integration_tests/codegen_fail/fail/union/trait_method_conflicts_with_external_resolver_fn.stderr index 6fff37828..197ba1b65 100644 --- a/integration_tests/codegen_fail/fail/union/trait_method_conflicts_with_external_resolver_fn.stderr +++ b/integration_tests/codegen_fail/fail/union/trait_method_conflicts_with_external_resolver_fn.stderr @@ -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>; + | ^^ diff --git a/integration_tests/codegen_fail/fail/union/trait_name_double_underscored.stderr b/integration_tests/codegen_fail/fail/union/trait_name_double_underscored.stderr index d010fee27..3faaa193e 100644 --- a/integration_tests/codegen_fail/fail/union/trait_name_double_underscored.stderr +++ b/integration_tests/codegen_fail/fail/union/trait_name_double_underscored.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/union/trait_no_fields.stderr b/integration_tests/codegen_fail/fail/union/trait_no_fields.stderr index 8261623dc..6bc9e4234 100644 --- a/integration_tests/codegen_fail/fail/union/trait_no_fields.stderr +++ b/integration_tests/codegen_fail/fail/union/trait_no_fields.stderr @@ -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 + | ^^^^^ diff --git a/integration_tests/codegen_fail/fail/union/trait_non_object_variant.stderr b/integration_tests/codegen_fail/fail/union/trait_non_object_variant.stderr index e9ac7cdb7..5697644cc 100644 --- a/integration_tests/codegen_fail/fail/union/trait_non_object_variant.stderr +++ b/integration_tests/codegen_fail/fail/union/trait_non_object_variant.stderr @@ -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) diff --git a/integration_tests/codegen_fail/fail/union/trait_same_type_pretty.stderr b/integration_tests/codegen_fail/fail/union/trait_same_type_pretty.stderr index f899e3075..490980167 100644 --- a/integration_tests/codegen_fail/fail/union/trait_same_type_pretty.stderr +++ b/integration_tests/codegen_fail/fail/union/trait_same_type_pretty.stderr @@ -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 { + | ^^^^^ diff --git a/integration_tests/codegen_fail/fail/union/trait_with_attr_on_method.stderr b/integration_tests/codegen_fail/fail/union/trait_with_attr_on_method.stderr index 427163a8b..e49387cd0 100644 --- a/integration_tests/codegen_fail/fail/union/trait_with_attr_on_method.stderr +++ b/integration_tests/codegen_fail/fail/union/trait_with_attr_on_method.stderr @@ -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 diff --git a/integration_tests/codegen_fail/fail/union/trait_wrong_method_input_args.stderr b/integration_tests/codegen_fail/fail/union/trait_wrong_method_input_args.stderr index 9b2fa2d85..f32098957 100644 --- a/integration_tests/codegen_fail/fail/union/trait_wrong_method_input_args.stderr +++ b/integration_tests/codegen_fail/fail/union/trait_wrong_method_input_args.stderr @@ -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 + | ^ diff --git a/integration_tests/codegen_fail/fail/union/trait_wrong_method_return_type.stderr b/integration_tests/codegen_fail/fail/union/trait_wrong_method_return_type.stderr index bbc40568a..9c0dd5b95 100644 --- a/integration_tests/codegen_fail/fail/union/trait_wrong_method_return_type.stderr +++ b/integration_tests/codegen_fail/fail/union/trait_wrong_method_return_type.stderr @@ -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 + | ^ diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs index f4f3cc475..a7bdf882e 100644 --- a/juniper_codegen/src/lib.rs +++ b/juniper_codegen/src/lib.rs @@ -233,7 +233,7 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream { /// fn from_input_value(value: &juniper::InputValue) -> Result { /// Ok(value.as_string_value() /// .map(|s| UserID(s.to_owned())) -/// .ok_or_else(|| format!("Expected String, found: {}", v))?) +/// .ok_or_else(|| format!("Expected String, found: {}", value))?) /// } /// /// fn from_str<'a>(value: juniper::ScalarToken<'a>) -> juniper::ParseScalarResult<'a, S> { From 135024535d690e6819599a3ac5b64e56041ef288 Mon Sep 17 00:00:00 2001 From: ilslv Date: Wed, 1 Dec 2021 15:37:40 +0300 Subject: [PATCH 08/25] Fix book example and docs --- docs/book/content/types/scalars.md | 15 +- err.txt | 836 ---------------------- integration_tests/async_await/src/main.rs | 132 ++-- juniper/src/ast.rs | 2 + 4 files changed, 78 insertions(+), 907 deletions(-) delete mode 100644 err.txt diff --git a/docs/book/content/types/scalars.md b/docs/book/content/types/scalars.md index 515815776..c6ce47134 100644 --- a/docs/book/content/types/scalars.md +++ b/docs/book/content/types/scalars.md @@ -113,8 +113,8 @@ The example below is used just for illustration. # } # } # } - -use juniper::{Value, ParseScalarResult, ParseScalarValue}; +# +use juniper::{FieldError, Value, ParseScalarResult, ParseScalarValue}; use date::Date; #[juniper::graphql_scalar(description = "Date")] @@ -128,10 +128,11 @@ where } // Define how to parse a primitive type into your custom scalar. - fn from_input_value(v: &InputValue) -> Option { - v.as_scalar_value() - .and_then(|v| v.as_str()) - .and_then(|s| s.parse().ok()) + fn from_input_value(v: &InputValue) -> Result> { + 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))) + .map_err(Into::into) } // Define how to parse a string value. @@ -139,6 +140,6 @@ where >::from_str(value) } } - +# # fn main() {} ``` diff --git a/err.txt b/err.txt deleted file mode 100644 index e397bf1d6..000000000 --- a/err.txt +++ /dev/null @@ -1,836 +0,0 @@ -/Users/work/.cargo/bin/cargo +stable test --color=always -p juniper_codegen_tests --all-features --no-fail-fast -- --format=json -Z unstable-options --show-output -Testing started at 15:02 ... - Finished test [unoptimized + debuginfo] target(s) in 0.20s - Running unittests (/Users/work/Work/juniper/target/debug/deps/juniper_codegen_tests-0ab20148dc3374c3) - Checking juniper_codegen_tests-tests v0.0.0 (/Users/work/Work/juniper/target/tests/juniper_codegen_tests) - Finished dev [unoptimized + debuginfo] target(s) in 0.28s - - -test fail/interface/field_double_underscored.rs ... ok -test fail/interface/implementers_duplicate_pretty.rs ... ok -test fail/interface/downcast_method_wrong_input_args.rs ... ok -test fail/interface/argument_wrong_default_array.rs ... ok -test fail/interface/wrong_item.rs ... ok -test fail/interface/name_double_underscored.rs ... ok -test fail/interface/argument_double_underscored.rs ... ok -test fail/interface/argument_non_input_type.rs ... ok -test fail/interface/downcast_method_conflicts_with_external_downcast_fn.rs ... ok -test fail/interface/no_fields.rs ... ok -test fail/interface/field_non_output_return_type.rs ... ok -test fail/interface/implementers_duplicate_ugly.rs ... ok -test fail/interface/downcast_method_wrong_return_type.rs ... ok -test fail/interface/fields_duplicate.rs ... ok -test fail/interface/implementer_non_object_type.rs ... ok -test fail/input-object/derive_no_fields.rs ... ok -test fail/input-object/derive_unique_name.rs ... ok -test fail/input-object/derive_no_underscore.rs ... ok -test fail/input-object/derive_incompatible_object.rs ... ok -test fail/enum/derive_no_fields.rs ... ok -test fail/subscription/field_double_underscored.rs ... ok -test fail/subscription/field_not_async.rs ... ok -test fail/subscription/argument_wrong_default_array.rs ... ok -test fail/subscription/wrong_item.rs ... ok -test fail/subscription/name_double_underscored.rs ... ok -test fail/subscription/argument_double_underscored.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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 - - --> fail/subscription/argument_double_underscored.rs:11:24 - | -11 | async fn id(&self, __num: i32) -> Stream<'static, &'static str> { - | -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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 - - --> fail/subscription/argument_double_underscored.rs:11:24 - | -11 | async fn id(&self, __num: i32) -> Stream<'static, &'static str> { - | ^^^^^ -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/subscription/argument_non_input_type.rs ... ok -test fail/subscription/no_fields.rs ... ok -test fail/subscription/field_non_output_return_type.rs ... ok -test fail/subscription/fields_duplicate.rs ... ok -test fail/object/derive_field_double_underscored.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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 - | -5 | __test: String, - | ^^^^^^ - | - = note: https://spec.graphql.org/June2018/#sec-Schema -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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 - - --> fail/object/derive_field_double_underscored.rs:5:5 - | -5 | __test: String, - | ^^^^^^ -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/object/derive_no_fields.rs ... ok -test fail/object/derive_wrong_item.rs ... ok -test fail/object/attr_fields_duplicate.rs ... ok -test fail/object/argument_wrong_default_array.rs ... ok -test fail/object/argument_double_underscored.rs ... ok -test fail/object/argument_non_input_type.rs ... ok -test fail/object/attr_no_fields.rs ... { "type": "test", "event": "timeout", "name": "test_failing_compiliation" } -ok -test fail/object/attr_name_double_underscored.rs ... ok -test fail/object/derive_name_double_underscored.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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 - - --> fail/object/derive_field_double_underscored.rs:5:5 - | -5 | __test: String, - | ^^^^^^ -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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 - - --> fail/object/derive_name_double_underscored.rs:4:8 - | -4 | struct __Obj { - | ^^^^^ -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/object/derive_fields_duplicate.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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 -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL object must have a different name for each field - - = note: https://spec.graphql.org/June2018/#sec-Objects - - --> fail/object/derive_fields_duplicate.rs:4:1 - | -4 | struct ObjA { - | ^^^^^^ -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/object/attr_wrong_item.rs ... ok -test fail/object/attr_field_non_output_return_type.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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) -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error[E0277]: the trait bound `ObjB: IsOutputType<__S>` is not satisfied - --> 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) -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/object/attr_field_double_underscored.rs ... ok -test fail/object/derive_field_non_output_return_type.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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) -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error[E0277]: the trait bound `ObjB: IsOutputType<__S>` is not satisfied - --> 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) -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/union/enum_wrong_variant_field.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union enum allows only unnamed variants with a single field, e.g. `Some(T)` - --> $DIR/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 - | -10 | A(Human, u8), - | ^^^^^^^^^^^ - | - = note: https://spec.graphql.org/June2018/#sec-Unions -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union enum allows only unnamed variants with a single field, e.g. `Some(T)` - - = note: https://spec.graphql.org/June2018/#sec-Unions - - --> fail/union/enum_wrong_variant_field.rs:5:5 - | -5 | A { human: Human }, - | ^ - -error: GraphQL union enum allows only unnamed variants with a single field, e.g. `Some(T)` - - = note: https://spec.graphql.org/June2018/#sec-Unions - - --> fail/union/enum_wrong_variant_field.rs:10:6 - | -10 | A(Human, u8), - | ^^^^^^^^^^^ -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/union/struct_non_object_variant.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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) -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error[E0277]: the trait bound `Test: GraphQLObject<__S>` is not satisfied - --> 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) -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/union/struct_same_type_pretty.rs ... ok -test fail/union/trait_fail_infer_context.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error[E0277]: the trait bound `CustomContext: FromContext` is not satisfied - --> $DIR/trait_fail_infer_context.rs:3:1 - | -3 | #[graphql_union] - | ^^^^^^^^^^^^^^^^ expected an implementor of trait `FromContext` - | - = 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) - -error[E0308]: mismatched types - --> $DIR/trait_fail_infer_context.rs:3:1 - | -3 | #[graphql_union] - | ^^^^^^^^^^^^^^^^ expected struct `CustomContext`, found struct `SubContext` - | - = note: expected reference `&CustomContext` - found reference `&SubContext` - = note: this error originates in the attribute macro `graphql_union` (in Nightly builds, run with -Z macro-backtrace for more info) -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error[E0277]: the trait bound `CustomContext: FromContext` is not satisfied - --> fail/union/trait_fail_infer_context.rs:3:1 - | -3 | #[graphql_union] - | ^^^^^^^^^^^^^^^^ expected an implementor of trait `FromContext` - | -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 - --> fail/union/trait_fail_infer_context.rs:3:1 - | -3 | #[graphql_union] - | ^^^^^^^^^^^^^^^^ expected struct `CustomContext`, found struct `SubContext` - | - = note: expected reference `&CustomContext` - found reference `&SubContext` - = note: this error originates in the attribute macro `graphql_union` (in Nightly builds, run with -Z macro-backtrace for more info) -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/union/derive_wrong_item.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union can only be derived for enums and structs - --> $DIR/derive_wrong_item.rs:4:1 - | -4 | union Character { id: i32 } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union can only be derived for enums and structs - --> fail/union/derive_wrong_item.rs:4:1 - | -4 | union Character { id: i32 } - | ^^^^^ -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/union/enum_no_fields.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union expects at least one union variant - --> $DIR/enum_no_fields.rs:4:1 - | -4 | enum Character {} - | ^^^^^^^^^^^^^^^^^ - | - = note: https://spec.graphql.org/June2018/#sec-Unions -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union expects at least one union variant - - = note: https://spec.graphql.org/June2018/#sec-Unions - - --> fail/union/enum_no_fields.rs:4:1 - | -4 | enum Character {} - | ^^^^ -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/union/enum_same_type_pretty.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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 -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union must have a different type for each union variant - - = note: https://spec.graphql.org/June2018/#sec-Unions - - --> fail/union/enum_same_type_pretty.rs:4:1 - | -4 | enum Character { - | ^^^^ -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/union/trait_no_fields.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union expects at least one union variant - --> $DIR/trait_no_fields.rs:4:1 - | -4 | trait Character {} - | ^^^^^^^^^^^^^^^^^^ - | - = note: https://spec.graphql.org/June2018/#sec-Unions -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union expects at least one union variant - - = note: https://spec.graphql.org/June2018/#sec-Unions - - --> fail/union/trait_no_fields.rs:4:1 - | -4 | trait Character {} - | ^^^^^ -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/union/enum_same_type_ugly.rs ... ok -test fail/union/trait_same_type_ugly.rs ... ok -test fail/union/enum_non_object_variant.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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) -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error[E0277]: the trait bound `Test: GraphQLObject<__S>` is not satisfied - --> 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) -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/union/trait_with_attr_on_method.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union cannot use #[graphql(with = ...)] attribute on a trait method - --> $DIR/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 -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union cannot use #[graphql(with = ...)] attribute on a trait method - - = 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: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/union/trait_wrong_method_input_args.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union expects trait method to accept `&self` only and, optionally, `&Context` - --> $DIR/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 -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union expects trait method to accept `&self` only and, optionally, `&Context` - - = 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: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/union/trait_name_double_underscored.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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 - | -4 | trait __Character { - | ^^^^^^^^^^^ - | - = note: https://spec.graphql.org/June2018/#sec-Schema -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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 - - --> fail/union/trait_name_double_underscored.rs:4:7 - | -4 | trait __Character { - | ^^^^^^^^^^^ -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/union/enum_external_resolver_fn_conflicts_with_variant_external_resolver_fn.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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 - | -6 | #[graphql(with = resolve_fn2)] - | ^^^^ - | - = note: https://spec.graphql.org/June2018/#sec-Unions -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union variant `Human` already has external resolver function `resolve_fn1` declared on the enum - - = 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: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/union/trait_same_type_pretty.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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 -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union must have a different type for each union variant - - = note: https://spec.graphql.org/June2018/#sec-Unions - - --> fail/union/trait_same_type_pretty.rs:4:1 - | -4 | trait Character { - | ^^^^^ -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/union/attr_wrong_item.rs ... ok -test fail/union/struct_no_fields.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union expects at least one union variant - --> $DIR/struct_no_fields.rs:4:1 - | -4 | struct Character; - | ^^^^^^^^^^^^^^^^^ - | - = note: https://spec.graphql.org/June2018/#sec-Unions -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union expects at least one union variant - - = note: https://spec.graphql.org/June2018/#sec-Unions - - --> fail/union/struct_no_fields.rs:4:1 - | -4 | struct Character; - | ^^^^^^ -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/union/struct_name_double_underscored.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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 - | -5 | struct __Character; - | ^^^^^^^^^^^ - | - = note: https://spec.graphql.org/June2018/#sec-Schema -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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 - - --> fail/union/struct_name_double_underscored.rs:5:8 - | -5 | struct __Character; - | ^^^^^^^^^^^ -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/union/trait_method_conflicts_with_external_resolver_fn.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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 -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union trait method `a` conflicts with the external resolver function `some_fn` declared on the trait to resolve the variant type `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>; - | ^^ -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/union/enum_name_double_underscored.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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 - | -4 | enum __Character { - | ^^^^^^^^^^^ - | - = note: https://spec.graphql.org/June2018/#sec-Schema -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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 - - --> fail/union/enum_name_double_underscored.rs:4:6 - | -4 | enum __Character { - | ^^^^^^^^^^^ -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/union/trait_non_object_variant.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -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) -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error[E0277]: the trait bound `Test: GraphQLObject<__S>` is not satisfied - --> 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) -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - -test fail/union/struct_same_type_ugly.rs ... ok -test fail/union/trait_wrong_method_return_type.rs ... mismatch - -EXPECTED: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union expects trait method return type to be `Option<&VariantType>` only - --> $DIR/trait_wrong_method_return_type.rs:5:20 - | -5 | fn a(&self) -> &Human; - | ^^^^^^ - | - = note: https://spec.graphql.org/June2018/#sec-Unions -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ - -ACTUAL OUTPUT: -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -error: GraphQL union expects trait method return type to be `Option<&VariantType>` only - - = note: https://spec.graphql.org/June2018/#sec-Unions - - --> fail/union/trait_wrong_method_return_type.rs:5:20 - | -5 | fn a(&self) -> &Human; - | ^ -┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ -note: If the actual output is the correct output you can bless it by rerunning - your test with the environment variable TRYBUILD=overwrite - - - - -25 of 69 tests failed -thread 'test_failing_compiliation' panicked at '25 of 69 tests failed', /Users/work/.cargo/registry/src/git.colasdn.top-1ecc6299db9ec823/trybuild-1.0.52/src/run.rs:74:13 -stack backtrace: - 0: rust_begin_unwind - at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/std/src/panicking.rs:517:5 - 1: std::panicking::begin_panic_fmt - at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/std/src/panicking.rs:460:5 - 2: trybuild::run::::run - at /Users/work/.cargo/registry/src/git.colasdn.top-1ecc6299db9ec823/trybuild-1.0.52/src/run.rs:74:13 - 3: ::drop - at /Users/work/.cargo/registry/src/git.colasdn.top-1ecc6299db9ec823/trybuild-1.0.52/src/lib.rs:306:13 - 4: core::ptr::drop_in_place - at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/core/src/ptr/mod.rs:188:1 - 5: juniper_codegen_tests::test_failing_compiliation - at ./src/lib.rs:38:1 - 6: juniper_codegen_tests::test_failing_compiliation::{{closure}} - at ./src/lib.rs:28:1 - 7: core::ops::function::FnOnce::call_once - at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/core/src/ops/function.rs:227:5 - 8: core::ops::function::FnOnce::call_once - at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/core/src/ops/function.rs:227:5 -note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. - - Doc-tests juniper_codegen_tests -error: test failed, to rerun pass '-p juniper_codegen_tests --lib' - -Process finished with exit code 101 diff --git a/integration_tests/async_await/src/main.rs b/integration_tests/async_await/src/main.rs index cf58f90f0..076739446 100644 --- a/integration_tests/async_await/src/main.rs +++ b/integration_tests/async_await/src/main.rs @@ -1,7 +1,4 @@ -use juniper::{ - graphql_object, graphql_value, EmptyMutation, EmptySubscription, GraphQLEnum, GraphQLError, - RootNode, Value, -}; +use juniper::{graphql_object, GraphQLEnum}; #[derive(GraphQLEnum)] enum UserKind { @@ -73,10 +70,18 @@ impl Query { } } -#[tokio::test] -async fn async_simple() { - let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new()); - let doc = r#" +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 @@ -89,35 +94,35 @@ async fn async_simple() { } "#; - 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#" + #[tokio::test] + async fn async_field_validation_error() { + let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new()); + let doc = r#" query { nonExistentField fieldSync @@ -131,37 +136,36 @@ async fn async_field_validation_error() { } "#; - 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); -} - -// 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); -// } + let error = result.err().unwrap(); + let is_validation_error = match error { + GraphQLError::ValidationError(_) => true, + _ => false, + }; + assert!(is_validation_error); + } -fn main() {} + // 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); + // } +} diff --git a/juniper/src/ast.rs b/juniper/src/ast.rs index 027836173..3c5971d26 100644 --- a/juniper/src/ast.rs +++ b/juniper/src/ast.rs @@ -168,6 +168,8 @@ pub trait FromInputValue: Sized { /// /// Thus not restricted, it should be convertible with [`IntoFieldError`] to /// fit well into the library machinery. + /// + /// [`IntoFieldError`]: crate::IntoFieldError type Error; /// Performs the conversion. From ea2fe645ff7d7e0d12d7c86aeb781cb74120cdd3 Mon Sep 17 00:00:00 2001 From: ilslv Date: Wed, 1 Dec 2021 16:16:16 +0300 Subject: [PATCH 09/25] Corrections --- .../src/codegen/derive_input_object.rs | 2 +- .../juniper_tests/src/codegen/impl_scalar.rs | 18 +++++---------- .../juniper_tests/src/custom_scalar.rs | 3 +-- juniper/src/executor/mod.rs | 7 ++---- .../src/executor_tests/introspection/mod.rs | 3 +-- juniper/src/integrations/chrono.rs | 5 +++-- juniper/src/macros/helper/mod.rs | 6 ++--- juniper/src/types/containers.rs | 6 ++--- juniper/src/types/scalars.rs | 15 +++++-------- juniper/src/validation/test_harness.rs | 12 +++++----- juniper_codegen/src/common/field/arg.rs | 22 ++++++++++++------- juniper_codegen/src/common/field/mod.rs | 4 ++-- juniper_codegen/src/lib.rs | 4 ++-- 13 files changed, 49 insertions(+), 58 deletions(-) diff --git a/integration_tests/juniper_tests/src/codegen/derive_input_object.rs b/integration_tests/juniper_tests/src/codegen/derive_input_object.rs index fc9065fa1..f24f7bd59 100644 --- a/integration_tests/juniper_tests/src/codegen/derive_input_object.rs +++ b/integration_tests/juniper_tests/src/codegen/derive_input_object.rs @@ -61,7 +61,7 @@ impl<'a> FromInputValue for &'a Fake { type Error = FieldError; fn from_input_value(_v: &InputValue) -> Result<&'a Fake, Self::Error> { - Err(FieldError::from("This is fake")) + Err("This is fake".into()) } } diff --git a/integration_tests/juniper_tests/src/codegen/impl_scalar.rs b/integration_tests/juniper_tests/src/codegen/impl_scalar.rs index 4c036b53d..facff5698 100644 --- a/integration_tests/juniper_tests/src/codegen/impl_scalar.rs +++ b/integration_tests/juniper_tests/src/codegen/impl_scalar.rs @@ -35,8 +35,7 @@ where fn from_input_value(v: &InputValue) -> Result { v.as_int_value() .map(DefaultName) - .ok_or_else(|| format!("Expected Int, found: {}", v)) - .map_err(Into::into) + .ok_or_else(|| format!("Expected Int, found: {}", v).into()) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -53,8 +52,7 @@ impl GraphQLScalar for OtherOrder { fn from_input_value(v: &InputValue) -> Result { v.as_int_value() .map(OtherOrder) - .ok_or_else(|| format!("Expected Int, found: {}", v)) - .map_err(Into::into) + .ok_or_else(|| format!("Expected Int, found: {}", v).into()) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { @@ -71,8 +69,7 @@ impl GraphQLScalar for Named { fn from_input_value(v: &InputValue) -> Result { v.as_int_value() .map(Named) - .ok_or_else(|| format!("Expected Int, found: {}", v)) - .map_err(Into::into) + .ok_or_else(|| format!("Expected Int, found: {}", v).into()) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { @@ -89,8 +86,7 @@ impl GraphQLScalar for ScalarDescription { fn from_input_value(v: &InputValue) -> Result { v.as_int_value() .map(ScalarDescription) - .ok_or_else(|| format!("Expected Int, found: {}", v)) - .map_err(Into::into) + .ok_or_else(|| format!("Expected Int, found: {}", v).into()) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { @@ -157,8 +153,7 @@ impl GraphQLScalar for WithCustomScalarValue { ) -> Result> { v.as_int_value() .map(WithCustomScalarValue) - .ok_or_else(|| format!("Expected Int, found: {}", v)) - .map_err(Into::into) + .ok_or_else(|| format!("Expected Int, found: {}", v).into()) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, MyScalarValue> { @@ -217,8 +212,7 @@ fn path_in_resolve_return_type() { fn from_input_value(v: &InputValue) -> Result { v.as_int_value() .map(ResolvePath) - .ok_or_else(|| format!("Expected Int, found: {}", v)) - .map_err(Into::into) + .ok_or_else(|| format!("Expected Int, found: {}", v).into()) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { diff --git a/integration_tests/juniper_tests/src/custom_scalar.rs b/integration_tests/juniper_tests/src/custom_scalar.rs index a2c12bbac..1ed488a6b 100644 --- a/integration_tests/juniper_tests/src/custom_scalar.rs +++ b/integration_tests/juniper_tests/src/custom_scalar.rs @@ -141,8 +141,7 @@ impl GraphQLScalar for i64 { fn from_input_value(v: &InputValue) -> Result { v.as_scalar_value::() .copied() - .ok_or_else(|| format!("Expected MyScalarValue::Long(), found: {}", v)) - .map_err(Into::into) + .ok_or_else(|| format!("Expected MyScalarValue::Long(), found: {}", v).into()) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, MyScalarValue> { diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 2bda95b2d..719ce7abc 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -151,11 +151,8 @@ where /// ``` #[derive(Clone, Debug, PartialEq)] pub struct FieldError { - /// Error message. - pub message: String, - - /// [`Value`] containing additional information. - pub extensions: Value, + message: String, + extensions: Value, } impl From for FieldError { diff --git a/juniper/src/executor_tests/introspection/mod.rs b/juniper/src/executor_tests/introspection/mod.rs index b02d7c2f8..04be94de4 100644 --- a/juniper/src/executor_tests/introspection/mod.rs +++ b/juniper/src/executor_tests/introspection/mod.rs @@ -31,8 +31,7 @@ impl GraphQLScalar for Scalar { fn from_input_value(v: &InputValue) -> Result { v.as_int_value() .map(Scalar) - .ok_or_else(|| format!("Expected Int, found: {}", v)) - .map_err(Into::into) + .ok_or_else(|| format!("Expected Int, found: {}", v).into()) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { diff --git a/juniper/src/integrations/chrono.rs b/juniper/src/integrations/chrono.rs index e20d1262c..82e28e42e 100644 --- a/juniper/src/integrations/chrono.rs +++ b/juniper/src/integrations/chrono.rs @@ -37,12 +37,13 @@ where } fn from_input_value(v: &InputValue) -> Result, FieldError> { - Ok(v.as_string_value() + v.as_string_value() .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)) - })?) + }) + .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { diff --git a/juniper/src/macros/helper/mod.rs b/juniper/src/macros/helper/mod.rs index 01caaf562..89249a7a9 100644 --- a/juniper/src/macros/helper/mod.rs +++ b/juniper/src/macros/helper/mod.rs @@ -33,12 +33,12 @@ pub trait AsDynGraphQLValue { crate::sa::assert_obj_safe!(AsDynGraphQLValue); -/// This trait is used in `juniper::graphql_scalar` macro to [`Result`]s -/// [`Error`]. +/// This trait is used in `juniper::graphql_scalar` macro to get [`Error`] type +/// from a [`Result`]. /// /// [`Error`]: Result::Error pub trait ExtractErrorFromResult { - /// [`Result`]s [`Error`]. + /// [`Error`] of a [`Result`]. /// /// [`Error`]: Result::Error type Error; diff --git a/juniper/src/types/containers.rs b/juniper/src/types/containers.rs index 053219a4d..14816194c 100644 --- a/juniper/src/types/containers.rs +++ b/juniper/src/types/containers.rs @@ -315,7 +315,7 @@ where /// Not enough elements. NotEnough(usize), - /// Too many elements. Value is [`Vec`] of __all__ [`InputValue`] elements. + /// Too many elements. Contains __all__ [`InputValue`] elements. TooMuch(Vec), /// Underlying [`ScalarValue`] conversion error. @@ -333,10 +333,10 @@ where match self { Self::NotEnough(len) => { - FieldError::::from(format!("{}: required {} more elements", ERROR_PREFIX, len)) + format!("{}: required {} more elements", ERROR_PREFIX, len).into() } Self::TooMuch(el) => { - FieldError::::from(format!("{}: too much elements: {}", ERROR_PREFIX, el.len())) + format!("{}: too much elements: {}", ERROR_PREFIX, el.len()).into() } Self::Scalar(s) => s.into_field_error(), } diff --git a/juniper/src/types/scalars.rs b/juniper/src/types/scalars.rs index a5c6eee35..83039aba6 100644 --- a/juniper/src/types/scalars.rs +++ b/juniper/src/types/scalars.rs @@ -63,9 +63,8 @@ where v.as_string_value() .map(str::to_owned) .or_else(|| v.as_int_value().map(|i| i.to_string())) - .ok_or_else(|| format!("Expected String or Int, found: {}", v)) + .ok_or_else(|| format!("Expected String or Int, found: {}", v).into()) .map(ID) - .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -88,8 +87,7 @@ where fn from_input_value(v: &InputValue) -> Result { v.as_string_value() .map(str::to_owned) - .ok_or_else(|| format!("Expected String, found: {}", v)) - .map_err(Into::into) + .ok_or_else(|| format!("Expected String, found: {}", v).into()) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -277,8 +275,7 @@ where fn from_input_value(v: &InputValue) -> Result { v.as_scalar_value() .and_then(ScalarValue::as_boolean) - .ok_or_else(|| format!("Expected Boolean, found: {}", v)) - .map_err(Into::into) + .ok_or_else(|| format!("Expected Boolean, found: {}", v).into()) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -298,8 +295,7 @@ where fn from_input_value(v: &InputValue) -> Result { v.as_int_value() - .ok_or_else(|| format!("Expected Int, found: {}", v)) - .map_err(Into::into) + .ok_or_else(|| format!("Expected Int, found: {}", v).into()) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -324,8 +320,7 @@ where fn from_input_value(v: &InputValue) -> Result { v.as_float_value() - .ok_or_else(|| format!("Expected Float, found: {}", v)) - .map_err(Into::into) + .ok_or_else(|| format!("Expected Float, found: {}", v).into()) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { diff --git a/juniper/src/validation/test_harness.rs b/juniper/src/validation/test_harness.rs index e626a2ce5..0bff649c0 100644 --- a/juniper/src/validation/test_harness.rs +++ b/juniper/src/validation/test_harness.rs @@ -215,7 +215,7 @@ where Some("SIT") => Ok(DogCommand::Sit), Some("HEEL") => Ok(DogCommand::Heel), Some("DOWN") => Ok(DogCommand::Down), - _ => Err("Unknown command".into()), + _ => Err("Unknown DogCommand".into()), } } } @@ -324,7 +324,7 @@ where Some("BLACK") => Ok(FurColor::Black), Some("TAN") => Ok(FurColor::Tan), Some("SPOTTED") => Ok(FurColor::Spotted), - _ => Err("Unknown color".into()), + _ => Err("Unknown FurColor".into()), } } } @@ -631,22 +631,22 @@ where .get("intField") .map(|v| v.convert().map_err(FieldError::map_scalar_value)) .transpose()? - .ok_or("Expected requiredField")?, + .ok_or("Expected intField")?, string_field: obj .get("stringField") .map(|v| v.convert().map_err(FieldError::map_scalar_value)) .transpose()? - .ok_or("Expected requiredField")?, + .ok_or("Expected stringField")?, boolean_field: obj .get("booleanField") .map(|v| v.convert().map_err(FieldError::map_scalar_value)) .transpose()? - .ok_or("Expected requiredField")?, + .ok_or("Expected booleanField")?, string_list_field: obj .get("stringListField") .map(|v| v.convert().map_err(FieldError::map_scalar_value)) .transpose()? - .ok_or("Expected requiredField")?, + .ok_or("Expected stringListField")?, }) } } diff --git a/juniper_codegen/src/common/field/arg.rs b/juniper_codegen/src/common/field/arg.rs index 56e01afca..274f93532 100644 --- a/juniper_codegen/src/common/field/arg.rs +++ b/juniper_codegen/src/common/field/arg.rs @@ -357,11 +357,14 @@ impl OnMethod { .and_then(|opt| { opt.map_or_else( || { - <#ty as ::juniper::FromInputValue<#scalar>>::from_implicit_null() + <#ty as ::juniper::FromInputValue<#scalar>>:: + from_implicit_null() .map_err(|e| { - let mut e = ::juniper::IntoFieldError::into_field_error(e); - e.message = format!(#err_text, e.message); - e + let e = ::juniper::IntoFieldError::into_field_error(e); + ::juniper::FieldError::new( + format!(#err_text, e.message()), + e.extensions().clone(), + ) }) }, Ok, @@ -394,11 +397,14 @@ impl OnMethod { .and_then(|opt| { opt.map_or_else( || { - <#ty as ::juniper::FromInputValue<#scalar>>::from_implicit_null() + <#ty as ::juniper::FromInputValue<#scalar>>:: + from_implicit_null() .map_err(|e| { - let mut e = ::juniper::IntoFieldError::into_field_error(e); - e.message = format!(#err_text, e.message); - e + let e = ::juniper::IntoFieldError::into_field_error(e); + ::juniper::FieldError::new( + format!(#err_text, e.message()), + e.extensions().clone(), + ) }) }, Ok, diff --git a/juniper_codegen/src/common/field/mod.rs b/juniper_codegen/src/common/field/mod.rs index 360cb28bb..fbf442ced 100644 --- a/juniper_codegen/src/common/field/mod.rs +++ b/juniper_codegen/src/common/field/mod.rs @@ -268,7 +268,7 @@ 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 @@ -285,7 +285,7 @@ impl Definition { } } - /// 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). /// diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs index a7bdf882e..b18b84702 100644 --- a/juniper_codegen/src/lib.rs +++ b/juniper_codegen/src/lib.rs @@ -231,9 +231,9 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream { /// } /// /// fn from_input_value(value: &juniper::InputValue) -> Result { -/// Ok(value.as_string_value() +/// value.as_string_value() /// .map(|s| UserID(s.to_owned())) -/// .ok_or_else(|| format!("Expected String, found: {}", value))?) +/// .ok_or_else(|| format!("Expected String, found: {}", value).into()) /// } /// /// fn from_str<'a>(value: juniper::ScalarToken<'a>) -> juniper::ParseScalarResult<'a, S> { From 115c55ff5a73805bf8a7d18da753a07f35560890 Mon Sep 17 00:00:00 2001 From: ilslv Date: Wed, 1 Dec 2021 16:22:35 +0300 Subject: [PATCH 10/25] CHANGELOG --- juniper/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md index 5c968de4b..720a3b490 100644 --- a/juniper/CHANGELOG.md +++ b/juniper/CHANGELOG.md @@ -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 = ""` attribute's argument renamed to `rename_all = ""`. ([#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 `#[graphql_scalar]` macro in `from_input_value()` return type. ([#987](https://github.com/graphql-rust/juniper/pull/987)) ## Features From 06e71c3bf69c859b78602fb7e644b22ecac80a8d Mon Sep 17 00:00:00 2001 From: ilslv Date: Wed, 1 Dec 2021 16:41:36 +0300 Subject: [PATCH 11/25] Correction --- juniper/src/integrations/chrono_tz.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/juniper/src/integrations/chrono_tz.rs b/juniper/src/integrations/chrono_tz.rs index c7f383a01..aa7b9020a 100644 --- a/juniper/src/integrations/chrono_tz.rs +++ b/juniper/src/integrations/chrono_tz.rs @@ -43,16 +43,20 @@ where #[cfg(test)] mod test { mod from_input_value { + use std::ops::Deref; + use chrono_tz::Tz; use crate::{graphql_input_value, FieldError, FromInputValue, InputValue}; fn tz_input_test(raw: &'static str, expected: Result) { let input: InputValue = graphql_input_value!((raw)); - let parsed = - FromInputValue::from_input_value(&input).map_err(|e: FieldError| e.message); + let parsed = FromInputValue::from_input_value(&input); - assert_eq!(parsed, expected.map_err(str::to_owned)); + assert_eq!( + parsed.as_ref().map_err(|e: &FieldError| e.message()), + expected.as_ref().map_err(Deref::deref), + ); } #[test] From 53a7c69d25a350a1a033d0aaecaf5d1608e46535 Mon Sep 17 00:00:00 2001 From: ilslv Date: Wed, 8 Dec 2021 15:19:09 +0300 Subject: [PATCH 12/25] Forbid `__typename` on subscription root --- .../juniper_tests/src/issue_372.rs | 52 +++++++++++++------ juniper/src/types/subscriptions.rs | 11 ---- .../rules/fields_on_correct_type.rs | 22 ++++++++ 3 files changed, 59 insertions(+), 26 deletions(-) diff --git a/integration_tests/juniper_tests/src/issue_372.rs b/integration_tests/juniper_tests/src/issue_372.rs index 8d765176a..70472df6f 100644 --- a/integration_tests/juniper_tests/src/issue_372.rs +++ b/integration_tests/juniper_tests/src/issue_372.rs @@ -1,14 +1,12 @@ //! Checks that `__typename` field queries okay on root types. //! See [#372](https://github.com/graphql-rust/juniper/issues/372) for details. -use futures::{stream, FutureExt as _}; +use futures::stream; use juniper::{ execute, graphql_object, graphql_subscription, graphql_value, graphql_vars, resolve_into_stream, RootNode, }; -use crate::util::extract_next; - pub struct Query; #[graphql_object] @@ -98,28 +96,52 @@ async fn explicit_mutation_typename() { #[tokio::test] async fn subscription_typename() { + use juniper::GraphQLError; + let query = r#"subscription { __typename }"#; let schema = RootNode::new(Query, Mutation, Subscription); - assert_eq!( - resolve_into_stream(query, None, &schema, &graphql_vars! {}, &()) - .then(|s| extract_next(s)) - .await, - Ok((graphql_value!({"__typename": "Subscription"}), vec![])), - ); + match resolve_into_stream(query, None, &schema, &graphql_vars! {}, &()).await { + Err(GraphQLError::ValidationError(mut errors)) => { + assert_eq!(errors.len(), 1); + + let error = errors.pop().unwrap(); + assert_eq!( + error.message(), + "__typename may not be included as a root field in a \ + subscription operation" + ); + assert_eq!(error.locations()[0].index(), 15); + assert_eq!(error.locations()[0].line(), 0); + assert_eq!(error.locations()[0].column(), 15); + } + _ => panic!("Expected ValidationError"), + }; } #[tokio::test] async fn explicit_subscription_typename() { + use juniper::GraphQLError; + let query = r#"subscription Subscription { __typename }"#; let schema = RootNode::new(Query, Mutation, Subscription); - assert_eq!( - resolve_into_stream(query, None, &schema, &graphql_vars! {}, &()) - .then(|s| extract_next(s)) - .await, - Ok((graphql_value!({"__typename": "Subscription"}), vec![])), - ); + match resolve_into_stream(query, None, &schema, &graphql_vars! {}, &()).await { + Err(GraphQLError::ValidationError(mut errors)) => { + assert_eq!(errors.len(), 1); + + let error = errors.pop().unwrap(); + assert_eq!( + error.message(), + "__typename may not be included as a root field in a \ + subscription operation" + ); + assert_eq!(error.locations()[0].index(), 28); + assert_eq!(error.locations()[0].line(), 0); + assert_eq!(error.locations()[0].column(), 28); + } + _ => panic!("Expected ValidationError"), + }; } diff --git a/juniper/src/types/subscriptions.rs b/juniper/src/types/subscriptions.rs index c8ac53af3..e99a5aafa 100644 --- a/juniper/src/types/subscriptions.rs +++ b/juniper/src/types/subscriptions.rs @@ -1,4 +1,3 @@ -use futures::{future, stream}; use serde::Serialize; use crate::{ @@ -293,16 +292,6 @@ where let response_name = f.alias.as_ref().unwrap_or(&f.name).item; - if f.name.item == "__typename" { - let typename = - Value::scalar(instance.concrete_type_name(executor.context(), info)); - object.add_field( - response_name, - Value::Scalar(Box::pin(stream::once(future::ok(typename)))), - ); - continue; - } - let meta_field = meta_type .field_by_name(f.name.item) .unwrap_or_else(|| { diff --git a/juniper/src/validation/rules/fields_on_correct_type.rs b/juniper/src/validation/rules/fields_on_correct_type.rs index c15379aac..8017a06f4 100644 --- a/juniper/src/validation/rules/fields_on_correct_type.rs +++ b/juniper/src/validation/rules/fields_on_correct_type.rs @@ -4,6 +4,7 @@ use crate::{ schema::meta::MetaType, validation::{ValidatorContext, Visitor}, value::ScalarValue, + Operation, OperationType, Selection, }; pub struct FieldsOnCorrectType; @@ -16,6 +17,27 @@ impl<'a, S> Visitor<'a, S> for FieldsOnCorrectType where S: ScalarValue, { + fn enter_operation_definition( + &mut self, + context: &mut ValidatorContext<'a, S>, + operation: &'a Spanning>, + ) { + // https://spec.graphql.org/October2021/#note-bc213 + if let OperationType::Subscription = operation.item.operation_type { + for selection in &operation.item.selection_set { + if let Selection::Field(field) = selection { + if field.item.name.item == "__typename" { + context.report_error( + "__typename may not be included as a root \ + field in a subscription operation", + &[field.item.name.start], + ); + } + } + } + } + } + fn enter_field( &mut self, context: &mut ValidatorContext<'a, S>, From c1d563164e1bb8448f83a4c010a916c2240bcf3f Mon Sep 17 00:00:00 2001 From: ilslv Date: Thu, 9 Dec 2021 15:24:25 +0300 Subject: [PATCH 13/25] CHANGELOG --- juniper/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md index 5c968de4b..118f46f7e 100644 --- a/juniper/CHANGELOG.md +++ b/juniper/CHANGELOG.md @@ -7,6 +7,7 @@ - `#[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 = ""` attribute's argument renamed to `rename_all = ""`. ([#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)) +- Forbid `__typename` field query on `subscription` [accordingly to the 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 From 64f7f8ea852e3e8ef59802358921029b96abb559 Mon Sep 17 00:00:00 2001 From: tyranron Date: Thu, 9 Dec 2021 15:23:22 +0100 Subject: [PATCH 14/25] Minor corrections --- integration_tests/juniper_tests/src/issue_372.rs | 14 +++++--------- juniper/CHANGELOG.md | 2 +- .../src/validation/rules/fields_on_correct_type.rs | 4 ++-- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/integration_tests/juniper_tests/src/issue_372.rs b/integration_tests/juniper_tests/src/issue_372.rs index 70472df6f..c92ef6aa3 100644 --- a/integration_tests/juniper_tests/src/issue_372.rs +++ b/integration_tests/juniper_tests/src/issue_372.rs @@ -1,10 +1,10 @@ -//! Checks that `__typename` field queries okay on root types. +//! Checks that `__typename` field queries okay (and not okay) on root types. //! See [#372](https://github.com/graphql-rust/juniper/issues/372) for details. use futures::stream; use juniper::{ execute, graphql_object, graphql_subscription, graphql_value, graphql_vars, - resolve_into_stream, RootNode, + resolve_into_stream, GraphQLError, RootNode, }; pub struct Query; @@ -96,8 +96,6 @@ async fn explicit_mutation_typename() { #[tokio::test] async fn subscription_typename() { - use juniper::GraphQLError; - let query = r#"subscription { __typename }"#; let schema = RootNode::new(Query, Mutation, Subscription); @@ -109,8 +107,8 @@ async fn subscription_typename() { let error = errors.pop().unwrap(); assert_eq!( error.message(), - "__typename may not be included as a root field in a \ - subscription operation" + "`__typename` may not be included as a root field in a \ + subscription operation", ); assert_eq!(error.locations()[0].index(), 15); assert_eq!(error.locations()[0].line(), 0); @@ -122,8 +120,6 @@ async fn subscription_typename() { #[tokio::test] async fn explicit_subscription_typename() { - use juniper::GraphQLError; - let query = r#"subscription Subscription { __typename }"#; let schema = RootNode::new(Query, Mutation, Subscription); @@ -135,7 +131,7 @@ async fn explicit_subscription_typename() { let error = errors.pop().unwrap(); assert_eq!( error.message(), - "__typename may not be included as a root field in a \ + "`__typename` may not be included as a root field in a \ subscription operation" ); assert_eq!(error.locations()[0].index(), 28); diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md index 118f46f7e..f773885ba 100644 --- a/juniper/CHANGELOG.md +++ b/juniper/CHANGELOG.md @@ -7,7 +7,7 @@ - `#[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 = ""` attribute's argument renamed to `rename_all = ""`. ([#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)) -- Forbid `__typename` field query on `subscription` [accordingly to the 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)) +- Forbid `__typename` field query 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 diff --git a/juniper/src/validation/rules/fields_on_correct_type.rs b/juniper/src/validation/rules/fields_on_correct_type.rs index 8017a06f4..68e6cc281 100644 --- a/juniper/src/validation/rules/fields_on_correct_type.rs +++ b/juniper/src/validation/rules/fields_on_correct_type.rs @@ -28,8 +28,8 @@ where if let Selection::Field(field) = selection { if field.item.name.item == "__typename" { context.report_error( - "__typename may not be included as a root \ - field in a subscription operation", + "`__typename` may not be included as a root \ + field in a subscription operation", &[field.item.name.start], ); } From 5cc75552feec4474c8c304142e65cc11f17a3471 Mon Sep 17 00:00:00 2001 From: tyranron Date: Fri, 10 Dec 2021 18:29:03 +0100 Subject: [PATCH 15/25] Corrections --- juniper/src/ast.rs | 9 +- juniper/src/executor/mod.rs | 30 +++- .../introspection/input_object.rs | 2 +- .../src/executor_tests/introspection/mod.rs | 8 +- juniper/src/executor_tests/variables.rs | 5 +- juniper/src/integrations/bson.rs | 16 +- juniper/src/integrations/chrono.rs | 35 ++-- juniper/src/integrations/chrono_tz.rs | 15 +- juniper/src/integrations/url.rs | 9 +- juniper/src/integrations/uuid.rs | 9 +- juniper/src/lib.rs | 2 +- juniper/src/macros/helper/mod.rs | 9 +- juniper/src/schema/meta.rs | 22 ++- juniper/src/types/containers.rs | 155 ++++++++++-------- juniper/src/types/scalars.rs | 22 +-- juniper/src/types/utilities.rs | 2 +- juniper/src/validation/input_value.rs | 19 ++- juniper/src/validation/test_harness.rs | 22 +-- juniper_codegen/src/graphql_union/mod.rs | 4 +- juniper_codegen/src/impl_scalar.rs | 2 +- juniper_codegen/src/lib.rs | 5 +- juniper_codegen/src/util/mod.rs | 17 +- 22 files changed, 232 insertions(+), 187 deletions(-) diff --git a/juniper/src/ast.rs b/juniper/src/ast.rs index 3c5971d26..a3f1d8034 100644 --- a/juniper/src/ast.rs +++ b/juniper/src/ast.rs @@ -150,7 +150,7 @@ pub type Document<'a, S> = [Definition<'a, S>]; #[doc(hidden)] pub type OwnedDocument<'a, S> = Vec>; -/// 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 [`Err`]. Thus not /// restricted in the definition of this trait, the returned [`Err`] should be @@ -178,9 +178,10 @@ pub trait FromInputValue: Sized { /// Performs the conversion from an absent value (e.g. to distinguish /// between implicit and explicit `null`). /// - /// The default implementation just calls - /// [`FromInputValue::from_input_value()`] as if an explicit `null` was - /// provided. + /// 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::from_input_value(&InputValue::::Null) } diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 719ce7abc..5a8cf32c7 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -167,12 +167,11 @@ impl From for FieldError { impl FieldError { /// Construct a new [`FieldError`] with additional data. /// - /// You can use the [`graphql_value!`] macro to construct an error: + /// You can use the [`graphql_value!`] macro for construction: /// ```rust - /// # use juniper::DefaultScalarValue; /// use juniper::{graphql_value, FieldError}; /// - /// # let _: FieldError = + /// # let _: FieldError = /// FieldError::new( /// "Could not open connection to the database", /// graphql_value!({"internal_error": "Connection refused"}), @@ -212,7 +211,7 @@ impl FieldError { /// Returns `"extensions"` field of this [`FieldError`]. /// - /// If there is none `"extensions"` then [`Value::Null`] will be returned. + /// If there is no `"extensions"`, then [`Value::Null`] will be returned. #[must_use] pub fn extensions(&self) -> &Value { &self.extensions @@ -249,7 +248,7 @@ pub type Variables = HashMap>; /// 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 itself to a +/// Any custom error type should implement this trait to convert itself into a /// [`FieldError`]. pub trait IntoFieldError { /// Performs the custom conversion into a [`FieldError`]. @@ -269,6 +268,24 @@ impl IntoFieldError for std::convert::Infallible { } } +impl<'a, S> IntoFieldError for &'a str { + fn into_field_error(self) -> FieldError { + FieldError::::from(self) + } +} + +impl IntoFieldError for String { + fn into_field_error(self) -> FieldError { + FieldError::::from(self) + } +} + +impl<'a, S> IntoFieldError for Cow<'a, str> { + fn into_field_error(self) -> FieldError { + FieldError::::from(self) + } +} + #[doc(hidden)] pub trait IntoResolvable<'a, S, T, C> where @@ -1228,6 +1245,7 @@ impl<'r, S: 'r> Registry<'r, S> { pub fn build_scalar_type(&mut self, info: &T::TypeInfo) -> ScalarMeta<'r, S> where T: GraphQLType + FromInputValue + ParseScalarValue + 'r, + T::Error: IntoFieldError, S: ScalarValue, { let name = T::name(info).expect("Scalar types must be named. Implement `name()`"); @@ -1287,6 +1305,7 @@ impl<'r, S: 'r> Registry<'r, S> { ) -> EnumMeta<'r, S> where T: GraphQLType + FromInputValue, + T::Error: IntoFieldError, S: ScalarValue, { let name = T::name(info).expect("Enum types must be named. Implement `name()`"); @@ -1330,6 +1349,7 @@ impl<'r, S: 'r> Registry<'r, S> { ) -> InputObjectMeta<'r, S> where T: GraphQLType + FromInputValue, + T::Error: IntoFieldError, S: ScalarValue, { let name = T::name(info).expect("Input object types must be named. Implement name()"); diff --git a/juniper/src/executor_tests/introspection/input_object.rs b/juniper/src/executor_tests/introspection/input_object.rs index d433681a5..b3451463c 100644 --- a/juniper/src/executor_tests/introspection/input_object.rs +++ b/juniper/src/executor_tests/introspection/input_object.rs @@ -206,7 +206,7 @@ fn default_name_input_value() { let dv = DefaultName::from_input_value(&iv); - assert!(dv.is_ok(), "error: {:?}", dv.unwrap_err()); + assert!(dv.is_ok(), "error: {}", dv.unwrap_err()); let dv = dv.unwrap(); diff --git a/juniper/src/executor_tests/introspection/mod.rs b/juniper/src/executor_tests/introspection/mod.rs index 04be94de4..c26baf1eb 100644 --- a/juniper/src/executor_tests/introspection/mod.rs +++ b/juniper/src/executor_tests/introspection/mod.rs @@ -9,8 +9,8 @@ use crate::{ graphql_interface, graphql_object, graphql_scalar, graphql_value, graphql_vars, schema::model::RootNode, types::scalars::{EmptyMutation, EmptySubscription}, - value::{ParseScalarResult, ParseScalarValue, Value}, - FieldError, GraphQLEnum, + value::{ParseScalarResult, ParseScalarValue, ScalarValue, Value}, + GraphQLEnum, }; #[derive(GraphQLEnum)] @@ -28,10 +28,10 @@ impl GraphQLScalar for Scalar { Value::scalar(self.0) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { v.as_int_value() .map(Scalar) - .ok_or_else(|| format!("Expected Int, found: {}", v).into()) + .ok_or_else(|| format!("Expected `Int`, found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { diff --git a/juniper/src/executor_tests/variables.rs b/juniper/src/executor_tests/variables.rs index be895dc95..f2e3bb818 100644 --- a/juniper/src/executor_tests/variables.rs +++ b/juniper/src/executor_tests/variables.rs @@ -6,7 +6,6 @@ use crate::{ types::scalars::{EmptyMutation, EmptySubscription}, validation::RuleError, value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue}, - FieldError, GraphQLError::ValidationError, GraphQLInputObject, }; @@ -20,11 +19,11 @@ impl GraphQLScalar for TestComplexScalar { graphql_value!("SerializedValue") } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { v.as_string_value() .filter(|s| *s == "SerializedValue") .map(|_| TestComplexScalar) - .ok_or_else(|| format!("Expected SerializedValue String, found: {}", v).into()) + .ok_or_else(|| format!("Expected SerializedValue String, found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { diff --git a/juniper/src/integrations/bson.rs b/juniper/src/integrations/bson.rs index ee502c1f7..e1c9c1cf5 100644 --- a/juniper/src/integrations/bson.rs +++ b/juniper/src/integrations/bson.rs @@ -7,7 +7,7 @@ use crate::{ graphql_scalar, parser::{ParseError, ScalarToken, Token}, value::ParseScalarResult, - FieldError, Value, + Value, }; #[graphql_scalar(description = "ObjectId")] @@ -19,13 +19,12 @@ where Value::scalar(self.to_hex()) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { v.as_string_value() - .ok_or_else(|| format!("Expected String, found: {}", v)) + .ok_or_else(|| format!("Expected `String`, found: {}", v)) .and_then(|s| { - Self::parse_str(s).map_err(|e| format!("Failed to parse UtcDateTime: {}", e)) + Self::parse_str(s).map_err(|e| format!("Failed to parse `ObjectId`: {}", e)) }) - .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -46,15 +45,14 @@ where Value::scalar((*self).to_chrono().to_rfc3339()) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { v.as_string_value() - .ok_or_else(|| format!("Expected String, found: {}", v)) + .ok_or_else(|| format!("Expected `String`, found: {}", v)) .and_then(|s| { s.parse::>() - .map_err(|e| format!("Failed to parse UtcDateTime: {}", e)) + .map_err(|e| format!("Failed to parse `UtcDateTime`: {}", e)) }) .map(Self::from_chrono) - .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { diff --git a/juniper/src/integrations/chrono.rs b/juniper/src/integrations/chrono.rs index 82e28e42e..9c0c5f64a 100644 --- a/juniper/src/integrations/chrono.rs +++ b/juniper/src/integrations/chrono.rs @@ -21,7 +21,7 @@ use chrono::prelude::*; use crate::{ parser::{ParseError, ScalarToken, Token}, value::{ParseScalarResult, ParseScalarValue}, - FieldError, Value, + Value, }; #[doc(hidden)] @@ -36,14 +36,13 @@ where Value::scalar(self.to_rfc3339()) } - fn from_input_value(v: &InputValue) -> Result, FieldError> { + fn from_input_value(v: &InputValue) -> Result, String> { v.as_string_value() - .ok_or_else(|| format!("Expected String, found: {}", v)) + .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)) + .map_err(|e| format!("Failed to parse `DateTimeFixedOffset`: {}", e)) }) - .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -64,14 +63,13 @@ where Value::scalar(self.to_rfc3339()) } - fn from_input_value(v: &InputValue) -> Result, FieldError> { + fn from_input_value(v: &InputValue) -> Result, String> { v.as_string_value() - .ok_or_else(|| format!("Expected String, found: {}", v)) + .ok_or_else(|| format!("Expected `String`, found: {}", v)) .and_then(|s| { s.parse::>() - .map_err(|e| format!("Failed to parse DateTime: {}", e)) + .map_err(|e| format!("Failed to parse `DateTimeUtc`: {}", e)) }) - .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -97,14 +95,13 @@ where Value::scalar(self.format("%Y-%m-%d").to_string()) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { v.as_string_value() - .ok_or_else(|| format!("Expected String, found: {}", v)) + .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)) + .map_err(|e| format!("Failed to parse `NaiveDate`: {}", e)) }) - .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -126,14 +123,13 @@ where Value::scalar(self.format("%H:%M:%S").to_string()) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { v.as_string_value() - .ok_or_else(|| format!("Expected String, found: {}", v)) + .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)) + .map_err(|e| format!("Failed to parse `NaiveTime`: {}", e)) }) - .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -156,15 +152,14 @@ where Value::scalar(self.timestamp() as f64) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { v.as_float_value() - .ok_or_else(|| format!("Expected Float, found: {}", v)) + .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)) }) - .map_err(Into::into) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { diff --git a/juniper/src/integrations/chrono_tz.rs b/juniper/src/integrations/chrono_tz.rs index aa7b9020a..9947015fd 100644 --- a/juniper/src/integrations/chrono_tz.rs +++ b/juniper/src/integrations/chrono_tz.rs @@ -9,7 +9,7 @@ use crate::{ graphql_scalar, parser::{ParseError, ScalarToken, Token}, value::ParseScalarResult, - FieldError, Value, + Value, }; #[graphql_scalar(name = "Tz", description = "Timezone")] @@ -21,14 +21,13 @@ where Value::scalar(self.name().to_owned()) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { v.as_string_value() - .ok_or_else(|| format!("Expected String, found: {}", v)) + .ok_or_else(|| format!("Expected `String`, found: {}", v)) .and_then(|s| { s.parse::() - .map_err(|e| format!("Failed to parse Timezone: {}", e)) + .map_err(|e| format!("Failed to parse `Tz`: {}", e)) }) - .map_err(Into::into) } fn from_str<'a>(val: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -76,7 +75,7 @@ mod test { fn forward_slash() { tz_input_test( "Abc/Xyz", - Err("Failed to parse Timezone: received invalid timezone"), + Err("Failed to parse `Timezone`: received invalid timezone"), ); } @@ -84,7 +83,7 @@ mod test { fn number() { tz_input_test( "8086", - Err("Failed to parse Timezone: received invalid timezone"), + Err("Failed to parse `Timezone`: received invalid timezone"), ); } @@ -92,7 +91,7 @@ mod test { fn no_forward_slash() { tz_input_test( "AbcXyz", - Err("Failed to parse Timezone: received invalid timezone"), + Err("Failed to parse `Timezone`: received invalid timezone"), ); } } diff --git a/juniper/src/integrations/url.rs b/juniper/src/integrations/url.rs index b3ae25196..97481bb20 100644 --- a/juniper/src/integrations/url.rs +++ b/juniper/src/integrations/url.rs @@ -4,7 +4,7 @@ use url::Url; use crate::{ value::{ParseScalarResult, ParseScalarValue}, - FieldError, Value, + Value, }; #[crate::graphql_scalar(description = "Url")] @@ -16,11 +16,10 @@ where Value::scalar(self.as_str().to_owned()) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { 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))) - .map_err(Into::into) + .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> { diff --git a/juniper/src/integrations/uuid.rs b/juniper/src/integrations/uuid.rs index 5dfa7a7bb..ae0c04a87 100644 --- a/juniper/src/integrations/uuid.rs +++ b/juniper/src/integrations/uuid.rs @@ -7,7 +7,7 @@ use uuid::Uuid; use crate::{ parser::{ParseError, ScalarToken, Token}, value::ParseScalarResult, - FieldError, Value, + Value, }; #[crate::graphql_scalar(description = "Uuid")] @@ -19,11 +19,10 @@ where Value::scalar(self.to_string()) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { 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))) - .map_err(Into::into) + .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> { diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index 365261e2e..259936739 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -163,7 +163,7 @@ pub use crate::{ introspection::IntrospectionFormat, macros::helper::{ subscription::{ExtractTypeFromStream, IntoFieldResult}, - AsDynGraphQLValue, ExtractErrorFromResult, + AsDynGraphQLValue, ExtractError, }, parser::{ParseError, Spanning}, schema::{ diff --git a/juniper/src/macros/helper/mod.rs b/juniper/src/macros/helper/mod.rs index 89249a7a9..0bd728fb2 100644 --- a/juniper/src/macros/helper/mod.rs +++ b/juniper/src/macros/helper/mod.rs @@ -33,17 +33,18 @@ pub trait AsDynGraphQLValue { crate::sa::assert_obj_safe!(AsDynGraphQLValue); -/// This trait is used in `juniper::graphql_scalar` macro to get [`Error`] type +/// This trait is used by [`graphql_scalar!`] macro to retrieve [`Error`] type /// from a [`Result`]. /// /// [`Error`]: Result::Error -pub trait ExtractErrorFromResult { - /// [`Error`] of a [`Result`]. +/// [`graphql_scalar!`]: macro@crate::graphql_scalar +pub trait ExtractError { + /// Extracted [`Error`] type of this [`Result`]. /// /// [`Error`]: Result::Error type Error; } -impl ExtractErrorFromResult for Result { +impl ExtractError for Result { type Error = E; } diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs index c20e21e52..fdb6e50b5 100644 --- a/juniper/src/schema/meta.rs +++ b/juniper/src/schema/meta.rs @@ -1,5 +1,6 @@ //! Types used to describe a `GraphQL` schema +use juniper::IntoFieldError; use std::{ borrow::{Cow, ToOwned}, fmt, @@ -11,6 +12,7 @@ use crate::{ schema::model::SchemaType, types::base::TypeKind, 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, - pub(crate) try_parse_fn: for<'b> fn(&'b InputValue) -> bool, + pub(crate) try_parse_fn: for<'b> fn(&'b InputValue) -> Result<(), FieldError>, pub(crate) parse_fn: for<'b> fn(ScalarToken<'b>) -> Result>, } @@ -88,7 +90,7 @@ pub struct EnumMeta<'a, S> { pub description: Option, #[doc(hidden)] pub values: Vec, - pub(crate) try_parse_fn: for<'b> fn(&'b InputValue) -> bool, + pub(crate) try_parse_fn: for<'b> fn(&'b InputValue) -> Result<(), FieldError>, } /// Interface type metadata @@ -121,7 +123,7 @@ pub struct InputObjectMeta<'a, S> { pub description: Option, #[doc(hidden)] pub input_fields: Vec>, - pub(crate) try_parse_fn: for<'b> fn(&'b InputValue) -> bool, + pub(crate) try_parse_fn: for<'b> fn(&'b InputValue) -> Result<(), FieldError>, } /// 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 fn(&'b InputValue) -> bool> { + pub fn input_value_parse_fn( + &self, + ) -> Option fn(&'b InputValue) -> Result<(), FieldError>> { match *self { MetaType::Scalar(ScalarMeta { ref try_parse_fn, .. @@ -412,6 +416,7 @@ impl<'a, S> ScalarMeta<'a, S> { pub fn new(name: Cow<'a, str>) -> Self where T: FromInputValue + ParseScalarValue, + T::Error: IntoFieldError, { Self { name, @@ -510,6 +515,7 @@ impl<'a, S> EnumMeta<'a, S> { pub fn new(name: Cow<'a, str>, values: &[EnumValue]) -> Self where T: FromInputValue, + T::Error: IntoFieldError, { Self { name, @@ -595,6 +601,7 @@ impl<'a, S> InputObjectMeta<'a, S> { pub fn new(name: Cow<'a, str>, input_fields: &[Argument<'a, S>]) -> Self where T: FromInputValue, + T::Error: IntoFieldError, S: Clone, { Self { @@ -736,9 +743,12 @@ impl<'a, S: fmt::Debug> fmt::Debug for InputObjectMeta<'a, S> { } } -fn try_parse_fn(v: &InputValue) -> bool +fn try_parse_fn(v: &InputValue) -> Result<(), FieldError> where T: FromInputValue, + T::Error: IntoFieldError, { - T::from_input_value(v).is_ok() + T::from_input_value(v) + .map(drop) + .map_err(T::Error::into_field_error) } diff --git a/juniper/src/types/containers.rs b/juniper/src/types/containers.rs index 14816194c..5ec67b9e8 100644 --- a/juniper/src/types/containers.rs +++ b/juniper/src/types/containers.rs @@ -1,5 +1,4 @@ use std::{ - array, mem::{self, MaybeUninit}, ptr, }; @@ -86,7 +85,7 @@ impl> FromInputValue for Option { fn from_input_value(v: &InputValue) -> Result { match v { - &InputValue::Null => Ok(None), + InputValue::Null => Ok(None), v => v.convert().map(Some), } } @@ -166,6 +165,7 @@ impl> FromInputValue for Vec { 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]), } @@ -306,43 +306,6 @@ where } } -/// Error converting [`InputValue`] into exact-size [`array`](prim@array). -pub enum FromInputValueArrayError -where - T: FromInputValue, - S: ScalarValue, -{ - /// Not enough elements. - NotEnough(usize), - - /// Too many elements. Contains __all__ [`InputValue`] elements. - TooMuch(Vec), - - /// Underlying [`ScalarValue`] conversion error. - Scalar(T::Error), -} - -impl IntoFieldError for FromInputValueArrayError -where - T: FromInputValue, - T::Error: IntoFieldError, - S: ScalarValue, -{ - fn into_field_error(self) -> FieldError { - const ERROR_PREFIX: &str = "Failed to convert into exact-size array"; - - match self { - Self::NotEnough(len) => { - format!("{}: required {} more elements", ERROR_PREFIX, len).into() - } - Self::TooMuch(el) => { - format!("{}: too much elements: {}", ERROR_PREFIX, el.len()).into() - } - Self::Scalar(s) => s.into_field_error(), - } - } -} - impl FromInputValue for [T; N] where T: FromInputValue, @@ -377,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` implementation is not @@ -396,7 +375,7 @@ where }; let mut items = ls.iter().map(|i| i.item.convert()); - for (id, elem) in out.arr.iter_mut().enumerate() { + for elem in &mut out.arr[..] { if let Some(i) = items .next() .transpose() @@ -404,8 +383,6 @@ where { *elem = MaybeUninit::new(i); out.init_len += 1; - } else { - return Err(FromInputValueArrayError::NotEnough(N - id)); } } @@ -422,19 +399,19 @@ where // we won't have a double-free when `T: Drop` here, // because original array elements are `MaybeUninit`, so // do nothing on `Drop`. - let arr = unsafe { mem::transmute_copy::<_, Self>(&out.arr) }; - - if items.len() > 0 { - return Err(FromInputValueArrayError::TooMuch( - array::IntoIter::new(arr) - .map(Ok) - .chain(items) - .collect::>() - .map_err(FromInputValueArrayError::Scalar)?, - )); - } - - Ok(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 @@ -442,24 +419,26 @@ where .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: + // `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`. + // 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 if N == 0 { - Err(FromInputValueArrayError::TooMuch(vec![e])) } else { - Err(FromInputValueArrayError::NotEnough(N - 1)) + Err(FromInputValueArrayError::WrongCount { + actual: 1, + expected: N, + }) } }) } @@ -477,6 +456,44 @@ where } } +/// Error converting [`InputValue`] into exact-size [`array`](prim@array). +pub enum FromInputValueArrayError +where + T: FromInputValue, + 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 IntoFieldError for FromInputValueArrayError +where + T: FromInputValue, + T::Error: IntoFieldError, + S: ScalarValue, +{ + fn into_field_error(self) -> FieldError { + 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, info: &T::TypeInfo, @@ -543,7 +560,7 @@ where #[cfg(test)] mod coercion { - use crate::{FromInputValue as _, InputValue}; + use crate::{graphql_input_value, FromInputValue as _, InputValue}; type V = InputValue; @@ -552,7 +569,7 @@ mod coercion { #[test] fn vec() { assert_eq!( - >::from_input_value(&V::list(vec![V::scalar(1), V::scalar(2), V::scalar(3)])), + >::from_input_value(&graphql_input_value!([1, 2, 3])), Ok(vec![1, 2, 3]), ); // TODO: all examples diff --git a/juniper/src/types/scalars.rs b/juniper/src/types/scalars.rs index 83039aba6..d55c6ac3d 100644 --- a/juniper/src/types/scalars.rs +++ b/juniper/src/types/scalars.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use crate::{ ast::{InputValue, Selection, ToInputValue}, - executor::{ExecutionResult, Executor, FieldError, Registry}, + executor::{ExecutionResult, Executor, Registry}, parser::{LexerError, ParseError, ScalarToken, Token}, schema::meta::MetaType, types::{ @@ -59,12 +59,12 @@ where Value::scalar(self.0.clone()) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { v.as_string_value() .map(str::to_owned) .or_else(|| v.as_int_value().map(|i| i.to_string())) - .ok_or_else(|| format!("Expected String or Int, found: {}", v).into()) .map(ID) + .ok_or_else(|| format!("Expected `String` or `Int`, found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -84,10 +84,10 @@ where Value::scalar(self.clone()) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { v.as_string_value() .map(str::to_owned) - .ok_or_else(|| format!("Expected String, found: {}", v).into()) + .ok_or_else(|| format!("Expected `String`, found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -272,10 +272,10 @@ where Value::scalar(*self) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { v.as_scalar_value() .and_then(ScalarValue::as_boolean) - .ok_or_else(|| format!("Expected Boolean, found: {}", v).into()) + .ok_or_else(|| format!("Expected `Boolean`, found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -293,9 +293,9 @@ where Value::scalar(*self) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { v.as_int_value() - .ok_or_else(|| format!("Expected Int, found: {}", v).into()) + .ok_or_else(|| format!("Expected `Int`, found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -318,9 +318,9 @@ where Value::scalar(*self) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { v.as_float_value() - .ok_or_else(|| format!("Expected Float, found: {}", v).into()) + .ok_or_else(|| format!("Expected `Float`, found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { diff --git a/juniper/src/types/utilities.rs b/juniper/src/types/utilities.rs index 1d1ec5fd6..2ed03b1f3 100644 --- a/juniper/src/types/utilities.rs +++ b/juniper/src/types/utilities.rs @@ -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 } diff --git a/juniper/src/validation/input_value.rs b/juniper/src/validation/input_value.rs index ba6803a69..95f5d1350 100644 --- a/juniper/src/validation/input_value.rs +++ b/juniper/src/validation/input_value.rs @@ -163,14 +163,16 @@ 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 +195,21 @@ fn unify_scalar<'a, S>( path: &Path<'a>, ) -> Vec where - S: fmt::Debug, + S: ScalarValue, { let mut errors: Vec = 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(), + ), )]; } diff --git a/juniper/src/validation/test_harness.rs b/juniper/src/validation/test_harness.rs index 0bff649c0..bcc0c7aae 100644 --- a/juniper/src/validation/test_harness.rs +++ b/juniper/src/validation/test_harness.rs @@ -2,7 +2,7 @@ use std::mem; use crate::{ ast::{FromInputValue, InputValue}, - executor::{FieldError, Registry}, + executor::Registry, parser::parse_document_source, schema::{ meta::{EnumValue, MetaType}, @@ -208,14 +208,14 @@ impl FromInputValue for DogCommand where S: ScalarValue, { - type Error = FieldError; + type Error = &'static str; fn from_input_value<'a>(v: &InputValue) -> Result { match v.as_enum_value() { Some("SIT") => Ok(DogCommand::Sit), Some("HEEL") => Ok(DogCommand::Heel), Some("DOWN") => Ok(DogCommand::Down), - _ => Err("Unknown DogCommand".into()), + _ => Err("Unknown DogCommand"), } } } @@ -316,7 +316,7 @@ impl FromInputValue for FurColor where S: ScalarValue, { - type Error = FieldError; + type Error = &'static str; fn from_input_value<'a>(v: &InputValue) -> Result { match v.as_enum_value() { @@ -324,7 +324,7 @@ where Some("BLACK") => Ok(FurColor::Black), Some("TAN") => Ok(FurColor::Tan), Some("SPOTTED") => Ok(FurColor::Spotted), - _ => Err("Unknown FurColor".into()), + _ => Err("Unknown FurColor"), } } } @@ -616,7 +616,7 @@ impl FromInputValue for ComplexInput where S: ScalarValue, { - type Error = FieldError; + type Error = String; fn from_input_value<'a>(v: &InputValue) -> Result { let obj = v.to_object_value().ok_or("Expected object")?; @@ -624,27 +624,27 @@ where Ok(ComplexInput { required_field: obj .get("requiredField") - .map(|v| v.convert().map_err(FieldError::map_scalar_value)) + .map(|v| v.convert()) .transpose()? .ok_or("Expected requiredField")?, int_field: obj .get("intField") - .map(|v| v.convert().map_err(FieldError::map_scalar_value)) + .map(|v| v.convert()) .transpose()? .ok_or("Expected intField")?, string_field: obj .get("stringField") - .map(|v| v.convert().map_err(FieldError::map_scalar_value)) + .map(|v| v.convert()) .transpose()? .ok_or("Expected stringField")?, boolean_field: obj .get("booleanField") - .map(|v| v.convert().map_err(FieldError::map_scalar_value)) + .map(|v| v.convert()) .transpose()? .ok_or("Expected booleanField")?, string_list_field: obj .get("stringListField") - .map(|v| v.convert().map_err(FieldError::map_scalar_value)) + .map(|v| v.convert()) .transpose()? .ok_or("Expected stringListField")?, }) diff --git a/juniper_codegen/src/graphql_union/mod.rs b/juniper_codegen/src/graphql_union/mod.rs index 040ce97f4..299cb7c24 100644 --- a/juniper_codegen/src/graphql_union/mod.rs +++ b/juniper_codegen/src/graphql_union/mod.rs @@ -707,8 +707,8 @@ impl VariantDefinition { return #resolving_code; } } - None => return Box::pin(::juniper::futures::future::ready( - Err(::juniper::FieldError::from("This GraphQLType has no name")) + None => return Box::pin(::juniper::futures::future::err( + ::juniper::FieldError::from("This GraphQLType has no name") )), } } diff --git a/juniper_codegen/src/impl_scalar.rs b/juniper_codegen/src/impl_scalar.rs index f05ea91ba..7bf901c63 100644 --- a/juniper_codegen/src/impl_scalar.rs +++ b/juniper_codegen/src/impl_scalar.rs @@ -309,7 +309,7 @@ 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::ExtractErrorFromResult>::Error; + type Error = <#from_input_value_result as ::juniper::ExtractError>::Error; fn from_input_value(#from_input_value_arg: &::juniper::InputValue<#generic_type>) -> #from_input_value_result { #from_input_value_body diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs index b18b84702..b25967a6e 100644 --- a/juniper_codegen/src/lib.rs +++ b/juniper_codegen/src/lib.rs @@ -230,10 +230,11 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream { /// juniper::Value::scalar(self.0.to_owned()) /// } /// -/// fn from_input_value(value: &juniper::InputValue) -> Result { +/// // NOTE: The error type should implement `IntoFieldError`. +/// fn from_input_value(value: &juniper::InputValue) -> Result { /// value.as_string_value() /// .map(|s| UserID(s.to_owned())) -/// .ok_or_else(|| format!("Expected String, found: {}", value).into()) +/// .ok_or_else(|| format!("Expected String, found: {}", value)) /// } /// /// fn from_str<'a>(value: juniper::ScalarToken<'a>) -> juniper::ParseScalarResult<'a, S> { diff --git a/juniper_codegen/src/util/mod.rs b/juniper_codegen/src/util/mod.rs index bb9bcbf44..a6083e073 100644 --- a/juniper_codegen/src/util/mod.rs +++ b/juniper_codegen/src/util/mod.rs @@ -860,18 +860,16 @@ impl GraphQLTypeDefiniton { impl#impl_generics ::juniper::FromInputValue<#scalar> for #ty #where_clause { - type Error = ::juniper::FieldError<#scalar>; + type Error = ::std::string::String; fn from_input_value( v: &::juniper::InputValue<#scalar> - ) -> Result<#ty, ::juniper::FieldError<#scalar>> { + ) -> Result<#ty, Self::Error> { match v.as_enum_value().or_else(|| { v.as_string_value() }) { #( #from_inputs )* - _ => Err(::juniper::FieldError::<#scalar>::from( - format!("Unknown Enum value: {}", v)) - ), + _ => Err(format!("Unknown enum value: {}", v)), } } } @@ -986,7 +984,7 @@ impl GraphQLTypeDefiniton { match obj.get(#field_name) { #from_input_default Some(ref v) => { - ::juniper::FromInputValue::from_input_value(v) + ::juniper::FromInputValue::<#scalar>::from_input_value(v) .map_err(::juniper::IntoFieldError::into_field_error)? }, None => { @@ -1111,10 +1109,11 @@ impl GraphQLTypeDefiniton { fn from_input_value( value: &::juniper::InputValue<#scalar> - ) -> Result> { - let obj = value.to_object_value() + ) -> Result { + let obj = value + .to_object_value() .ok_or_else(|| ::juniper::FieldError::<#scalar>::from( - format!("Expected Object, found: {}", value)) + format!("Expected input object, found: {}", value)) )?; Ok(#ty { #( #from_inputs )* From 3e438866556a955e6aa021231a2e5c025e2a0436 Mon Sep 17 00:00:00 2001 From: ilslv Date: Mon, 13 Dec 2021 08:05:47 +0300 Subject: [PATCH 16/25] Add tests --- .../rules/fields_on_correct_type.rs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/juniper/src/validation/rules/fields_on_correct_type.rs b/juniper/src/validation/rules/fields_on_correct_type.rs index 68e6cc281..ecd9cf693 100644 --- a/juniper/src/validation/rules/fields_on_correct_type.rs +++ b/juniper/src/validation/rules/fields_on_correct_type.rs @@ -379,4 +379,28 @@ mod tests { "#, ); } + + #[test] + fn typename_on_subscription() { + expect_fails_rule::<_, _, DefaultScalarValue>( + factory, + r#"subscription { __typename }"#, + &[RuleError::new( + "`__typename` may not be included as a root field in a subscription operation", + &[SourcePosition::new(15, 0, 15)], + )], + ); + } + + #[test] + fn typename_on_explicit_subscription() { + expect_fails_rule::<_, _, DefaultScalarValue>( + factory, + r#"subscription SubscriptionRoot { __typename }"#, + &[RuleError::new( + "`__typename` may not be included as a root field in a subscription operation", + &[SourcePosition::new(32, 0, 32)], + )], + ); + } } From efca2c0f3027c0ce43537067cda46abf141e1e44 Mon Sep 17 00:00:00 2001 From: ilslv Date: Mon, 13 Dec 2021 08:41:10 +0300 Subject: [PATCH 17/25] Tests fixes --- .../fail/union/trait_fail_infer_context.stderr | 9 +++++++-- juniper/src/executor_tests/introspection/input_object.rs | 2 +- juniper/src/executor_tests/variables.rs | 9 ++++++--- juniper/src/integrations/chrono_tz.rs | 8 ++++---- juniper/src/parser/tests/value.rs | 5 +++-- juniper/src/types/containers.rs | 6 ++---- 6 files changed, 23 insertions(+), 16 deletions(-) diff --git a/integration_tests/codegen_fail/fail/union/trait_fail_infer_context.stderr b/integration_tests/codegen_fail/fail/union/trait_fail_infer_context.stderr index b4eb24816..e3e3b8065 100644 --- a/integration_tests/codegen_fail/fail/union/trait_fail_infer_context.stderr +++ b/integration_tests/codegen_fail/fail/union/trait_fail_infer_context.stderr @@ -1,8 +1,13 @@ error[E0277]: the trait bound `CustomContext: FromContext` is not satisfied --> fail/union/trait_fail_infer_context.rs:3:1 | -3 | #[graphql_union] - | ^^^^^^^^^^^^^^^^ expected an implementor of trait `FromContext` +3 | #[graphql_union] + | ^^^^^^^^^^^^^^^^ expected an implementor of trait `FromContext` +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 diff --git a/juniper/src/executor_tests/introspection/input_object.rs b/juniper/src/executor_tests/introspection/input_object.rs index b3451463c..d433681a5 100644 --- a/juniper/src/executor_tests/introspection/input_object.rs +++ b/juniper/src/executor_tests/introspection/input_object.rs @@ -206,7 +206,7 @@ fn default_name_input_value() { let dv = DefaultName::from_input_value(&iv); - assert!(dv.is_ok(), "error: {}", dv.unwrap_err()); + assert!(dv.is_ok(), "error: {:?}", dv.unwrap_err()); let dv = dv.unwrap(); diff --git a/juniper/src/executor_tests/variables.rs b/juniper/src/executor_tests/variables.rs index f2e3bb818..6af5ef7f5 100644 --- a/juniper/src/executor_tests/variables.rs +++ b/juniper/src/executor_tests/variables.rs @@ -1072,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)], )]), ); @@ -1096,7 +1097,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)], )]), ); @@ -1154,7 +1156,8 @@ 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)], )]), ); diff --git a/juniper/src/integrations/chrono_tz.rs b/juniper/src/integrations/chrono_tz.rs index 9947015fd..8fce3a97b 100644 --- a/juniper/src/integrations/chrono_tz.rs +++ b/juniper/src/integrations/chrono_tz.rs @@ -53,7 +53,7 @@ mod test { let parsed = FromInputValue::from_input_value(&input); assert_eq!( - parsed.as_ref().map_err(|e: &FieldError| e.message()), + parsed.as_ref().map_err(Deref::deref), expected.as_ref().map_err(Deref::deref), ); } @@ -75,7 +75,7 @@ mod test { fn forward_slash() { tz_input_test( "Abc/Xyz", - Err("Failed to parse `Timezone`: received invalid timezone"), + Err("Failed to parse `Tz`: received invalid timezone"), ); } @@ -83,7 +83,7 @@ mod test { fn number() { tz_input_test( "8086", - Err("Failed to parse `Timezone`: received invalid timezone"), + Err("Failed to parse `Tz`: received invalid timezone"), ); } @@ -91,7 +91,7 @@ mod test { fn no_forward_slash() { tz_input_test( "AbcXyz", - Err("Failed to parse `Timezone`: received invalid timezone"), + Err("Failed to parse `Tz`: received invalid timezone"), ); } } diff --git a/juniper/src/parser/tests/value.rs b/juniper/src/parser/tests/value.rs index 0ce829000..8dbe78bd2 100644 --- a/juniper/src/parser/tests/value.rs +++ b/juniper/src/parser/tests/value.rs @@ -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(name: &'static str) -> MetaType +fn scalar_meta(name: &'static str) -> MetaType where T: FromInputValue + ParseScalarValue, + T::Error: IntoFieldError, { MetaType::Scalar(ScalarMeta::new::(name.into())) } diff --git a/juniper/src/types/containers.rs b/juniper/src/types/containers.rs index 5ec67b9e8..580a8c981 100644 --- a/juniper/src/types/containers.rs +++ b/juniper/src/types/containers.rs @@ -568,10 +568,8 @@ mod coercion { // https://spec.graphql.org/June2018/#sec-Type-System.List #[test] fn vec() { - assert_eq!( - >::from_input_value(&graphql_input_value!([1, 2, 3])), - Ok(vec![1, 2, 3]), - ); + let v: V = graphql_input_value!([1, 2, 3]); + assert_eq!(>::from_input_value(&v), Ok(vec![1, 2, 3]),); // TODO: all examples } } From 0112c1adae539f21e4caea664a8427bff2362494 Mon Sep 17 00:00:00 2001 From: ilslv Date: Mon, 13 Dec 2021 10:20:46 +0300 Subject: [PATCH 18/25] Upgrade actix --- juniper_actix/Cargo.toml | 12 +++--- juniper_actix/src/lib.rs | 82 ++++++++++++++++++++++++++-------------- 2 files changed, 59 insertions(+), 35 deletions(-) diff --git a/juniper_actix/Cargo.toml b/juniper_actix/Cargo.toml index d5730bd7c..f3f4fd27f 100644 --- a/juniper_actix/Cargo.toml +++ b/juniper_actix/Cargo.toml @@ -13,10 +13,10 @@ subscriptions = ["juniper_graphql_ws", "tokio"] [dependencies] actix = "0.12" -actix-http = "3.0.0-beta.13" +actix-http = "3.0.0-beta.15" http = "0.2.4" -actix-web = "4.0.0-beta.12" -actix-web-actors = "4.0.0-beta.7" +actix-web = "4.0.0-beta.14" +actix-web-actors = "4.0.0-beta.8" juniper = { version = "0.15.7", path = "../juniper", default-features = false } juniper_graphql_ws = { version = "0.3.0", path = "../juniper_graphql_ws", optional = true } @@ -30,11 +30,11 @@ tokio = { version = "1.0", features = ["sync"], optional = true } [dev-dependencies] actix-rt = "2" -actix-cors = "0.6.0-beta.4" -actix-identity = "0.4.0-beta.4" +actix-cors = "0.6.0-beta.6" +actix-identity = "0.4.0-beta.5" tokio = "1.0" async-stream = "0.3" -actix-test = "0.1.0-beta.6" +actix-test = "0.1.0-beta.8" juniper = { version = "0.15.7", path = "../juniper", features = ["expose-test-schema"] } diff --git a/juniper_actix/src/lib.rs b/juniper_actix/src/lib.rs index d1b89da5d..f22906b9f 100644 --- a/juniper_actix/src/lib.rs +++ b/juniper_actix/src/lib.rs @@ -466,7 +466,9 @@ pub mod subscriptions { #[cfg(test)] mod tests { - use actix_http::body::AnyBody; + use std::{future::Future, pin::Pin, task}; + + use actix_http::body::{BoxBody, MessageBody}; use actix_web::{ dev::ServiceResponse, http, @@ -475,6 +477,7 @@ mod tests { web::Data, App, }; + use bytes::Bytes; use juniper::{ http::tests::{run_http_test_suite, HttpIntegration, TestResponse}, tests::fixtures::starwars::schema::{Database, Query}, @@ -487,13 +490,27 @@ mod tests { type Schema = juniper::RootNode<'static, Query, EmptyMutation, EmptySubscription>; - async fn take_response_body_string(resp: &mut ServiceResponse) -> String { - match resp.response().body() { - AnyBody::Bytes(body) => String::from_utf8(body.to_vec()).unwrap(), - _ => String::from(""), + struct BoxBodyFuture(BoxBody); + + impl Future for BoxBodyFuture { + type Output = Option>; + + fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + Pin::new(&mut self.get_mut().0).poll_next(cx) } } + async fn take_response_body_string(resp: ServiceResponse) -> String { + String::from_utf8( + BoxBodyFuture(resp.into_body()) + .await + .unwrap() + .unwrap() + .to_vec(), + ) + .unwrap() + } + async fn index( req: HttpRequest, payload: actix_web::web::Payload, @@ -537,13 +554,13 @@ mod tests { .append_header((ACCEPT, "text/html")) .to_request(); - let mut resp = test::call_service(&mut app, req).await; - let body = take_response_body_string(&mut resp).await; + let resp = test::call_service(&mut app, req).await; assert_eq!(resp.status(), http::StatusCode::OK); assert_eq!( resp.headers().get(CONTENT_TYPE).unwrap().to_str().unwrap(), "text/html; charset=utf-8" ); + let body = take_response_body_string(resp).await; assert!(body.contains("")); assert!(body.contains( "" @@ -578,13 +595,13 @@ mod tests { .append_header((ACCEPT, "text/html")) .to_request(); - let mut resp = test::call_service(&mut app, req).await; - let body = take_response_body_string(&mut resp).await; + let resp = test::call_service(&mut app, req).await; assert_eq!(resp.status(), http::StatusCode::OK); assert_eq!( resp.headers().get(CONTENT_TYPE).unwrap().to_str().unwrap(), "text/html; charset=utf-8" ); + let body = take_response_body_string(resp).await; assert!(body.contains("GraphQLPlayground.init(root, { endpoint: '/dogs-api/graphql', subscriptionEndpoint: '/dogs-api/subscriptions' })")); } @@ -611,17 +628,17 @@ mod tests { ) .await; - let mut resp = test::call_service(&mut app, req).await; - dbg!(take_response_body_string(&mut resp).await); + let resp = test::call_service(&mut app, req).await; assert_eq!(resp.status(), http::StatusCode::OK); - assert_eq!( - take_response_body_string(&mut resp).await, - r#"{"data":{"hero":{"name":"R2-D2"}}}"# - ); assert_eq!( resp.headers().get("content-type").unwrap(), "application/json", ); + + assert_eq!( + take_response_body_string(resp).await, + r#"{"data":{"hero":{"name":"R2-D2"}}}"# + ); } #[actix_web::rt::test] @@ -644,17 +661,18 @@ mod tests { ) .await; - let mut resp = test::call_service(&mut app, req).await; + let resp = test::call_service(&mut app, req).await; assert_eq!(resp.status(), http::StatusCode::OK); - assert_eq!( - take_response_body_string(&mut resp).await, - r#"{"data":{"hero":{"name":"R2-D2"}}}"# - ); assert_eq!( resp.headers().get("content-type").unwrap(), "application/json", ); + + assert_eq!( + take_response_body_string(resp).await, + r#"{"data":{"hero":{"name":"R2-D2"}}}"# + ); } #[actix_web::rt::test] @@ -688,17 +706,17 @@ mod tests { ) .await; - let mut resp = test::call_service(&mut app, req).await; + let resp = test::call_service(&mut app, req).await; assert_eq!(resp.status(), http::StatusCode::OK); - assert_eq!( - take_response_body_string(&mut resp).await, - r#"[{"data":{"hero":{"name":"R2-D2"}}},{"data":{"hero":{"id":"1000","name":"Luke Skywalker"}}}]"# - ); assert_eq!( resp.headers().get("content-type").unwrap(), "application/json", ); + assert_eq!( + take_response_body_string(resp).await, + r#"[{"data":{"hero":{"name":"R2-D2"}}},{"data":{"hero":{"id":"1000","name":"Luke Skywalker"}}}]"# + ); } #[test] @@ -757,14 +775,20 @@ mod tests { } } - async fn make_test_response(mut resp: ServiceResponse) -> TestResponse { - let body = take_response_body_string(&mut resp).await; + async fn make_test_response(resp: ServiceResponse) -> TestResponse { let status_code = resp.status().as_u16(); - let content_type = resp.headers().get(CONTENT_TYPE).unwrap(); + let content_type = resp + .headers() + .get(CONTENT_TYPE) + .unwrap() + .to_str() + .unwrap() + .to_string(); + let body = take_response_body_string(resp).await; TestResponse { status_code: status_code as i32, body: Some(body), - content_type: content_type.to_str().unwrap().to_string(), + content_type, } } From 7006d5838f6bf5ac4e3b27207bb7589b518aa14f Mon Sep 17 00:00:00 2001 From: ilslv Date: Mon, 13 Dec 2021 10:33:36 +0300 Subject: [PATCH 19/25] Use poll_fn() --- juniper_actix/src/lib.rs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/juniper_actix/src/lib.rs b/juniper_actix/src/lib.rs index f22906b9f..de870df80 100644 --- a/juniper_actix/src/lib.rs +++ b/juniper_actix/src/lib.rs @@ -466,9 +466,9 @@ pub mod subscriptions { #[cfg(test)] mod tests { - use std::{future::Future, pin::Pin, task}; + use std::pin::Pin; - use actix_http::body::{BoxBody, MessageBody}; + use actix_http::body::MessageBody; use actix_web::{ dev::ServiceResponse, http, @@ -477,7 +477,7 @@ mod tests { web::Data, App, }; - use bytes::Bytes; + use futures::future; use juniper::{ http::tests::{run_http_test_suite, HttpIntegration, TestResponse}, tests::fixtures::starwars::schema::{Database, Query}, @@ -490,19 +490,10 @@ mod tests { type Schema = juniper::RootNode<'static, Query, EmptyMutation, EmptySubscription>; - struct BoxBodyFuture(BoxBody); - - impl Future for BoxBodyFuture { - type Output = Option>; - - fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { - Pin::new(&mut self.get_mut().0).poll_next(cx) - } - } - async fn take_response_body_string(resp: ServiceResponse) -> String { + let mut body = resp.into_body(); String::from_utf8( - BoxBodyFuture(resp.into_body()) + future::poll_fn(|cx| Pin::new(&mut body).poll_next(cx)) .await .unwrap() .unwrap() From f2dbec323d934fcf51e65d179f084eb5f8b84abb Mon Sep 17 00:00:00 2001 From: ilslv Date: Mon, 13 Dec 2021 11:29:57 +0300 Subject: [PATCH 20/25] Add boxed_field_err_fut --- docs/book/content/types/scalars.md | 4 ++-- juniper/src/macros/helper/mod.rs | 14 +++++++++++++- juniper_codegen/src/graphql_union/mod.rs | 14 +++++++------- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/docs/book/content/types/scalars.md b/docs/book/content/types/scalars.md index c6ce47134..857a1d020 100644 --- a/docs/book/content/types/scalars.md +++ b/docs/book/content/types/scalars.md @@ -128,11 +128,11 @@ where } // Define how to parse a primitive type into your custom scalar. - fn from_input_value(v: &InputValue) -> Result> { + // NOTE: The error type should implement `IntoFieldError`. + fn from_input_value(v: &InputValue) -> Result { 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))) - .map_err(Into::into) } // Define how to parse a string value. diff --git a/juniper/src/macros/helper/mod.rs b/juniper/src/macros/helper/mod.rs index 0bd728fb2..8ae7d10d7 100644 --- a/juniper/src/macros/helper/mod.rs +++ b/juniper/src/macros/helper/mod.rs @@ -2,7 +2,19 @@ pub mod subscription; -use crate::{DefaultScalarValue, DynGraphQLValue, DynGraphQLValueAsync, ScalarValue}; +use std::{fmt::Display, future::Future, pin::Pin}; + +use futures::future; + +use crate::{DefaultScalarValue, DynGraphQLValue, DynGraphQLValueAsync, FieldError, ScalarValue}; + +/// Wraps `msg` with [`Display`] implementation into opaque [`Future`] which +/// immediately resolves into [`FieldError`]. +pub fn boxed_field_err_fut( + msg: D, +) -> Pin>>>> { + Box::pin(future::err(FieldError::from(msg))) +} /// Conversion of a [`GraphQLValue`] to its [trait object][1]. /// diff --git a/juniper_codegen/src/graphql_union/mod.rs b/juniper_codegen/src/graphql_union/mod.rs index 299cb7c24..d36d546a3 100644 --- a/juniper_codegen/src/graphql_union/mod.rs +++ b/juniper_codegen/src/graphql_union/mod.rs @@ -602,13 +602,13 @@ impl Definition { let context = executor.context(); #( #variant_async_resolvers )* - return Box::pin(::juniper::futures::future::ready( - Err(::juniper::FieldError::from(format!( + return ::juniper::macros::helper::boxed_field_err_fut( + format!( "Concrete type `{}` is not handled by instance \ resolvers on GraphQL union `{}`", type_name, #name, - ))) - )); + ) + ); } } } @@ -707,9 +707,9 @@ impl VariantDefinition { return #resolving_code; } } - None => return Box::pin(::juniper::futures::future::err( - ::juniper::FieldError::from("This GraphQLType has no name") - )), + None => return ::juniper::macros::helper::field_err_boxed_fut( + "This GraphQLType has no name" + ), } } } From 91a91a1d328fecb449de2ba500be0a8fbc7e4862 Mon Sep 17 00:00:00 2001 From: ilslv Date: Mon, 13 Dec 2021 13:35:47 +0300 Subject: [PATCH 21/25] Corrections --- juniper/src/lib.rs | 1 + juniper/src/macros/helper/mod.rs | 7 ++++--- juniper_codegen/src/graphql_union/mod.rs | 14 ++++++-------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index 259936739..c63931d68 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -162,6 +162,7 @@ pub use crate::{ }, introspection::IntrospectionFormat, macros::helper::{ + field_err_boxed_fut, subscription::{ExtractTypeFromStream, IntoFieldResult}, AsDynGraphQLValue, ExtractError, }, diff --git a/juniper/src/macros/helper/mod.rs b/juniper/src/macros/helper/mod.rs index 8ae7d10d7..549ca0d4b 100644 --- a/juniper/src/macros/helper/mod.rs +++ b/juniper/src/macros/helper/mod.rs @@ -4,15 +4,16 @@ pub mod subscription; use std::{fmt::Display, future::Future, pin::Pin}; -use futures::future; +use futures::future::{self, BoxFuture}; use crate::{DefaultScalarValue, DynGraphQLValue, DynGraphQLValueAsync, FieldError, ScalarValue}; /// Wraps `msg` with [`Display`] implementation into opaque [`Future`] which /// immediately resolves into [`FieldError`]. -pub fn boxed_field_err_fut( +#[doc(hidden)] +pub fn field_err_boxed_fut<'ok, D: Display, Ok: 'ok, S: 'static>( msg: D, -) -> Pin>>>> { +) -> BoxFuture<'ok, Result>> { Box::pin(future::err(FieldError::from(msg))) } diff --git a/juniper_codegen/src/graphql_union/mod.rs b/juniper_codegen/src/graphql_union/mod.rs index d36d546a3..9d2ae57fb 100644 --- a/juniper_codegen/src/graphql_union/mod.rs +++ b/juniper_codegen/src/graphql_union/mod.rs @@ -602,13 +602,11 @@ impl Definition { let context = executor.context(); #( #variant_async_resolvers )* - return ::juniper::macros::helper::boxed_field_err_fut( - format!( - "Concrete type `{}` is not handled by instance \ - resolvers on GraphQL union `{}`", - type_name, #name, - ) - ); + return ::juniper::field_err_boxed_fut(format!( + "Concrete type `{}` is not handled by instance \ + resolvers on GraphQL union `{}`", + type_name, #name, + )); } } } @@ -707,7 +705,7 @@ impl VariantDefinition { return #resolving_code; } } - None => return ::juniper::macros::helper::field_err_boxed_fut( + None => return ::juniper::field_err_boxed_fut( "This GraphQLType has no name" ), } From dd807359c5c703fc05b12fd80912cb83e53458c8 Mon Sep 17 00:00:00 2001 From: ilslv Date: Mon, 13 Dec 2021 13:56:09 +0300 Subject: [PATCH 22/25] Corrections --- juniper/src/macros/helper/mod.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/juniper/src/macros/helper/mod.rs b/juniper/src/macros/helper/mod.rs index 549ca0d4b..2715ff140 100644 --- a/juniper/src/macros/helper/mod.rs +++ b/juniper/src/macros/helper/mod.rs @@ -2,18 +2,21 @@ pub mod subscription; -use std::{fmt::Display, future::Future, pin::Pin}; +use std::fmt::Display; use futures::future::{self, BoxFuture}; use crate::{DefaultScalarValue, DynGraphQLValue, DynGraphQLValueAsync, FieldError, ScalarValue}; -/// Wraps `msg` with [`Display`] implementation into opaque [`Future`] which -/// immediately resolves into [`FieldError`]. +/// Wraps `msg` with [`Display`] implementation into opaque [`Send`] [`Future`] +/// which immediately resolves into [`FieldError`]. #[doc(hidden)] -pub fn field_err_boxed_fut<'ok, D: Display, Ok: 'ok, S: 'static>( - msg: D, -) -> BoxFuture<'ok, Result>> { +pub fn field_err_boxed_fut<'ok, D, Ok, S>(msg: D) -> BoxFuture<'ok, Result>> +where + D: Display, + Ok: Send + 'ok, + S: Send + 'static, +{ Box::pin(future::err(FieldError::from(msg))) } From 00ff1df118080923956f4fd7b1d10f83beb3b60e Mon Sep 17 00:00:00 2001 From: ilslv Date: Tue, 14 Dec 2021 10:30:25 +0300 Subject: [PATCH 23/25] Corrections --- docs/book/content/types/scalars.md | 4 +-- .../juniper_tests/src/codegen/impl_scalar.rs | 31 +++++++++---------- .../juniper_tests/src/custom_scalar.rs | 8 ++--- juniper_codegen/src/graphql_interface/mod.rs | 4 +-- 4 files changed, 21 insertions(+), 26 deletions(-) diff --git a/docs/book/content/types/scalars.md b/docs/book/content/types/scalars.md index 857a1d020..a66468f41 100644 --- a/docs/book/content/types/scalars.md +++ b/docs/book/content/types/scalars.md @@ -131,8 +131,8 @@ where // NOTE: The error type should implement `IntoFieldError`. fn from_input_value(v: &InputValue) -> Result { 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))) + .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. diff --git a/integration_tests/juniper_tests/src/codegen/impl_scalar.rs b/integration_tests/juniper_tests/src/codegen/impl_scalar.rs index facff5698..34d571903 100644 --- a/integration_tests/juniper_tests/src/codegen/impl_scalar.rs +++ b/integration_tests/juniper_tests/src/codegen/impl_scalar.rs @@ -1,7 +1,6 @@ use juniper::{ execute, graphql_object, graphql_scalar, graphql_value, graphql_vars, DefaultScalarValue, - EmptyMutation, EmptySubscription, FieldError, Object, ParseScalarResult, ParseScalarValue, - RootNode, Value, + EmptyMutation, EmptySubscription, Object, ParseScalarResult, ParseScalarValue, RootNode, Value, }; use crate::custom_scalar::MyScalarValue; @@ -32,10 +31,10 @@ where Value::scalar(self.0) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { v.as_int_value() .map(DefaultName) - .ok_or_else(|| format!("Expected Int, found: {}", v).into()) + .ok_or_else(|| format!("Expected Int, found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -49,10 +48,10 @@ impl GraphQLScalar for OtherOrder { Value::scalar(self.0) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { v.as_int_value() .map(OtherOrder) - .ok_or_else(|| format!("Expected Int, found: {}", v).into()) + .ok_or_else(|| format!("Expected Int, found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { @@ -66,10 +65,10 @@ impl GraphQLScalar for Named { Value::scalar(self.0) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { v.as_int_value() .map(Named) - .ok_or_else(|| format!("Expected Int, found: {}", v).into()) + .ok_or_else(|| format!("Expected Int, found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { @@ -83,10 +82,10 @@ impl GraphQLScalar for ScalarDescription { Value::scalar(self.0) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { v.as_int_value() .map(ScalarDescription) - .ok_or_else(|| format!("Expected Int, found: {}", v).into()) + .ok_or_else(|| format!("Expected Int, found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { @@ -105,11 +104,11 @@ macro_rules! impl_scalar { Value::scalar(self.0.clone()) } - fn from_input_value(v: &InputValue) -> Result> { + fn from_input_value(v: &InputValue) -> Result { v.as_scalar_value() .and_then(|v| v.as_str()) .and_then(|s| Some(Self(s.to_owned()))) - .ok_or_else(|| "Expected String".into()) + .ok_or_else(|| "Expected `String`") } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -148,9 +147,7 @@ impl GraphQLScalar for WithCustomScalarValue { Value::scalar(self.0) } - fn from_input_value( - v: &InputValue, - ) -> Result> { + fn from_input_value(v: &InputValue) -> Result { v.as_int_value() .map(WithCustomScalarValue) .ok_or_else(|| format!("Expected Int, found: {}", v).into()) @@ -209,10 +206,10 @@ fn path_in_resolve_return_type() { Value::scalar(self.0) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { v.as_int_value() .map(ResolvePath) - .ok_or_else(|| format!("Expected Int, found: {}", v).into()) + .ok_or_else(|| format!("Expected Int, found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { diff --git a/integration_tests/juniper_tests/src/custom_scalar.rs b/integration_tests/juniper_tests/src/custom_scalar.rs index 1ed488a6b..3e945934a 100644 --- a/integration_tests/juniper_tests/src/custom_scalar.rs +++ b/integration_tests/juniper_tests/src/custom_scalar.rs @@ -6,8 +6,8 @@ use juniper::{ graphql_vars, parser::{ParseError, ScalarToken, Token}, serde::{de, Deserialize, Deserializer, Serialize}, - EmptyMutation, FieldError, FieldResult, GraphQLScalarValue, InputValue, Object, - ParseScalarResult, RootNode, ScalarValue, Value, Variables, + EmptyMutation, FieldResult, GraphQLScalarValue, InputValue, Object, ParseScalarResult, + RootNode, ScalarValue, Value, Variables, }; #[derive(GraphQLScalarValue, Clone, Debug, PartialEq, Serialize)] @@ -138,10 +138,10 @@ impl GraphQLScalar for i64 { Value::scalar(*self) } - fn from_input_value(v: &InputValue) -> Result { + fn from_input_value(v: &InputValue) -> Result { v.as_scalar_value::() .copied() - .ok_or_else(|| format!("Expected MyScalarValue::Long(), found: {}", v).into()) + .ok_or_else(|| format!("Expected MyScalarValue::Long(), found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, MyScalarValue> { diff --git a/juniper_codegen/src/graphql_interface/mod.rs b/juniper_codegen/src/graphql_interface/mod.rs index a509d3cf1..f6be46abf 100644 --- a/juniper_codegen/src/graphql_interface/mod.rs +++ b/juniper_codegen/src/graphql_interface/mod.rs @@ -884,9 +884,7 @@ impl Implementer { return #resolving_code; } } - None => return Box::pin(::juniper::futures::future::ready( - Err(::juniper::FieldError::from("This GraphQLType has no name")) - )), + None => return ::juniper::field_err_boxed_fut("This GraphQLType has no name"), } }) } From 982ec422eefb2f4adcb84cc6e9d20559e4d28124 Mon Sep 17 00:00:00 2001 From: ilslv Date: Tue, 14 Dec 2021 10:48:43 +0300 Subject: [PATCH 24/25] Corrections --- integration_tests/juniper_tests/src/codegen/impl_scalar.rs | 2 +- juniper/src/executor_tests/introspection/mod.rs | 2 +- juniper/src/integrations/chrono_tz.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/integration_tests/juniper_tests/src/codegen/impl_scalar.rs b/integration_tests/juniper_tests/src/codegen/impl_scalar.rs index 34d571903..70a28a768 100644 --- a/integration_tests/juniper_tests/src/codegen/impl_scalar.rs +++ b/integration_tests/juniper_tests/src/codegen/impl_scalar.rs @@ -150,7 +150,7 @@ impl GraphQLScalar for WithCustomScalarValue { fn from_input_value(v: &InputValue) -> Result { v.as_int_value() .map(WithCustomScalarValue) - .ok_or_else(|| format!("Expected Int, found: {}", v).into()) + .ok_or_else(|| format!("Expected Int, found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, MyScalarValue> { diff --git a/juniper/src/executor_tests/introspection/mod.rs b/juniper/src/executor_tests/introspection/mod.rs index c26baf1eb..07e7d668a 100644 --- a/juniper/src/executor_tests/introspection/mod.rs +++ b/juniper/src/executor_tests/introspection/mod.rs @@ -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, }; diff --git a/juniper/src/integrations/chrono_tz.rs b/juniper/src/integrations/chrono_tz.rs index 8fce3a97b..dd84aa7a8 100644 --- a/juniper/src/integrations/chrono_tz.rs +++ b/juniper/src/integrations/chrono_tz.rs @@ -46,7 +46,7 @@ mod test { use chrono_tz::Tz; - use crate::{graphql_input_value, FieldError, FromInputValue, InputValue}; + use crate::{graphql_input_value, FromInputValue, InputValue}; fn tz_input_test(raw: &'static str, expected: Result) { let input: InputValue = graphql_input_value!((raw)); From afd607ef88ab7d59404397a5373355c6ddc8aec4 Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 14 Dec 2021 17:56:44 +0100 Subject: [PATCH 25/25] Polishing --- docs/book/content/types/scalars.md | 6 +- integration_tests/async_await/src/main.rs | 12 +-- .../juniper_tests/src/codegen/impl_scalar.rs | 10 +-- .../juniper_tests/src/custom_scalar.rs | 2 +- juniper/CHANGELOG.md | 2 +- juniper/src/executor/mod.rs | 9 ++ .../introspection/input_object.rs | 2 +- juniper/src/executor_tests/variables.rs | 12 +-- juniper/src/lib.rs | 6 +- juniper/src/macros/helper/mod.rs | 45 +++++++--- juniper/src/macros/mod.rs | 1 + juniper/src/validation/input_value.rs | 3 +- juniper_codegen/src/common/field/arg.rs | 83 +++++-------------- juniper_codegen/src/common/field/mod.rs | 30 +++---- juniper_codegen/src/graphql_interface/mod.rs | 18 ++-- juniper_codegen/src/graphql_object/mod.rs | 12 ++- .../src/graphql_subscription/mod.rs | 6 +- juniper_codegen/src/graphql_union/mod.rs | 14 ++-- juniper_codegen/src/impl_scalar.rs | 2 +- juniper_codegen/src/lib.rs | 2 +- juniper_codegen/src/util/mod.rs | 4 +- 21 files changed, 142 insertions(+), 139 deletions(-) diff --git a/docs/book/content/types/scalars.md b/docs/book/content/types/scalars.md index a66468f41..23afc7fd3 100644 --- a/docs/book/content/types/scalars.md +++ b/docs/book/content/types/scalars.md @@ -114,7 +114,7 @@ The example below is used just for illustration. # } # } # -use juniper::{FieldError, Value, ParseScalarResult, ParseScalarValue}; +use juniper::{Value, ParseScalarResult, ParseScalarValue}; use date::Date; #[juniper::graphql_scalar(description = "Date")] @@ -131,8 +131,8 @@ where // NOTE: The error type should implement `IntoFieldError`. fn from_input_value(v: &InputValue) -> Result { 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))) + .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. diff --git a/integration_tests/async_await/src/main.rs b/integration_tests/async_await/src/main.rs index 076739446..2b913a7e9 100644 --- a/integration_tests/async_await/src/main.rs +++ b/integration_tests/async_await/src/main.rs @@ -81,8 +81,7 @@ mod tests { #[tokio::test] async fn async_simple() { let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new()); - let doc = r#" - query { + let doc = r#"query { fieldSync fieldAsyncPlain delayed @@ -91,8 +90,7 @@ mod tests { name delayed } - } - "#; + }"#; let vars = Default::default(); let (res, errs) = juniper::execute(doc, None, &schema, &vars, &()) @@ -122,8 +120,7 @@ mod tests { #[tokio::test] async fn async_field_validation_error() { let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new()); - let doc = r#" - query { + let doc = r#"query { nonExistentField fieldSync fieldAsyncPlain @@ -133,8 +130,7 @@ mod tests { name delayed } - } - "#; + }"#; let vars = Default::default(); let result = juniper::execute(doc, None, &schema, &vars, &()).await; diff --git a/integration_tests/juniper_tests/src/codegen/impl_scalar.rs b/integration_tests/juniper_tests/src/codegen/impl_scalar.rs index 70a28a768..1a53b8892 100644 --- a/integration_tests/juniper_tests/src/codegen/impl_scalar.rs +++ b/integration_tests/juniper_tests/src/codegen/impl_scalar.rs @@ -34,7 +34,7 @@ where fn from_input_value(v: &InputValue) -> Result { v.as_int_value() .map(DefaultName) - .ok_or_else(|| format!("Expected Int, found: {}", v)) + .ok_or_else(|| format!("Expected `Int`, found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -51,7 +51,7 @@ impl GraphQLScalar for OtherOrder { fn from_input_value(v: &InputValue) -> Result { v.as_int_value() .map(OtherOrder) - .ok_or_else(|| format!("Expected Int, found: {}", v)) + .ok_or_else(|| format!("Expected `Int`, found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { @@ -68,7 +68,7 @@ impl GraphQLScalar for Named { fn from_input_value(v: &InputValue) -> Result { v.as_int_value() .map(Named) - .ok_or_else(|| format!("Expected Int, found: {}", v)) + .ok_or_else(|| format!("Expected `Int`, found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { @@ -85,7 +85,7 @@ impl GraphQLScalar for ScalarDescription { fn from_input_value(v: &InputValue) -> Result { v.as_int_value() .map(ScalarDescription) - .ok_or_else(|| format!("Expected Int, found: {}", v)) + .ok_or_else(|| format!("Expected `Int`, found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { @@ -209,7 +209,7 @@ fn path_in_resolve_return_type() { fn from_input_value(v: &InputValue) -> Result { v.as_int_value() .map(ResolvePath) - .ok_or_else(|| format!("Expected Int, found: {}", v)) + .ok_or_else(|| format!("Expected `Int`, found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { diff --git a/integration_tests/juniper_tests/src/custom_scalar.rs b/integration_tests/juniper_tests/src/custom_scalar.rs index 3e945934a..a59b73002 100644 --- a/integration_tests/juniper_tests/src/custom_scalar.rs +++ b/integration_tests/juniper_tests/src/custom_scalar.rs @@ -141,7 +141,7 @@ impl GraphQLScalar for i64 { fn from_input_value(v: &InputValue) -> Result { v.as_scalar_value::() .copied() - .ok_or_else(|| format!("Expected MyScalarValue::Long(), found: {}", v)) + .ok_or_else(|| format!("Expected `MyScalarValue::Long`, found: {}", v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, MyScalarValue> { diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md index a67d370a1..88e58f9f7 100644 --- a/juniper/CHANGELOG.md +++ b/juniper/CHANGELOG.md @@ -8,7 +8,7 @@ - `rename = ""` attribute's argument renamed to `rename_all = ""`. ([#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 `#[graphql_scalar]` macro in `from_input_value()` return type. ([#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 diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 5a8cf32c7..553af48ea 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -230,6 +230,15 @@ impl FieldError { 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` diff --git a/juniper/src/executor_tests/introspection/input_object.rs b/juniper/src/executor_tests/introspection/input_object.rs index d433681a5..f68b97963 100644 --- a/juniper/src/executor_tests/introspection/input_object.rs +++ b/juniper/src/executor_tests/introspection/input_object.rs @@ -206,7 +206,7 @@ fn default_name_input_value() { let dv = DefaultName::from_input_value(&iv); - assert!(dv.is_ok(), "error: {:?}", dv.unwrap_err()); + assert!(dv.is_ok(), "error: {}", dv.unwrap_err().message()); let dv = dv.unwrap(); diff --git a/juniper/src/executor_tests/variables.rs b/juniper/src/executor_tests/variables.rs index 6af5ef7f5..d05727307 100644 --- a/juniper/src/executor_tests/variables.rs +++ b/juniper/src/executor_tests/variables.rs @@ -23,7 +23,7 @@ impl GraphQLScalar for TestComplexScalar { v.as_string_value() .filter(|s| *s == "SerializedValue") .map(|_| TestComplexScalar) - .ok_or_else(|| format!("Expected SerializedValue String, found: {}", v)) + .ok_or_else(|| format!(r#"Expected "SerializedValue" string, found: {}"#, v)) } fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -1097,8 +1097,9 @@ mod integers { assert_eq!( error, ValidationError(vec![RuleError::new( - "Variable \"$var\" got invalid value. Expected input scalar `Int`. \ - Got: `\"10\"`. Details: Expected `Int`, found: \"10\".", + "Variable \"$var\" got invalid value. \ + Expected input scalar `Int`. Got: `\"10\"`. \ + Details: Expected `Int`, found: \"10\".", &[SourcePosition::new(8, 0, 8)], )]), ); @@ -1156,8 +1157,9 @@ mod floats { assert_eq!( error, ValidationError(vec![RuleError::new( - "Variable \"$var\" got invalid value. Expected input scalar `Float`. \ - Got: `\"10\"`. Details: Expected `Float`, found: \"10\".", + "Variable \"$var\" got invalid value. \ + Expected input scalar `Float`. Got: `\"10\"`. \ + Details: Expected `Float`, found: \"10\".", &[SourcePosition::new(8, 0, 8)], )]), ); diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index c63931d68..a986d540b 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -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; @@ -162,9 +163,8 @@ pub use crate::{ }, introspection::IntrospectionFormat, macros::helper::{ - field_err_boxed_fut, subscription::{ExtractTypeFromStream, IntoFieldResult}, - AsDynGraphQLValue, ExtractError, + AsDynGraphQLValue, }, parser::{ParseError, Spanning}, schema::{ diff --git a/juniper/src/macros/helper/mod.rs b/juniper/src/macros/helper/mod.rs index 2715ff140..0f81018da 100644 --- a/juniper/src/macros/helper/mod.rs +++ b/juniper/src/macros/helper/mod.rs @@ -2,24 +2,12 @@ pub mod subscription; -use std::fmt::Display; +use std::fmt; use futures::future::{self, BoxFuture}; use crate::{DefaultScalarValue, DynGraphQLValue, DynGraphQLValueAsync, FieldError, ScalarValue}; -/// Wraps `msg` with [`Display`] implementation into opaque [`Send`] [`Future`] -/// which immediately resolves into [`FieldError`]. -#[doc(hidden)] -pub fn field_err_boxed_fut<'ok, D, Ok, S>(msg: D) -> BoxFuture<'ok, Result>> -where - D: Display, - Ok: Send + 'ok, - S: Send + 'static, -{ - Box::pin(future::err(FieldError::from(msg))) -} - /// Conversion of a [`GraphQLValue`] to its [trait object][1]. /// /// [`GraphQLValue`]: crate::GraphQLValue @@ -64,3 +52,34 @@ pub trait ExtractError { impl ExtractError for Result { 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>> +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(name: &str) -> FieldError { + 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>> +where + Ok: Send + 'ok, + S: Send + 'static, +{ + Box::pin(future::err(err_unnamed_type(name))) +} diff --git a/juniper/src/macros/mod.rs b/juniper/src/macros/mod.rs index 273ee814e..cecfee388 100644 --- a/juniper/src/macros/mod.rs +++ b/juniper/src/macros/mod.rs @@ -1,5 +1,6 @@ //! Declarative macros and helper definitions for procedural macros. +#[doc(hidden)] pub mod helper; #[macro_use] diff --git a/juniper/src/validation/input_value.rs b/juniper/src/validation/input_value.rs index 95f5d1350..b62381722 100644 --- a/juniper/src/validation/input_value.rs +++ b/juniper/src/validation/input_value.rs @@ -169,7 +169,8 @@ where var_pos, &path, &format!( - "Expected input of type `{}`. Got: `{}`. Details: {}", + "Expected input of type `{}`. Got: `{}`. \ + Details: {}", iom.name, value, e.message(), diff --git a/juniper_codegen/src/common/field/arg.rs b/juniper_codegen/src/common/field/arg.rs index 274f93532..0534432c2 100644 --- a/juniper_codegen/src/common/field/arg.rs +++ b/juniper_codegen/src/common/field/arg.rs @@ -347,73 +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!("Missing argument `{}`: {{}}", &name); - quote! { - args.get::<#ty>(#name) - .and_then(|opt| { - opt.map_or_else( - || { - <#ty as ::juniper::FromInputValue<#scalar>>:: - from_implicit_null() - .map_err(|e| { - let e = ::juniper::IntoFieldError::into_field_error(e); - ::juniper::FieldError::new( - format!(#err_text, e.message()), - e.extensions().clone(), - ) - }) - }, - Ok, - ) - })? - } - } - - Self::Context(_) => quote! { - ::juniper::FromContext::from(executor.context()) - }, - - Self::Executor => quote! { &executor }, - } - } - /// Returns generated code for the [`GraphQLValue::resolve_field`] method, - /// which provides the value of this [`OnMethod`] argument to be passed into - /// a trait method call. - /// - /// [`GraphQLValue::resolve_field`]: juniper::GraphQLValue::resolve_field - #[must_use] - pub(crate) fn method_resolve_field_async_tokens(&self, scalar: &scalar::Type) -> TokenStream { - match self { - Self::Regular(arg) => { - let (name, ty) = (&arg.name, &arg.ty); - let err_text = format!("Missing argument `{}`: {{}}", &name); - quote! { - match args.get::<#ty>(#name) - .and_then(|opt| { - opt.map_or_else( - || { - <#ty as ::juniper::FromInputValue<#scalar>>:: - from_implicit_null() - .map_err(|e| { - let e = ::juniper::IntoFieldError::into_field_error(e); - ::juniper::FieldError::new( - format!(#err_text, e.message()), - e.extensions().clone(), - ) - }) - }, - Ok, - ) - }) - { - Ok(ok) => ok, - Err(err) => return Box::pin(async { Err(err) }), + 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? } } } diff --git a/juniper_codegen/src/common/field/mod.rs b/juniper_codegen/src/common/field/mod.rs index fbf442ced..0437f97f3 100644 --- a/juniper_codegen/src/common/field/mod.rs +++ b/juniper_codegen/src/common/field/mod.rs @@ -274,13 +274,16 @@ impl Definition { /// [`GraphQLValue::resolve_field`]: juniper::GraphQLValue::resolve_field /// [1]: https://spec.graphql.org/June2018/#sec-Language.Fields #[must_use] - pub(crate) fn method_resolve_field_err_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! { - return Err(::juniper::FieldError::<#scalar>::from(format!( + return Err(::juniper::FieldError::from(format!( "Field `{}` not found on type `{}`", field, >::name(info) - .ok_or_else(|| ::juniper::FieldError::<#scalar>::from("No name for GraphQLType"))?, + .ok_or_else(|| ::juniper::macros::helper::err_unnamed_type(#ty_name))?, ))) } } @@ -295,16 +298,15 @@ impl Definition { pub(crate) fn method_resolve_field_err_async_field_tokens( field_names: &[&str], scalar: &scalar::Type, + ty_name: &str, ) -> TokenStream { quote! { - #( #field_names )|* => return Err(::juniper::FieldError::<#scalar>::from( - format!( - "Tried to resolve async field `{}` on type `{}` with a sync resolver", - field, - >::name(info) - .ok_or_else(|| ::juniper::FieldError::<#scalar>::from("No name for GraphQLType"))?, - ) - )), + #( #field_names )|* => return Err(::juniper::FieldError::from(format!( + "Tried to resolve async field `{}` on type `{}` with a sync resolver", + field, + >::name(info) + .ok_or_else(|| ::juniper::macros::helper::err_unnamed_type(#ty_name))?, + ))), } } @@ -413,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, } @@ -459,7 +461,7 @@ impl Definition { .as_ref() .unwrap() .iter() - .map(|arg| arg.method_resolve_field_async_tokens(scalar)); + .map(|arg| arg.method_resolve_field_tokens(scalar, true)); let rcv = self.has_receiver.then(|| { quote! { self, } @@ -508,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, } diff --git a/juniper_codegen/src/graphql_interface/mod.rs b/juniper_codegen/src/graphql_interface/mod.rs index f6be46abf..0e4ff0665 100644 --- a/juniper_codegen/src/graphql_interface/mod.rs +++ b/juniper_codegen/src/graphql_interface/mod.rs @@ -572,6 +572,7 @@ 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 @@ -585,10 +586,13 @@ impl Definition { .filter_map(|f| f.is_async.then(|| f.name.as_str())) .collect::>(); (!names.is_empty()).then(|| { - field::Definition::method_resolve_field_err_async_field_tokens(&names, scalar) + field::Definition::method_resolve_field_err_async_field_tokens( + &names, scalar, &ty_name, + ) }) }; - let no_field_err = field::Definition::method_resolve_field_err_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 @@ -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_err = field::Definition::method_resolve_field_err_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 @@ -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); @@ -848,7 +855,7 @@ impl Implementer { Some(quote! { if type_name == <#ty as ::juniper::GraphQLType<#scalar>>::name(info) - .ok_or_else(|| ::juniper::FieldError::from("This GraphQLType has no name"))? + .ok_or_else(|| ::juniper::macros::helper::err_unnamed_type(#ty_name))? { let res = #downcast; return #resolving_code; @@ -870,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); @@ -884,7 +892,7 @@ impl Implementer { return #resolving_code; } } - None => return ::juniper::field_err_boxed_fut("This GraphQLType has no name"), + None => return ::juniper::macros::helper::err_unnamed_type_fut(#ty_name), } }) } diff --git a/juniper_codegen/src/graphql_object/mod.rs b/juniper_codegen/src/graphql_object/mod.rs index dd9a1b3a6..171e00a3d 100644 --- a/juniper_codegen/src/graphql_object/mod.rs +++ b/juniper_codegen/src/graphql_object/mod.rs @@ -486,6 +486,7 @@ impl Definition { 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; @@ -500,10 +501,13 @@ impl Definition { .filter_map(|f| f.is_async.then(|| f.name.as_str())) .collect::>(); (!names.is_empty()).then(|| { - field::Definition::method_resolve_field_err_async_field_tokens(&names, scalar) + field::Definition::method_resolve_field_err_async_field_tokens( + &names, scalar, &ty_name, + ) }) }; - let no_field_err = field::Definition::method_resolve_field_err_no_field_tokens(scalar); + let no_field_err = + field::Definition::method_resolve_field_err_no_field_tokens(scalar, &ty_name); quote! { #[allow(deprecated)] @@ -553,12 +557,14 @@ impl Definition { 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_err = field::Definition::method_resolve_field_err_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)] diff --git a/juniper_codegen/src/graphql_subscription/mod.rs b/juniper_codegen/src/graphql_subscription/mod.rs index 288547485..ca64e0e0b 100644 --- a/juniper_codegen/src/graphql_subscription/mod.rs +++ b/juniper_codegen/src/graphql_subscription/mod.rs @@ -61,7 +61,7 @@ impl Definition { _: &::juniper::Executor, ) -> ::juniper::ExecutionResult<#scalar> { Err(::juniper::FieldError::from( - "Called `resolve_field` on subscription object" + "Called `resolve_field` on subscription object", )) } @@ -97,12 +97,14 @@ impl Definition { .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_err = field::Definition::method_resolve_field_err_no_field_tokens(scalar); + let no_field_err = + field::Definition::method_resolve_field_err_no_field_tokens(scalar, &ty_name); quote! { #[allow(deprecated)] diff --git a/juniper_codegen/src/graphql_union/mod.rs b/juniper_codegen/src/graphql_union/mod.rs index 9d2ae57fb..00ba528e5 100644 --- a/juniper_codegen/src/graphql_union/mod.rs +++ b/juniper_codegen/src/graphql_union/mod.rs @@ -558,7 +558,6 @@ impl Definition { ) -> ::juniper::ExecutionResult<#scalar> { let context = executor.context(); #( #variant_resolvers )* - return Err(::juniper::FieldError::from(format!( "Concrete type `{}` is not handled by instance \ resolvers on GraphQL union `{}`", @@ -601,10 +600,9 @@ impl Definition { ) -> ::juniper::BoxFuture<'b, ::juniper::ExecutionResult<#scalar>> { let context = executor.context(); #( #variant_async_resolvers )* - - return ::juniper::field_err_boxed_fut(format!( + return ::juniper::macros::helper::err_fut(format!( "Concrete type `{}` is not handled by instance \ - resolvers on GraphQL union `{}`", + resolvers on GraphQL union `{}`", type_name, #name, )); } @@ -672,12 +670,13 @@ 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) - .ok_or_else(|| ::juniper::FieldError::from("This GraphQLType has no name"))? + .ok_or_else(|| ::juniper::macros::helper::err_unnamed_type(#ty_name))? { let res = { #expr }; return #resolving_code; @@ -694,6 +693,7 @@ 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); @@ -705,9 +705,7 @@ impl VariantDefinition { return #resolving_code; } } - None => return ::juniper::field_err_boxed_fut( - "This GraphQLType has no name" - ), + None => return ::juniper::macros::helper::err_unnamed_type_fut(#ty_name), } } } diff --git a/juniper_codegen/src/impl_scalar.rs b/juniper_codegen/src/impl_scalar.rs index 7bf901c63..94c8481c3 100644 --- a/juniper_codegen/src/impl_scalar.rs +++ b/juniper_codegen/src/impl_scalar.rs @@ -309,7 +309,7 @@ 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::ExtractError>::Error; + 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 diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs index b25967a6e..14f4c1a79 100644 --- a/juniper_codegen/src/lib.rs +++ b/juniper_codegen/src/lib.rs @@ -234,7 +234,7 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream { /// fn from_input_value(value: &juniper::InputValue) -> Result { /// value.as_string_value() /// .map(|s| UserID(s.to_owned())) -/// .ok_or_else(|| format!("Expected String, found: {}", value)) +/// .ok_or_else(|| format!("Expected `String`, found: {}", value)) /// } /// /// fn from_str<'a>(value: juniper::ScalarToken<'a>) -> juniper::ParseScalarResult<'a, S> { diff --git a/juniper_codegen/src/util/mod.rs b/juniper_codegen/src/util/mod.rs index a6083e073..d8002f512 100644 --- a/juniper_codegen/src/util/mod.rs +++ b/juniper_codegen/src/util/mod.rs @@ -865,9 +865,7 @@ impl GraphQLTypeDefiniton { fn from_input_value( v: &::juniper::InputValue<#scalar> ) -> Result<#ty, Self::Error> { - match v.as_enum_value().or_else(|| { - v.as_string_value() - }) { + match v.as_enum_value().or_else(|| v.as_string_value()) { #( #from_inputs )* _ => Err(format!("Unknown enum value: {}", v)), }