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 {}
}
```
This commit is contained in:
David Pedersen 2019-11-01 16:31:08 +01:00 committed by theduke
parent 91a9752166
commit 1148c75a1d
6 changed files with 133 additions and 4 deletions

View file

@ -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<String> {
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
}

View file

@ -3,4 +3,5 @@ mod util;
mod derive_enum;
mod derive_input_object;
mod derive_object;
mod derive_object_with_raw_idents;
mod scalar_value_transparent;

View file

@ -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)

View file

@ -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 {

View file

@ -352,6 +352,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 {

View file

@ -593,6 +593,11 @@ pub struct GraphQLTypeDefinitionField {
pub resolver_code: proc_macro2::TokenStream,
}
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.
@ -635,7 +640,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 ) ),
@ -675,7 +680,7 @@ impl GraphQLTypeDefiniton {
None => quote!(),
};
let field_name = &field.name;
let field_name = unraw(&field.name);
let _type = &field._type;
quote! {