Skip to content

Commit a96a19a

Browse files
committed
Implement working operation selection
The mechanism for now is the following: - Take the operation with the same name as the struct under derive - As a fallback, take the first operation
1 parent dcd1d44 commit a96a19a

File tree

19 files changed

+267
-172
lines changed

19 files changed

+267
-172
lines changed

examples/github/src/main.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,7 @@ fn main() -> Result<(), failure::Error> {
6969
.header(reqwest::header::Authorization(format!(
7070
"bearer {}",
7171
config.github_api_token
72-
)))
73-
.json(&q)
72+
))).json(&q)
7473
.send()?;
7574

7675
let response_body: GraphQLResponse<query1::ResponseData> = res.json()?;

graphql_query_derive/src/codegen.rs

Lines changed: 49 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use constants::*;
21
use failure;
32
use fragments::GqlFragment;
43
use graphql_parser::query;
4+
use operations::Operation;
55
use proc_macro2::TokenStream;
66
use query::QueryContext;
77
use schema;
@@ -10,84 +10,16 @@ use selection::Selection;
1010
pub(crate) fn response_for_query(
1111
schema: schema::Schema,
1212
query: query::Document,
13-
selected_operation: Option<String>,
13+
selected_operation: String,
1414
) -> Result<TokenStream, failure::Error> {
1515
let mut context = QueryContext::new(schema);
1616
let mut definitions = Vec::new();
17+
let mut operations: Vec<Operation> = Vec::new();
1718

1819
for definition in query.definitions {
1920
match definition {
20-
query::Definition::Operation(query::OperationDefinition::Query(q)) => {
21-
context.root = {
22-
let definition = context
23-
.schema
24-
.query_type
25-
.clone()
26-
.and_then(|query_type| context.schema.objects.get(&query_type))
27-
.expect("query type is defined");
28-
let prefix = &q.name.expect("unnamed operation");
29-
let prefix = format!("RUST_{}", prefix);
30-
let selection = Selection::from(&q.selection_set);
31-
32-
definitions.extend(
33-
definition.field_impls_for_selection(&context, &selection, &prefix)?,
34-
);
35-
Some(definition.response_fields_for_selection(&context, &selection, &prefix)?)
36-
};
37-
38-
context.register_variables(&q.variable_definitions);
39-
}
40-
query::Definition::Operation(query::OperationDefinition::Mutation(q)) => {
41-
context.root = {
42-
let definition = context
43-
.schema
44-
.mutation_type
45-
.clone()
46-
.and_then(|mutation_type| context.schema.objects.get(&mutation_type))
47-
.expect("mutation type is defined");
48-
let prefix = &q.name.expect("unnamed operation");
49-
let prefix = format!("RUST_{}", prefix);
50-
let selection = Selection::from(&q.selection_set);
51-
52-
definitions.extend(
53-
definition.field_impls_for_selection(&context, &selection, &prefix)?,
54-
);
55-
Some(definition.response_fields_for_selection(&context, &selection, &prefix)?)
56-
};
57-
58-
context.register_variables(&q.variable_definitions);
59-
}
60-
query::Definition::Operation(query::OperationDefinition::Subscription(q)) => {
61-
context.root = {
62-
let definition = context
63-
.schema
64-
.subscription_type
65-
.clone()
66-
.and_then(|subscription_type| {
67-
context.schema.objects.get(&subscription_type)
68-
})
69-
.expect("subscription type is defined");
70-
let prefix = &q.name.expect("unnamed operation");
71-
let prefix = format!("RUST_{}", prefix);
72-
let selection = Selection::from(&q.selection_set);
73-
74-
if selection.0.len() > 1 {
75-
Err(format_err!(
76-
"{}",
77-
::constants::MULTIPLE_SUBSCRIPTION_FIELDS_ERROR
78-
))?
79-
}
80-
81-
definitions.extend(
82-
definition.field_impls_for_selection(&context, &selection, &prefix)?,
83-
);
84-
Some(definition.response_fields_for_selection(&context, &selection, &prefix)?)
85-
};
86-
87-
context.register_variables(&q.variable_definitions);
88-
}
89-
query::Definition::Operation(query::OperationDefinition::SelectionSet(_)) => {
90-
panic!(SELECTION_SET_AT_ROOT)
21+
query::Definition::Operation(op) => {
22+
operations.push(op.into());
9123
}
9224
query::Definition::Fragment(fragment) => {
9325
let query::TypeCondition::On(on) = fragment.type_condition;
@@ -103,15 +35,56 @@ pub(crate) fn response_for_query(
10335
}
10436
}
10537

38+
context.selected_operation = operations
39+
.iter()
40+
.find(|op| op.name == selected_operation)
41+
.map(|i| i.to_owned());
42+
43+
let operation = context.selected_operation.clone().unwrap_or_else(|| {
44+
operations
45+
.iter()
46+
.next()
47+
.map(|i| i.to_owned())
48+
.expect("no operation in query document")
49+
});
50+
51+
let response_data_fields = {
52+
let root_name: String = operation
53+
.root_name(&context.schema)
54+
.expect("operation type not in schema");
55+
let definition = context
56+
.schema
57+
.objects
58+
.get(&root_name)
59+
.expect("schema declaration is invalid");
60+
let prefix = format!("RUST_{}", operation.name);
61+
let selection = &operation.selection;
62+
63+
if operation.is_subscription() && selection.0.len() > 1 {
64+
Err(format_err!(
65+
"{}",
66+
::constants::MULTIPLE_SUBSCRIPTION_FIELDS_ERROR
67+
))?
68+
}
69+
70+
definitions.extend(
71+
definition
72+
.field_impls_for_selection(&context, &selection, &prefix)
73+
.unwrap(),
74+
);
75+
definition
76+
.response_fields_for_selection(&context, &selection, &prefix)
77+
.unwrap()
78+
};
79+
10680
let enum_definitions = context.schema.enums.values().map(|enm| enm.to_rust());
10781
let fragment_definitions: Result<Vec<TokenStream>, _> = context
10882
.fragments
10983
.values()
11084
.map(|fragment| fragment.to_rust(&context))
11185
.collect();
11286
let fragment_definitions = fragment_definitions?;
113-
let variables_struct = context.expand_variables();
114-
let response_data_fields = context.root.as_ref().expect("no selection defined");
87+
let variables_struct = operation.expand_variables(&context);
11588

11689
let input_object_definitions: Result<Vec<TokenStream>, _> = context
11790
.schema
@@ -149,7 +122,7 @@ pub(crate) fn response_for_query(
149122
#[derive(Debug, Serialize, Deserialize)]
150123
#[serde(rename_all = "camelCase")]
151124
pub struct ResponseData {
152-
#(#response_data_fields)*,
125+
#(#response_data_fields,)*
153126
}
154127

155128
})

graphql_query_derive/src/enums.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ impl GqlEnum {
2525
let description = &v.description;
2626
let description = description.as_ref().map(|d| quote!(#[doc = #d]));
2727
quote!(#description #name)
28-
})
29-
.collect();
28+
}).collect();
3029
let variant_names = &variant_names;
3130
let name_ident = Ident::new(&format!("{}{}", ENUMS_PREFIX, self.name), Span::call_site());
3231
let constructors: Vec<_> = self
@@ -35,8 +34,7 @@ impl GqlEnum {
3534
.map(|v| {
3635
let v = Ident::new(&v.name, Span::call_site());
3736
quote!(#name_ident::#v)
38-
})
39-
.collect();
37+
}).collect();
4038
let constructors = &constructors;
4139
let variant_str: Vec<&str> = self.variants.iter().map(|v| v.name.as_str()).collect();
4240
let variant_str = &variant_str;

graphql_query_derive/src/field_type.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub enum FieldType {
1414

1515
impl FieldType {
1616
/// Takes a field type with its name
17-
pub fn to_rust(&self, context: &QueryContext, prefix: &str) -> TokenStream {
17+
pub(crate) fn to_rust(&self, context: &QueryContext, prefix: &str) -> TokenStream {
1818
let prefix: String = if prefix.is_empty() {
1919
self.inner_name_string()
2020
} else {
@@ -24,10 +24,9 @@ impl FieldType {
2424
FieldType::Named(name) => {
2525
let name_string = name.to_string();
2626

27-
let name = if context.schema.scalars.contains_key(&name_string)
28-
|| DEFAULT_SCALARS
29-
.iter()
30-
.any(|elem| elem == &name_string.as_str())
27+
let name = if context.schema.scalars.contains_key(&name_string) || DEFAULT_SCALARS
28+
.iter()
29+
.any(|elem| elem == &name_string.as_str())
3130
{
3231
name.clone()
3332
} else if context.schema.enums.contains_key(&name_string) {

graphql_query_derive/src/fragments.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub struct GqlFragment {
1010
}
1111

1212
impl GqlFragment {
13-
pub fn to_rust(&self, context: &QueryContext) -> Result<TokenStream, ::failure::Error> {
13+
pub(crate) fn to_rust(&self, context: &QueryContext) -> Result<TokenStream, ::failure::Error> {
1414
let name_ident = Ident::new(&self.name, Span::call_site());
1515
let object = context.schema.objects.get(&self.on).expect("oh, noes");
1616
let field_impls = object.field_impls_for_selection(context, &self.selection, &self.name)?;

graphql_query_derive/src/inputs.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub struct GqlInput {
1616
}
1717

1818
impl GqlInput {
19-
pub fn to_rust(&self, context: &QueryContext) -> Result<TokenStream, failure::Error> {
19+
pub(crate) fn to_rust(&self, context: &QueryContext) -> Result<TokenStream, failure::Error> {
2020
let name = Ident::new(&self.name, Span::call_site());
2121
let mut fields: Vec<&GqlObjectField> = self.fields.values().collect();
2222
fields.sort_unstable_by(|a, b| a.name.cmp(&b.name));
@@ -52,8 +52,7 @@ impl ::std::convert::From<graphql_parser::schema::InputObjectType> for GqlInput
5252
type_: field.value_type.into(),
5353
};
5454
(name, field)
55-
})
56-
.collect(),
55+
}).collect(),
5756
}
5857
}
5958
}
@@ -80,8 +79,7 @@ impl ::std::convert::From<introspection_response::FullType> for GqlInput {
8079
.into(),
8180
};
8281
(name, field)
83-
})
84-
.collect(),
82+
}).collect(),
8583
}
8684
}
8785
}
@@ -129,7 +127,7 @@ mod tests {
129127
},
130128
),
131129
].into_iter()
132-
.collect(),
130+
.collect(),
133131
};
134132

