Skip to content

Commit 1148c75

Browse files
davidpdrsntheduke
authored andcommitted
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 {} } ```
1 parent 91a9752 commit 1148c75

File tree

6 files changed

+133
-4
lines changed

6 files changed

+133
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#[cfg(test)]
2+
use fnv::FnvHashMap;
3+
#[cfg(test)]
4+
use juniper::Object;
5+
6+
#[cfg(test)]
7+
use juniper::{
8+
self, execute, graphql_value, DefaultScalarValue, EmptyMutation, GraphQLInputObject,
9+
GraphQLType, RootNode, Value, Variables,
10+
};
11+
12+
pub struct Query;
13+
14+
#[juniper::object]
15+
impl Query {
16+
fn r#type(r#fn: MyInputType) -> Vec<String> {
17+
unimplemented!()
18+
}
19+
}
20+
21+
#[derive(GraphQLInputObject, Debug, PartialEq)]
22+
struct MyInputType {
23+
r#trait: String,
24+
}
25+
26+
#[test]
27+
fn supports_raw_idents_in_types_and_args() {
28+
let doc = r#"
29+
{
30+
__type(name: "Query") {
31+
fields {
32+
name
33+
args {
34+
name
35+
}
36+
}
37+
}
38+
}
39+
"#;
40+
41+
let value = run_type_info_query(&doc);
42+
43+
assert_eq!(
44+
value,
45+
graphql_value!(
46+
{
47+
"__type": {
48+
"fields": [
49+
{
50+
"name": "type",
51+
"args": [
52+
{
53+
"name": "fn"
54+
}
55+
]
56+
}
57+
]
58+
}
59+
}
60+
),
61+
);
62+
}
63+
64+
#[test]
65+
fn supports_raw_idents_in_fields_of_input_types() {
66+
let doc = r#"
67+
{
68+
__type(name: "MyInputType") {
69+
inputFields {
70+
name
71+
}
72+
}
73+
}
74+
"#;
75+
76+
let value = run_type_info_query(&doc);
77+
78+
assert_eq!(
79+
value,
80+
graphql_value!(
81+
{
82+
"__type": {
83+
"inputFields": [
84+
{
85+
"name": "trait",
86+
}
87+
]
88+
}
89+
}
90+
),
91+
);
92+
}
93+
94+
#[cfg(test)]
95+
fn run_type_info_query(doc: &str) -> Value {
96+
let schema = RootNode::new(Query, EmptyMutation::<()>::new());
97+
98+
let (result, errs) =
99+
execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed");
100+
101+
assert_eq!(errs, []);
102+
103+
println!("Result: {:#?}", result);
104+
result
105+
}

integration_tests/juniper_tests/src/codegen/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ mod util;
33
mod derive_enum;
44
mod derive_input_object;
55
mod derive_object;
6+
mod derive_object_with_raw_idents;
67
mod scalar_value_transparent;

juniper/CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# master
22

3-
- No changes yet
3+
- Correctly handle raw identifiers in field and argument names.
44

55
# [[0.14.1] 2019-10-24](https://github.com/graphql-rust/juniper/releases/tag/juniper-0.14.1)
66

juniper_codegen/src/derive_input_object.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ pub fn impl_input_object(ast: &syn::DeriveInput, is_internal: bool) -> TokenStre
170170
}
171171
None => {
172172
// Note: auto camel casing when no custom name specified.
173-
crate::util::to_camel_case(&field_ident.to_string())
173+
crate::util::to_camel_case(&unraw(&field_ident.to_string()))
174174
}
175175
};
176176
let field_description = match field_attrs.description {

juniper_codegen/src/lib.rs

+18
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,24 @@ impl Query {
352352
}
353353
```
354354
355+
## Raw identifiers
356+
357+
You can use [raw identifiers](https://doc.rust-lang.org/stable/edition-guide/rust-2018/module-system/raw-identifiers.html)
358+
if you want a GrahpQL field that happens to be a Rust keyword:
359+
360+
```
361+
struct User {
362+
r#type: String,
363+
}
364+
365+
#[juniper::object]
366+
impl User {
367+
fn r#type(&self) -> &str {
368+
&self.r#type
369+
}
370+
}
371+
```
372+
355373
*/
356374
#[proc_macro_attribute]
357375
pub fn object(args: TokenStream, input: TokenStream) -> TokenStream {

juniper_codegen/src/util.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,11 @@ pub struct GraphQLTypeDefinitionField {
593593
pub resolver_code: proc_macro2::TokenStream,
594594
}
595595

596+
pub fn unraw(s: &str) -> String {
597+
use syn::ext::IdentExt;
598+
quote::format_ident!("{}", s).unraw().to_string()
599+
}
600+
596601
/// Definition of a graphql type based on information extracted
597602
/// by various macros.
598603
/// The definition can be rendered to Rust code.
@@ -635,7 +640,7 @@ impl GraphQLTypeDefiniton {
635640
let field_definitions = self.fields.iter().map(|field| {
636641
let args = field.args.iter().map(|arg| {
637642
let arg_type = &arg._type;
638-
let arg_name = &arg.name;
643+
let arg_name = unraw(&arg.name);
639644

640645
let description = match arg.description.as_ref() {
641646
Some(value) => quote!( .description( #value ) ),
@@ -675,7 +680,7 @@ impl GraphQLTypeDefiniton {
675680
None => quote!(),
676681
};
677682

678-
let field_name = &field.name;
683+
let field_name = unraw(&field.name);
679684

680685
let _type = &field._type;
681686
quote! {

0 commit comments

Comments
 (0)