From 237e69c036fef0b2428dddf4043a88da7361d843 Mon Sep 17 00:00:00 2001 From: nWacky <38620459+nWacky@users.noreply.github.com> Date: Fri, 8 Nov 2019 12:46:48 +0300 Subject: [PATCH] Rebase `async_await` onto master (#454) * feat: Raw identifier support in object macro This commit implements raw identifier (`r#name`) support for field names (methods) and arguments in the `object` proc macro. Eg: ```rust impl T { fn r#type(r#trait: String) -> bool {} } ``` * Rebase onto master * Fix merge [skip ci] --- .../codegen/derive_object_with_raw_idents.rs | 105 ++++++++++++++++++ .../juniper_tests/src/codegen/mod.rs | 1 + juniper/CHANGELOG.md | 2 +- juniper/src/schema/schema.rs | 6 +- juniper_codegen/src/derive_input_object.rs | 2 +- juniper_codegen/src/lib.rs | 18 +++ juniper_codegen/src/util.rs | 9 +- 7 files changed, 136 insertions(+), 7 deletions(-) create mode 100644 integration_tests/juniper_tests/src/codegen/derive_object_with_raw_idents.rs diff --git a/integration_tests/juniper_tests/src/codegen/derive_object_with_raw_idents.rs b/integration_tests/juniper_tests/src/codegen/derive_object_with_raw_idents.rs new file mode 100644 index 00000000..93549ac8 --- /dev/null +++ b/integration_tests/juniper_tests/src/codegen/derive_object_with_raw_idents.rs @@ -0,0 +1,105 @@ +#[cfg(test)] +use fnv::FnvHashMap; +#[cfg(test)] +use juniper::Object; + +#[cfg(test)] +use juniper::{ + self, execute, graphql_value, DefaultScalarValue, EmptyMutation, GraphQLInputObject, + GraphQLType, RootNode, Value, Variables, +}; + +pub struct Query; + +#[juniper::object] +impl Query { + fn r#type(r#fn: MyInputType) -> Vec { + unimplemented!() + } +} + +#[derive(GraphQLInputObject, Debug, PartialEq)] +struct MyInputType { + r#trait: String, +} + +#[test] +fn supports_raw_idents_in_types_and_args() { + let doc = r#" + { + __type(name: "Query") { + fields { + name + args { + name + } + } + } + } + "#; + + let value = run_type_info_query(&doc); + + assert_eq!( + value, + graphql_value!( + { + "__type": { + "fields": [ + { + "name": "type", + "args": [ + { + "name": "fn" + } + ] + } + ] + } + } + ), + ); +} + +#[test] +fn supports_raw_idents_in_fields_of_input_types() { + let doc = r#" + { + __type(name: "MyInputType") { + inputFields { + name + } + } + } + "#; + + let value = run_type_info_query(&doc); + + assert_eq!( + value, + graphql_value!( + { + "__type": { + "inputFields": [ + { + "name": "trait", + } + ] + } + } + ), + ); +} + +#[cfg(test)] +fn run_type_info_query(doc: &str) -> Value { + let schema = RootNode::new(Query, EmptyMutation::<()>::new()); + + let (result, errs) = + execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed"); + + assert_eq!(errs, []); + + println!("Result: {:#?}", result); + result +} diff --git a/integration_tests/juniper_tests/src/codegen/mod.rs b/integration_tests/juniper_tests/src/codegen/mod.rs index 562f6761..4fcc5351 100644 --- a/integration_tests/juniper_tests/src/codegen/mod.rs +++ b/integration_tests/juniper_tests/src/codegen/mod.rs @@ -3,5 +3,6 @@ mod util; mod derive_enum; mod derive_input_object; mod derive_object; +mod derive_object_with_raw_idents; mod scalar_value_transparent; mod unions; diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md index dc6ad7ed..0020dceb 100644 --- a/juniper/CHANGELOG.md +++ b/juniper/CHANGELOG.md @@ -1,6 +1,6 @@ # master -- No changes yet +- Correctly handle raw identifiers in field and argument names. # [[0.14.1] 2019-10-24](https://github.com/graphql-rust/juniper/releases/tag/juniper-0.14.1) diff --git a/juniper/src/schema/schema.rs b/juniper/src/schema/schema.rs index 5192dc20..8cade0ba 100644 --- a/juniper/src/schema/schema.rs +++ b/juniper/src/schema/schema.rs @@ -99,12 +99,12 @@ where use futures::future::{ready, FutureExt}; match field_name { "__schema" | "__type" => { - let v = self.resolve_field(info, field_name, arguments, executor); - Box::pin(ready(v)) + self.resolve_field(info, field_name, arguments, executor) } _ => self .query_type - .resolve_field_async(info, field_name, arguments, executor), + .resolve_field_async(info, field_name, arguments, executor) + .await } } } diff --git a/juniper_codegen/src/derive_input_object.rs b/juniper_codegen/src/derive_input_object.rs index 514a4474..0f428cfd 100644 --- a/juniper_codegen/src/derive_input_object.rs +++ b/juniper_codegen/src/derive_input_object.rs @@ -170,7 +170,7 @@ pub fn impl_input_object(ast: &syn::DeriveInput, is_internal: bool) -> TokenStre } None => { // Note: auto camel casing when no custom name specified. - crate::util::to_camel_case(&field_ident.to_string()) + crate::util::to_camel_case(&unraw(&field_ident.to_string())) } }; let field_description = match field_attrs.description { diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs index cf85bfb7..cd3a9903 100644 --- a/juniper_codegen/src/lib.rs +++ b/juniper_codegen/src/lib.rs @@ -354,6 +354,24 @@ impl Query { } ``` +## Raw identifiers + +You can use [raw identifiers](https://doc.rust-lang.org/stable/edition-guide/rust-2018/module-system/raw-identifiers.html) +if you want a GrahpQL field that happens to be a Rust keyword: + +``` +struct User { + r#type: String, +} + +#[juniper::object] +impl User { + fn r#type(&self) -> &str { + &self.r#type + } +} +``` + */ #[proc_macro_attribute] pub fn object(args: TokenStream, input: TokenStream) -> TokenStream { diff --git a/juniper_codegen/src/util.rs b/juniper_codegen/src/util.rs index 40607de0..3e25274f 100644 --- a/juniper_codegen/src/util.rs +++ b/juniper_codegen/src/util.rs @@ -631,6 +631,11 @@ pub struct GraphQLTypeDefinitionField { pub is_async: bool, } +pub fn unraw(s: &str) -> String { + use syn::ext::IdentExt; + quote::format_ident!("{}", s).unraw().to_string() +} + /// Definition of a graphql type based on information extracted /// by various macros. /// The definition can be rendered to Rust code. @@ -679,7 +684,7 @@ impl GraphQLTypeDefiniton { let field_definitions = self.fields.iter().map(|field| { let args = field.args.iter().map(|arg| { let arg_type = &arg._type; - let arg_name = &arg.name; + let arg_name = unraw(&arg.name); let description = match arg.description.as_ref() { Some(value) => quote!( .description( #value ) ), @@ -719,7 +724,7 @@ impl GraphQLTypeDefiniton { None => quote!(), }; - let field_name = &field.name; + let field_name = unraw(&field.name); let _type = &field._type; quote! {