diff --git a/Cargo.toml b/Cargo.toml index 08120b82d..48508f849 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,6 @@ members = [ "juniper_hyper", "juniper_iron", "juniper_rocket", + "juniper_warp", + "juniper_warp/examples/warp_server/", ] diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml index 838c47a2d..3d9405b5e 100644 --- a/juniper/Cargo.toml +++ b/juniper/Cargo.toml @@ -28,7 +28,7 @@ expose-test-schema = [] default = [ "chrono", "url", - "uuid" + "uuid", ] [dependencies] diff --git a/juniper/src/integrations/serde.rs b/juniper/src/integrations/serde.rs index 159f18223..d7f4ded9a 100644 --- a/juniper/src/integrations/serde.rs +++ b/juniper/src/integrations/serde.rs @@ -394,11 +394,10 @@ where mod tests { use super::{ExecutionError, GraphQLError}; use ast::InputValue; - use executor::ExecutionError; use serde_json::from_str; use serde_json::to_string; - use {FieldError, Value}; use value::{DefaultScalarValue, Object}; + use {FieldError, Value}; #[test] fn int() { @@ -431,8 +430,8 @@ mod tests { #[test] fn error_extensions() { - let mut obj = Object::with_capacity(1); - obj.add_field("foo".to_string(), Value::String("bar".to_string())); + let mut obj: Object = Object::with_capacity(1); + obj.add_field("foo".to_string(), Value::scalar("bar")); assert_eq!( to_string(&ExecutionError::at_origin(FieldError::new( "foo error", diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index ee13e6e12..4900fc9b9 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -211,7 +211,6 @@ where MutationT: GraphQLType, { let document = parse_document_source(document_source, &root_node.schema)?; - { let errors = validate_input_values(variables, &document, &root_node.schema); diff --git a/juniper/src/macros/interface.rs b/juniper/src/macros/interface.rs index 020128b9b..7f79944ce 100644 --- a/juniper/src/macros/interface.rs +++ b/juniper/src/macros/interface.rs @@ -147,7 +147,7 @@ macro_rules! graphql_interface { )* let fields = &[$( registry.field_convert::<$return_ty, _, Self::Context>( - &$crate::to_camel_case(stringify!($fn_name)), + &$crate::to_camel_case(__graphql__stringify!($fn_name)), info ) $(.description($fn_description))* @@ -180,13 +180,13 @@ macro_rules! graphql_interface { executor: &$crate::Executor ) -> $crate::ExecutionResult<__juniper_insert_generic!($($scalar)+)> { $( - if field == &$crate::to_camel_case(stringify!($fn_name)) { + if field == &$crate::to_camel_case(__graphql__stringify!($fn_name)) { let result: $return_ty = (|| { $( let $arg_name: $arg_ty = args.get(&$crate::to_camel_case(stringify!($arg_name))) - .expect(concat!( + .expect(__graphql__concat!( "Argument ", - stringify!($arg_name), + __graphql__stringify!($arg_name), " missing - validation must have failed" )); )* @@ -209,7 +209,7 @@ macro_rules! graphql_interface { } )* - panic!("Field {} not found on type {}", field, $($outname)*) + __graphql__panic!("Field {} not found on type {}", field, $($outname)*) } #[allow(unused_variables)] @@ -223,7 +223,7 @@ macro_rules! graphql_interface { } )* - panic!("Concrete type not handled by instance resolvers on {}", $($outname)*); + __graphql__panic!("Concrete type not handled by instance resolvers on {}", $($outname)*); } fn resolve_into_type( @@ -241,7 +241,7 @@ macro_rules! graphql_interface { } )* - panic!("Concrete type not handled by instance resolvers on {}", $($outname)*); + __graphql__panic!("Concrete type not handled by instance resolvers on {}", $($outname)*); } } ); @@ -265,7 +265,7 @@ macro_rules! graphql_interface { }; (@$($stuff:tt)*) => { - compile_error!("Invalid syntax for `graphql_interface!`"); + __graphql__compile_error!("Invalid syntax for `graphql_interface!`"); }; ( diff --git a/juniper/src/macros/mod.rs b/juniper/src/macros/mod.rs index 772e60bbe..4526fa1f8 100644 --- a/juniper/src/macros/mod.rs +++ b/juniper/src/macros/mod.rs @@ -14,8 +14,14 @@ macro_rules! __graphql__stringify { #[doc(hidden)] #[macro_export] -macro_rules! __graphql__vec { - ($($t:tt)*) => ( vec!($($t)*) ); +macro_rules! __graphql__concat { + ($($t:tt)*) => ( concat!($($t)*) ); +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __graphql__compile_error { + ($t:expr) => ( compile_error!($t) ); } #[macro_use] diff --git a/juniper/src/macros/object.rs b/juniper/src/macros/object.rs index ec2e640e7..5261b15a5 100644 --- a/juniper/src/macros/object.rs +++ b/juniper/src/macros/object.rs @@ -321,7 +321,7 @@ macro_rules! graphql_object { { let fields = &[$( registry.field_convert::<$return_ty, _, Self::Context>( - &$crate::to_camel_case(stringify!($fn_name)), + &$crate::to_camel_case(__graphql__stringify!($fn_name)), info ) $(.description($fn_description))* @@ -360,13 +360,13 @@ macro_rules! graphql_object { executor: &$crate::Executor ) -> $crate::ExecutionResult<__juniper_insert_generic!($($scalar)+)> { $( - if field == &$crate::to_camel_case(stringify!($fn_name)) { + if field == &$crate::to_camel_case(__graphql__stringify!($fn_name)) { let result: $return_ty = (|| { $( - let $arg_name: $arg_ty = args.get(&$crate::to_camel_case(stringify!($arg_name))) - .expect(concat!( + let $arg_name: $arg_ty = args.get(&$crate::to_camel_case(__graphql__stringify!($arg_name))) + .expect(__graphql__concat!( "Argument ", - stringify!($arg_name), + __graphql__stringify!($arg_name), " missing - validation must have failed" )); )* @@ -389,7 +389,7 @@ macro_rules! graphql_object { } )* - panic!("Field {} not found on type {}", field, $($outname)*); + __graphql__panic!("Field {} not found on type {}", field, $($outname)*); } } ); @@ -472,7 +472,7 @@ macro_rules! graphql_object { }; (@$($stuff:tt)*) => { - compile_error!("Invalid syntax for `graphql_object!`"); + __graphql__compile_error!("Invalid syntax for `graphql_object!`"); }; ( diff --git a/juniper/src/macros/scalar.rs b/juniper/src/macros/scalar.rs index ec744d75f..967857cca 100644 --- a/juniper/src/macros/scalar.rs +++ b/juniper/src/macros/scalar.rs @@ -59,6 +59,7 @@ macro_rules! graphql_scalar { resolve = { self_var = $resolve_self_var:ident, body = $resolve_body: block, + return_type = $resolve_retun_type: ty, }, from_input_value = { arg = $from_input_value_arg: ident, @@ -134,12 +135,6 @@ macro_rules! graphql_scalar { } } ); - - impl $crate::FromInputValue for $name { - fn from_input_value($fiv_arg: &$crate::InputValue) -> $fiv_result { - $fiv_body - } - } }; // No more items to parse @@ -182,7 +177,7 @@ macro_rules! graphql_scalar { $(from_str = {$($from_str_body:tt)+})*, rest = ) => { - compile_error!("Missing resolve function"); + __graphql__compile_error!("Missing resolve function"); }; ( @@ -197,7 +192,7 @@ macro_rules! graphql_scalar { $(from_str = {$($from_str_body:tt)+})*, rest = ) => { - compile_error!("Missing from_input_value function"); + __graphql__compile_error!("Missing from_input_value function"); }; ( @@ -212,7 +207,7 @@ macro_rules! graphql_scalar { from_input_value = {$($from_input_value_body:tt)+}, rest = ) =>{ - compile_error!("Missing from_str function"); + __graphql__compile_error!("Missing from_str function"); }; @@ -223,7 +218,7 @@ macro_rules! graphql_scalar { $(resolve = {$($resolve_body:tt)+},)* $(from_input_value = {$($from_input_value_body:tt)+},)* $(from_str = {$($from_str_body:tt)+},)* - rest = resolve(&$selfvar:ident) -> Value $body:block $($rest:tt)* + rest = resolve(&$selfvar:ident) -> $return_ty:ty $body:block $($rest:tt)* ) => { graphql_scalar!( @parse_functions, @@ -231,6 +226,7 @@ macro_rules! graphql_scalar { resolve = { self_var = $selfvar, body = $body, + return_type = $return_ty, }, $(from_input_value = {$($from_input_value_body)+},)* $(from_str = {$($from_str_body)+},)* @@ -335,7 +331,7 @@ macro_rules! graphql_scalar { }; (@$($stuff:tt)*) => { - compile_error!("Invalid syntax for `graphql_scalar!`"); + __graphql__compile_error!("Invalid syntax for `graphql_scalar!`"); }; ($($rest:tt)*) => { diff --git a/juniper/src/macros/tests/scalar.rs b/juniper/src/macros/tests/scalar.rs index 29addd605..068936e20 100644 --- a/juniper/src/macros/tests/scalar.rs +++ b/juniper/src/macros/tests/scalar.rs @@ -112,13 +112,18 @@ where #[test] fn path_in_resolve_return_type() { struct ResolvePath(i32); + graphql_scalar!(ResolvePath { resolve(&self) -> self::Value { - Value::int(self.0) + Value::scalar(self.0) } from_input_value(v: &InputValue) -> Option { - v.as_int_value().map(|i| ResolvePath(i)) + v.as_scalar_value().map(|i: &i32| ResolvePath(*i)) + } + + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a> { + ::from_str(value) } }); } diff --git a/juniper/src/macros/union.rs b/juniper/src/macros/union.rs index abe2a3ebc..60fe627b4 100644 --- a/juniper/src/macros/union.rs +++ b/juniper/src/macros/union.rs @@ -83,7 +83,7 @@ macro_rules! graphql_union { } )* - panic!("Concrete type not handled by instance resolvers on {}", $($outname)*); + __graphql__panic!("Concrete type not handled by instance resolvers on {}", $($outname)*); } fn resolve_into_type( @@ -101,7 +101,7 @@ macro_rules! graphql_union { } )* - panic!("Concrete type not handled by instance resolvers on {}", $($outname)*); + __graphql__panic!("Concrete type not handled by instance resolvers on {}", $($outname)*); } } ); @@ -125,7 +125,7 @@ macro_rules! graphql_union { ); }; (@$($stuff:tt)*) => { - compile_error!("Invalid syntax for `graphql_union!`"); + __graphql__compile_error!("Invalid syntax for `graphql_union!`"); }; ($($rest: tt)*) => { diff --git a/juniper/src/validation/input_value.rs b/juniper/src/validation/input_value.rs index 9236e9635..219fb5738 100644 --- a/juniper/src/validation/input_value.rs +++ b/juniper/src/validation/input_value.rs @@ -7,7 +7,7 @@ use parser::SourcePosition; use schema::meta::{EnumMeta, InputObjectMeta, MetaType, ScalarMeta}; use schema::model::{SchemaType, TypeType}; use validation::RuleError; -use value::{ScalarValue, ScalarRefValue}; +use value::{ScalarRefValue, ScalarValue}; #[derive(Debug)] enum Path<'a> { @@ -23,7 +23,7 @@ pub fn validate_input_values( ) -> Vec where S: ScalarValue, - for<'b> &'b S: ScalarRefValue<'b> + for<'b> &'b S: ScalarRefValue<'b>, { let mut errs = vec![]; @@ -46,7 +46,7 @@ fn validate_var_defs( errors: &mut Vec, ) where S: ScalarValue, - for<'b> &'b S: ScalarRefValue<'b> + for<'b> &'b S: ScalarRefValue<'b>, { for &(ref name, ref def) in var_defs.iter() { let raw_type_name = def.var_type.item.innermost_name(); @@ -85,10 +85,10 @@ fn unify_value<'a, S>( meta_type: &TypeType<'a, S>, schema: &SchemaType, path: Path<'a>, -) -> Vec - where +) -> Vec +where S: ScalarValue, - for<'b> &'b S: ScalarRefValue<'b> + for<'b> &'b S: ScalarRefValue<'b>, { let mut errors: Vec = vec![]; @@ -173,7 +173,7 @@ fn unify_scalar<'a, S>( value: &InputValue, meta: &ScalarMeta, path: &Path<'a>, -) -> Vec +) -> Vec where S: fmt::Debug, { @@ -212,15 +212,26 @@ fn unify_enum<'a, S>( value: &InputValue, meta: &EnumMeta, path: &Path<'a>, -) -> Vec +) -> Vec where S: ScalarValue, - for<'b> &'b S: ScalarRefValue<'b> + for<'b> &'b S: ScalarRefValue<'b>, { let mut errors: Vec = vec![]; - match *value { - InputValue::String(ref name) | InputValue::Enum(ref name) => { + InputValue::Scalar(ref scalar) if scalar.is_type::() => { + if let Some(ref name) = <&S as Into>>::into(scalar) { + if !meta.values.iter().any(|ev| &ev.name == *name) { + errors.push(unification_error( + var_name, + var_pos, + path, + &format!(r#"Invalid value for enum "{}""#, meta.name), + )) + } + } + } + InputValue::Enum(ref name) => { if !meta.values.iter().any(|ev| &ev.name == name) { errors.push(unification_error( var_name, @@ -247,10 +258,10 @@ fn unify_input_object<'a, S>( meta: &InputObjectMeta, schema: &SchemaType, path: &Path<'a>, -) -> Vec +) -> Vec where S: ScalarValue, - for<'b> &'b S: ScalarRefValue<'b> + for<'b> &'b S: ScalarRefValue<'b>, { let mut errors: Vec = vec![]; diff --git a/juniper_codegen/src/derive_enum.rs b/juniper_codegen/src/derive_enum.rs index 769afc6be..31f1dc10f 100644 --- a/juniper_codegen/src/derive_enum.rs +++ b/juniper_codegen/src/derive_enum.rs @@ -24,7 +24,9 @@ impl EnumAttrs { // Check attributes for name and description. if let Some(items) = get_graphql_attr(&input.attrs) { for item in items { - if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "name", AttributeValidation::String) { + if let Some(AttributeValue::String(val)) = + keyed_item_value(&item, "name", AttributeValidation::String) + { if is_valid_name(&*val) { res.name = Some(val); continue; @@ -35,7 +37,9 @@ impl EnumAttrs { ); } } - if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "description", AttributeValidation::String) { + if let Some(AttributeValue::String(val)) = + keyed_item_value(&item, "description", AttributeValidation::String) + { res.description = Some(val); continue; } @@ -66,7 +70,9 @@ impl EnumVariantAttrs { // Check attributes for name and description. if let Some(items) = get_graphql_attr(&variant.attrs) { for item in items { - if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "name", AttributeValidation::String) { + if let Some(AttributeValue::String(val)) = + keyed_item_value(&item, "name", AttributeValidation::String) + { if is_valid_name(&*val) { res.name = Some(val); continue; @@ -77,11 +83,15 @@ impl EnumVariantAttrs { ); } } - if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "description", AttributeValidation::String) { + if let Some(AttributeValue::String(val)) = + keyed_item_value(&item, "description", AttributeValidation::String) + { res.description = Some(val); continue; } - if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "deprecated", AttributeValidation::String) { + if let Some(AttributeValue::String(val)) = + keyed_item_value(&item, "deprecated", AttributeValidation::String) + { res.deprecation = Some(val); continue; } @@ -150,8 +160,7 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> TokenStream { description: #descr, deprecation_reason: #depr, }, - }; - values.push(value); + }); // Build resolve match clause. resolves.extend(quote!{ diff --git a/juniper_codegen/src/derive_input_object.rs b/juniper_codegen/src/derive_input_object.rs index 74a241ade..25a3c7137 100644 --- a/juniper_codegen/src/derive_input_object.rs +++ b/juniper_codegen/src/derive_input_object.rs @@ -23,7 +23,9 @@ impl ObjAttrs { // Check attributes for name and description. if let Some(items) = get_graphql_attr(&input.attrs) { for item in items { - if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "name", AttributeValidation::String) { + if let Some(AttributeValue::String(val)) = + keyed_item_value(&item, "name", AttributeValidation::String) + { if is_valid_name(&*val) { res.name = Some(val); continue; @@ -34,12 +36,16 @@ impl ObjAttrs { ); } } - if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "description", AttributeValidation::String) { + if let Some(AttributeValue::String(val)) = + keyed_item_value(&item, "description", AttributeValidation::String) + { res.description = Some(val); continue; } - if let Some(scalar) = keyed_item_value(&item, "scalar", true) { - res.scalar = Some(Ident::from(&scalar as &str)); + if let Some(AttributeValue::String(scalar)) = + keyed_item_value(&item, "scalar", AttributeValidation::String) + { + res.scalar = Some(Ident::new(&scalar as &str, Span::call_site())); continue; } panic!(format!( @@ -70,7 +76,9 @@ impl ObjFieldAttrs { // Check attributes for name and description. if let Some(items) = get_graphql_attr(&variant.attrs) { for item in items { - if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "name", AttributeValidation::String) { + if let Some(AttributeValue::String(val)) = + keyed_item_value(&item, "name", AttributeValidation::String) + { if is_valid_name(&*val) { res.name = Some(val); continue; @@ -81,11 +89,15 @@ impl ObjFieldAttrs { ); } } - if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "description", AttributeValidation::String) { + if let Some(AttributeValue::String(val)) = + keyed_item_value(&item, "description", AttributeValidation::String) + { res.description = Some(val); continue; } - if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "default", AttributeValidation::Any) { + if let Some(AttributeValue::String(val)) = + keyed_item_value(&item, "default", AttributeValidation::Any) + { res.default_expr = Some(val); continue; } @@ -236,75 +248,83 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> TokenStream { }); } - let (where_clause, define_scalar) = if attrs.scalar.is_none() { - ( - Some(quote!{ - where __S: _juniper::ScalarValue, - for<'__b> &'__b __S: _juniper::ScalarRefValue<'__b> - }), - Some(quote!(<__S>)), - ) + let (_, ty_generics, _) = generics.split_for_impl(); + + let mut generics = generics.clone(); + + let scalar = if let Some(scalar) = attrs.scalar { + scalar } else { - (None, None) + generics.params.push(parse_quote!(__S)); + { + let where_clause = generics.where_clause.get_or_insert(parse_quote!(where)); + where_clause + .predicates + .push(parse_quote!(__S: _juniper::ScalarValue)); + where_clause + .predicates + .push(parse_quote!(for<'__b> &'__b __S: _juniper::ScalarRefValue<'__b>)); + } + Ident::new("__S", Span::call_site()) }; - let scalar = attrs.scalar.unwrap_or_else(|| Ident::from("__S")); + let (impl_generics, _, where_clause) = generics.split_for_impl(); let body = quote! { - impl#define_scalar _juniper::GraphQLType<#scalar> for #ident - #where_clause - { - type Context = (); - type TypeInfo = (); - - fn name(_: &()) -> Option<&'static str> { - Some(#name) - } - - fn meta<'r>( - _: &(), - registry: &mut _juniper::Registry<'r, #scalar> - ) -> _juniper::meta::MetaType<'r, #scalar> - where #scalar: 'r - { - let fields = &[ - #(#meta_fields)* - ]; - let meta = registry.build_input_object_type::<#ident>(&(), fields); - #meta_description - meta.into_meta() - } + impl#impl_generics _juniper::GraphQLType<#scalar> for #ident #ty_generics + #where_clause + { + type Context = (); + type TypeInfo = (); + + fn name(_: &()) -> Option<&'static str> { + Some(#name) } - impl#define_scalar _juniper::FromInputValue<#scalar> for #ident - #where_clause + fn meta<'r>( + _: &(), + registry: &mut _juniper::Registry<'r, #scalar> + ) -> _juniper::meta::MetaType<'r, #scalar> + where #scalar: 'r { - fn from_input_value(value: &_juniper::InputValue<#scalar>) -> Option<#ident> - where - for<'__b> &'__b #scalar: _juniper::ScalarRefValue<'__b> - { - if let Some(obj) = value.to_object_value() { - let item = #ident { - #(#from_inputs)* - }; - Some(item) - } - else { - None - } - } + let fields = &[ + #(#meta_fields)* + ]; + let meta = registry.build_input_object_type::<#ident>(&(), fields); + #meta_description + meta.into_meta() } + } - impl#define_scalar _juniper::ToInputValue<#scalar> for #ident - #where_clause + impl#impl_generics _juniper::FromInputValue<#scalar> for #ident #ty_generics + #where_clause + { + fn from_input_value(value: &_juniper::InputValue<#scalar>) -> Option + where + for<'__b> &'__b #scalar: _juniper::ScalarRefValue<'__b> { - fn to_input_value(&self) -> _juniper::InputValue<#scalar> { - _juniper::InputValue::object(vec![ - #(#to_inputs)* - ].into_iter().collect()) + if let Some(obj) = value.to_object_value() { + let item = #ident { + #(#from_inputs)* + }; + Some(item) + } + else { + None } } - }; + } + + impl#impl_generics _juniper::ToInputValue<#scalar> for #ident #ty_generics + #where_clause + { + fn to_input_value(&self) -> _juniper::InputValue<#scalar> { + _juniper::InputValue::object(vec![ + #(#to_inputs)* + ].into_iter().collect()) + } + } + }; let dummy_const = Ident::new( &format!("_IMPL_GRAPHQLINPUTOBJECT_FOR_{}", ident), diff --git a/juniper_codegen/src/derive_juniper_scalar_value.rs b/juniper_codegen/src/derive_juniper_scalar_value.rs index cc467117d..8ff198362 100644 --- a/juniper_codegen/src/derive_juniper_scalar_value.rs +++ b/juniper_codegen/src/derive_juniper_scalar_value.rs @@ -1,14 +1,19 @@ -use quote::Tokens; +use proc_macro2::{Span, TokenStream}; + use syn::{self, Data, Fields, Ident, Variant}; -use util::{get_juniper_attr, keyed_item_value}; +use util::{get_juniper_attr, keyed_item_value, AttributeValidation, AttributeValue}; -pub fn impl_scalar_value(ast: &syn::DeriveInput) -> Tokens { +pub fn impl_scalar_value(ast: &syn::DeriveInput) -> TokenStream { let ident = &ast.ident; let visitor = if let Some(items) = get_juniper_attr(&ast.attrs) { items.into_iter() - .filter_map(|i| keyed_item_value(&i, "visitor", true)) + .filter_map(|i| keyed_item_value(&i, "visitor", AttributeValidation::String)) .next() - .map(|t| Ident::from(&t as &str)) + .and_then(|t| if let AttributeValue::String(s) = t { + Some(Ident::new(&s, Span::call_site())) + } else { + None + }) .expect("`#[derive(ScalarValue)]` needs a annotation of the form `#[juniper(visitor = \"VisitorType\")]`") } else { panic!("`#[derive(ScalarValue)]` needs a annotation of the form `#[juniper(visitor = \"VisitorType\")]`"); @@ -30,7 +35,10 @@ pub fn impl_scalar_value(ast: &syn::DeriveInput) -> Tokens { let serialize = derive_serialize(variants.iter(), ident); let display = derive_display(variants.iter(), ident); - let dummy_const = Ident::from(format!("_IMPL_JUNIPER_SCALAR_VALUE_FOR_{}", ident).as_str()); + let dummy_const = Ident::new( + format!("_IMPL_JUNIPER_SCALAR_VALUE_FOR_{}", ident).as_str(), + Span::call_site(), + ); quote!{ const #dummy_const: () = { @@ -53,7 +61,7 @@ pub fn impl_scalar_value(ast: &syn::DeriveInput) -> Tokens { } } -fn derive_display<'a, I>(variants: I, ident: &Ident) -> Tokens +fn derive_display<'a, I>(variants: I, ident: &Ident) -> TokenStream where I: Iterator, { @@ -73,7 +81,7 @@ where } } -fn derive_serialize<'a, I>(variants: I, ident: &Ident) -> Tokens +fn derive_serialize<'a, I>(variants: I, ident: &Ident) -> TokenStream where I: Iterator, { @@ -95,7 +103,7 @@ where } } -fn derive_from_variant(variant: &Variant, ident: &Ident) -> Result { +fn derive_from_variant(variant: &Variant, ident: &Ident) -> Result { let ty = match variant.fields { Fields::Unnamed(ref u) if u.unnamed.len() == 1 => &u.unnamed.first().unwrap().value().ty, diff --git a/juniper_codegen/src/derive_object.rs b/juniper_codegen/src/derive_object.rs index e0218b580..6a43edc35 100644 --- a/juniper_codegen/src/derive_object.rs +++ b/juniper_codegen/src/derive_object.rs @@ -1,4 +1,4 @@ -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use syn; use syn::{Data, DeriveInput, Field, Fields, Ident}; @@ -21,7 +21,9 @@ impl ObjAttrs { // Check attributes for name and description. if let Some(items) = get_graphql_attr(&input.attrs) { for item in items { - if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "name", AttributeValidation::String) { + if let Some(AttributeValue::String(val)) = + keyed_item_value(&item, "name", AttributeValidation::String) + { if is_valid_name(&*val) { res.name = Some(val); continue; @@ -32,12 +34,16 @@ impl ObjAttrs { ); } } - if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "description", AttributeValidation::String) { + if let Some(AttributeValue::String(val)) = + keyed_item_value(&item, "description", AttributeValidation::String) + { res.description = Some(val); continue; } - if let Some(AttributeValue::String(scalar)) = keyed_item_value(&item, "scalar", true) { - res.scalar = Some(Ident::from(&scalar as &str)); + if let Some(AttributeValue::String(scalar)) = + keyed_item_value(&item, "scalar", AttributeValidation::String) + { + res.scalar = Some(Ident::new(&scalar as &str, Span::call_site())); continue; } panic!(format!( @@ -68,7 +74,9 @@ impl ObjFieldAttrs { // Check attributes. if let Some(items) = get_graphql_attr(&variant.attrs) { for item in items { - if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "name", AttributeValidation::String) { + if let Some(AttributeValue::String(val)) = + keyed_item_value(&item, "name", AttributeValidation::String) + { if is_valid_name(&*val) { res.name = Some(val); continue; @@ -79,11 +87,15 @@ impl ObjFieldAttrs { ); } } - if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "description", AttributeValidation::String) { + if let Some(AttributeValue::String(val)) = + keyed_item_value(&item, "description", AttributeValidation::String) + { res.description = Some(val); continue; } - if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "deprecation", AttributeValidation::String) { + if let Some(AttributeValue::String(val)) = + keyed_item_value(&item, "deprecation", AttributeValidation::String) + { res.deprecation = Some(val); continue; } @@ -175,23 +187,31 @@ pub fn impl_object(ast: &syn::DeriveInput) -> TokenStream { }); } - let (where_clause, define_scalar) = if attrs.scalar.is_none() { - ( - Some(quote!{ - where __S: juniper::ScalarValue, - for<'__b> &'__b __S: juniper::ScalarRefValue<'__b> - }), - Some(quote!(<__S>)), - ) - } else { - (None, None) - }; + let (_, ty_generics, _) = generics.split_for_impl(); + + let mut generics = generics.clone(); - let scalar = attrs.scalar.unwrap_or_else(|| Ident::from("__S")); - let dummy_const = Ident::from(format!("_IMPL_JUNIPER_SCALAR_VALUE_FOR_{}", ident).as_str()); + if attrs.scalar.is_none() { + generics.params.push(parse_quote!(__S)); + { + let where_clause = generics.where_clause.get_or_insert(parse_quote!(where)); + where_clause + .predicates + .push(parse_quote!(__S: juniper::ScalarValue)); + where_clause + .predicates + .push(parse_quote!(for<'__b> &'__b __S: juniper::ScalarRefValue<'__b>)); + } + } + + let scalar = attrs + .scalar + .unwrap_or_else(|| Ident::new("__S", Span::call_site())); + + let (impl_generics, _, where_clause) = generics.split_for_impl(); let toks = quote! { - impl#define_scalar juniper::GraphQLType<#scalar> for #ident + impl#impl_generics juniper::GraphQLType<#scalar> for #ident #ty_generics #where_clause { type Context = (); @@ -236,6 +256,12 @@ pub fn impl_object(ast: &syn::DeriveInput) -> TokenStream { } } }; + + let dummy_const = Ident::new( + format!("_IMPL_JUNIPER_SCALAR_VALUE_FOR_{}", ident).as_str(), + Span::call_site(), + ); + quote!{ const #dummy_const: () = { mod juniper { diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs index 5d3abd192..d15c97435 100644 --- a/juniper_codegen/src/lib.rs +++ b/juniper_codegen/src/lib.rs @@ -10,6 +10,7 @@ extern crate proc_macro; extern crate proc_macro2; #[macro_use] extern crate quote; +#[macro_use] extern crate syn; #[macro_use] extern crate lazy_static; diff --git a/juniper_hyper/src/lib.rs b/juniper_hyper/src/lib.rs index 1e899cf95..6a8b8edcc 100644 --- a/juniper_hyper/src/lib.rs +++ b/juniper_hyper/src/lib.rs @@ -19,7 +19,8 @@ use hyper::{header, Body, Method, Request, Response, StatusCode}; use juniper::http::{ GraphQLRequest as JuniperGraphQLRequest, GraphQLResponse as JuniperGraphQLResponse, }; -use juniper::{GraphQLType, InputValue, RootNode}; +use juniper::serde::Deserialize; +use juniper::{DefaultScalarValue, GraphQLType, InputValue, RootNode, ScalarRefValue, ScalarValue}; use serde_json::error::Error as SerdeError; use std::error::Error; use std::fmt; @@ -27,16 +28,18 @@ use std::string::FromUtf8Error; use std::sync::Arc; use url::form_urlencoded; -pub fn graphql( +pub fn graphql( pool: CpuPool, - root_node: Arc>, + root_node: Arc>, context: Arc, request: Request, ) -> Box, Error = hyper::Error> + Send> where + S: ScalarValue + Send + Sync + 'static, + for<'b> &'b S: ScalarRefValue<'b>, CtxT: Send + Sync + 'static, - QueryT: GraphQLType + Send + Sync + 'static, - MutationT: GraphQLType + Send + Sync + 'static, + QueryT: GraphQLType + Send + Sync + 'static, + MutationT: GraphQLType + Send + Sync + 'static, QueryT::TypeInfo: Send + Sync, MutationT::TypeInfo: Send + Sync, { @@ -63,7 +66,7 @@ where String::from_utf8(chunk.iter().cloned().collect::>()) .map_err(GraphQLRequestError::BodyUtf8) .and_then(|input| { - serde_json::from_str::(&input) + serde_json::from_str::>(&input) .map_err(GraphQLRequestError::BodyJSONError) }) }) @@ -89,23 +92,25 @@ fn render_error(err: GraphQLRequestError) -> Response { resp } -fn execute_request( +fn execute_request( pool: CpuPool, - root_node: Arc>, + root_node: Arc>, context: Arc, - request: GraphQLRequest, + request: GraphQLRequest, ) -> impl Future, Error = Err> where + S: ScalarValue + Send + Sync + 'static, + for<'b> &'b S: ScalarRefValue<'b>, CtxT: Send + Sync + 'static, - QueryT: GraphQLType + Send + Sync + 'static, - MutationT: GraphQLType + Send + Sync + 'static, + QueryT: GraphQLType + Send + Sync + 'static, + MutationT: GraphQLType + Send + Sync + 'static, QueryT::TypeInfo: Send + Sync, MutationT::TypeInfo: Send + Sync, Err: Send + Sync + 'static, { pool.spawn_fn(move || { future::lazy(move || { - let res = request.execute(&root_node, &context); + let res = request.execute(&*root_node, &context); let code = if res.is_ok() { StatusCode::OK } else { @@ -122,7 +127,10 @@ where }) } -fn gql_request_from_get(input: &str) -> Result { +fn gql_request_from_get(input: &str) -> Result, GraphQLRequestError> +where + S: ScalarValue, +{ let mut query = None; let operation_name = None; let mut variables = None; @@ -143,7 +151,7 @@ fn gql_request_from_get(input: &str) -> Result(&value) + match serde_json::from_str::>(&value) .map_err(GraphQLRequestError::Variables) { Ok(parsed_variables) => variables = Some(parsed_variables), @@ -185,20 +193,28 @@ fn new_html_response(code: StatusCode) -> Response { #[derive(Deserialize)] #[serde(untagged)] -enum GraphQLRequest { - Single(JuniperGraphQLRequest), - Batch(Vec), +#[serde(bound = "InputValue: Deserialize<'de>")] +enum GraphQLRequest +where + S: ScalarValue, +{ + Single(JuniperGraphQLRequest), + Batch(Vec>), } -impl GraphQLRequest { +impl GraphQLRequest +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, +{ pub fn execute<'a, CtxT, QueryT, MutationT>( &'a self, - root_node: &RootNode, + root_node: &'a RootNode, context: &CtxT, - ) -> GraphQLResponse<'a> + ) -> GraphQLResponse<'a, S> where - QueryT: GraphQLType, - MutationT: GraphQLType, + QueryT: GraphQLType, + MutationT: GraphQLType, { match self { &GraphQLRequest::Single(ref request) => { @@ -216,12 +232,18 @@ impl GraphQLRequest { #[derive(Serialize)] #[serde(untagged)] -enum GraphQLResponse<'a> { - Single(JuniperGraphQLResponse<'a>), - Batch(Vec>), +enum GraphQLResponse<'a, S = DefaultScalarValue> +where + S: ScalarValue, +{ + Single(JuniperGraphQLResponse<'a, S>), + Batch(Vec>), } -impl<'a> GraphQLResponse<'a> { +impl<'a, S> GraphQLResponse<'a, S> +where + S: ScalarValue, +{ fn is_ok(&self) -> bool { match self { &GraphQLResponse::Single(ref response) => response.is_ok(), @@ -316,9 +338,7 @@ mod tests { fn make_test_response(mut response: ReqwestResponse) -> http_tests::TestResponse { let status_code = response.status().as_u16() as i32; let body = response.text().unwrap(); - let content_type_header = response - .headers() - .get::(); + let content_type_header = response.headers().get::(); let content_type = if let Some(ct) = content_type_header { format!("{}", ct) } else { diff --git a/juniper_tests/src/codegen/derive_input_object.rs b/juniper_tests/src/codegen/derive_input_object.rs index 191d6c94b..068068fa9 100644 --- a/juniper_tests/src/codegen/derive_input_object.rs +++ b/juniper_tests/src/codegen/derive_input_object.rs @@ -6,7 +6,11 @@ use juniper::{self, FromInputValue, GraphQLType, InputValue, ToInputValue}; use juniper::DefaultScalarValue; #[derive(GraphQLInputObject, Debug, PartialEq)] -#[graphql(name = "MyInput", description = "input descr", scalar = "DefaultScalarValue")] +#[graphql( + name = "MyInput", + description = "input descr", + scalar = "DefaultScalarValue" +)] struct Input { regular_field: String, #[graphql(name = "haha", default = "33", description = "haha descr")] @@ -54,7 +58,7 @@ impl<'a> FromInputValue for &'a Fake { impl<'a> ToInputValue for &'a Fake { fn to_input_value(&self) -> InputValue { - InputValue::string("this is fake".to_string()) + InputValue::scalar("this is fake") } } @@ -65,7 +69,10 @@ impl<'a> GraphQLType for &'a Fake { fn name(_: &()) -> Option<&'static str> { None } - fn meta<'r>(_: &(), registry: &mut juniper::Registry<'r>) -> juniper::meta::MetaType<'r> { + fn meta<'r>(_: &(), registry: &mut juniper::Registry<'r>) -> juniper::meta::MetaType<'r> + where + DefaultScalarValue: 'r, + { let meta = registry.build_enum_type::<&'a Fake>( &(), &[juniper::meta::EnumValue { @@ -79,6 +86,7 @@ impl<'a> GraphQLType for &'a Fake { } #[derive(GraphQLInputObject, Debug, PartialEq)] +#[graphql(scalar = "DefaultScalarValue")] struct WithLifetime<'a> { regular_field: &'a Fake, } diff --git a/juniper_warp/Cargo.toml b/juniper_warp/Cargo.toml index fa338dd91..b8405a0ab 100644 --- a/juniper_warp/Cargo.toml +++ b/juniper_warp/Cargo.toml @@ -20,6 +20,3 @@ serde = "1.0.75" [dev-dependencies] juniper = { path = "../juniper", version = "0.10.0", features = ["expose-test-schema", "serde_json"] } percent-encoding = "1.0" - -[workspace] -members = [".", "examples/warp_server"] diff --git a/juniper_warp/src/lib.rs b/juniper_warp/src/lib.rs index 952674c92..d844be66f 100644 --- a/juniper_warp/src/lib.rs +++ b/juniper_warp/src/lib.rs @@ -55,25 +55,35 @@ extern crate percent_encoding; use futures::Future; use futures_cpupool::CpuPool; +use juniper::{DefaultScalarValue, ScalarRefValue, ScalarValue, InputValue}; +use serde::Deserialize; use std::sync::Arc; use warp::{filters::BoxedFilter, Filter}; #[derive(Debug, Deserialize, PartialEq)] #[serde(untagged)] -enum GraphQLBatchRequest { - Single(juniper::http::GraphQLRequest), - Batch(Vec), +#[serde(bound = "InputValue: Deserialize<'de>")] +enum GraphQLBatchRequest +where + S: ScalarValue, +{ + Single(juniper::http::GraphQLRequest), + Batch(Vec>), } -impl GraphQLBatchRequest { +impl GraphQLBatchRequest +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, +{ pub fn execute<'a, CtxT, QueryT, MutationT>( &'a self, - root_node: &juniper::RootNode, + root_node: &'a juniper::RootNode, context: &CtxT, - ) -> GraphQLBatchResponse<'a> + ) -> GraphQLBatchResponse<'a, S> where - QueryT: juniper::GraphQLType, - MutationT: juniper::GraphQLType, + QueryT: juniper::GraphQLType, + MutationT: juniper::GraphQLType, { match self { &GraphQLBatchRequest::Single(ref request) => { @@ -91,12 +101,18 @@ impl GraphQLBatchRequest { #[derive(Serialize)] #[serde(untagged)] -enum GraphQLBatchResponse<'a> { - Single(juniper::http::GraphQLResponse<'a>), - Batch(Vec>), +enum GraphQLBatchResponse<'a, S = DefaultScalarValue> +where + S: ScalarValue, +{ + Single(juniper::http::GraphQLResponse<'a, S>), + Batch(Vec>), } -impl<'a> GraphQLBatchResponse<'a> { +impl<'a, S> GraphQLBatchResponse<'a, S> +where + S: ScalarValue, +{ fn is_ok(&self) -> bool { match self { GraphQLBatchResponse::Single(res) => res.is_ok(), @@ -182,22 +198,24 @@ type Response = Box>, Error = warp::reject::Rejection> + Send>; /// Same as [make_graphql_filter](./fn.make_graphql_filter.html), but use the provided [CpuPool](../futures_cpupool/struct.CpuPool.html) instead. -pub fn make_graphql_filter_with_thread_pool( - schema: juniper::RootNode<'static, Query, Mutation>, +pub fn make_graphql_filter_with_thread_pool( + schema: juniper::RootNode<'static, Query, Mutation, S>, context_extractor: BoxedFilter<(Context,)>, thread_pool: futures_cpupool::CpuPool, ) -> BoxedFilter<(warp::http::Response>,)> where + S: ScalarValue + Send + Sync + 'static, + for<'b> &'b S: ScalarRefValue<'b>, Context: Send + 'static, - Query: juniper::GraphQLType + Send + Sync + 'static, - Mutation: juniper::GraphQLType + Send + Sync + 'static, + Query: juniper::GraphQLType + Send + Sync + 'static, + Mutation: juniper::GraphQLType + Send + Sync + 'static, { let schema = Arc::new(schema); let post_schema = schema.clone(); let pool_filter = warp::any().map(move || thread_pool.clone()); let handle_post_request = - move |context: Context, request: GraphQLBatchRequest, pool: CpuPool| -> Response { + move |context: Context, request: GraphQLBatchRequest, pool: CpuPool| -> Response { let schema = post_schema.clone(); Box::new( pool.spawn_fn(move || {