135133
let expected: String = vec![
@@ -141,7 +139,7 @@ mod tests {
141139
"pub requirements : Option < CatRequirements > , ",
142140
"}",
143141
].into_iter()
144-
.collect();
142+
.collect();
145143

146144
let mut context = QueryContext::new_empty();
147145
context.schema.inputs.insert(cat.name.clone(), cat);

graphql_query_derive/src/interfaces.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ impl GqlInterface {
2626
}
2727
}
2828

29-
pub fn response_for_selection(
29+
pub(crate) fn response_for_selection(
3030
&self,
3131
query_context: &QueryContext,
3232
selection: &Selection,

graphql_query_derive/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ mod inputs;
2525
mod interfaces;
2626
mod introspection_response;
2727
mod objects;
28+
mod operations;
2829
mod query;
2930
mod scalars;
3031
mod schema;
@@ -107,7 +108,7 @@ fn impl_gql_query(input: &syn::DeriveInput) -> Result<TokenStream, failure::Erro
107108

108109
let module_name = Ident::new(&input.ident.to_string().to_snake_case(), Span::call_site());
109110
let struct_name = &input.ident;
110-
let schema_output = codegen::response_for_query(schema, query)?;
111+
let schema_output = codegen::response_for_query(schema, query, input.ident.to_string())?;
111112

112113
let result = quote!(
113114
pub mod #module_name {
@@ -146,8 +147,7 @@ fn extract_attr(ast: &syn::DeriveInput, attr: &str) -> Result<String, failure::E
146147
.find(|attr| {
147148
let path = &attr.path;
148149
quote!(#path).to_string() == "graphql"
149-
})
150-
.ok_or_else(|| format_err!("The graphql attribute is missing"))?;
150+
}).ok_or_else(|| format_err!("The graphql attribute is missing"))?;
151151
if let syn::Meta::List(items) = &attribute
152152
.interpret_meta()
153153
.expect("Attribute is well formatted")

graphql_query_derive/src/objects.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ impl GqlObject {
6161
item
6262
}
6363

64-
pub fn response_for_selection(
64+
pub(crate) fn response_for_selection(
6565
&self,
6666
query_context: &QueryContext,
6767
selection: &Selection,
@@ -83,7 +83,7 @@ impl GqlObject {
8383
})
8484
}
8585

86-
pub fn field_impls_for_selection(
86+
pub(crate) fn field_impls_for_selection(
8787
&self,
8888
query_context: &QueryContext,
8989
selection: &Selection,
@@ -92,7 +92,7 @@ impl GqlObject {
9292
field_impls_for_selection(&self.fields, query_context, selection, prefix)
9393
}
9494

95-
pub fn response_fields_for_selection(
95+
pub(crate) fn response_fields_for_selection(
9696
&self,
9797
query_context: &QueryContext,
9898
selection: &Selection,

0 commit comments

Comments
 (0)