-
Notifications
You must be signed in to change notification settings - Fork 177
Expand file tree
/
Copy pathgenerated_module.rs
More file actions
128 lines (108 loc) · 4.35 KB
/
Copy pathgenerated_module.rs
File metadata and controls
128 lines (108 loc) · 4.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use crate::{
codegen_options::*,
query::{BoundQuery, OperationId},
BoxError,
};
use heck::*;
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;
use std::{error::Error, fmt::Display};
#[derive(Debug)]
struct OperationNotFound {
operation_name: String,
}
impl Display for OperationNotFound {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Could not find an operation named ")?;
f.write_str(&self.operation_name)?;
f.write_str(" in the query document.")
}
}
impl Error for OperationNotFound {}
/// This struct contains the parameters necessary to generate code for a given operation.
pub(crate) struct GeneratedModule<'a> {
pub operation: &'a str,
pub query_string: &'a str,
pub resolved_query: &'a crate::query::Query,
pub schema: &'a crate::schema::Schema,
pub options: &'a crate::GraphQLClientCodegenOptions,
}
impl GeneratedModule<'_> {
/// Generate the items for the variables and the response that will go inside the module.
fn build_impls(&self) -> Result<TokenStream, BoxError> {
Ok(crate::codegen::response_for_query(
self.root()?,
self.options,
BoundQuery {
query: self.resolved_query,
schema: self.schema,
},
)?)
}
fn root(&self) -> Result<OperationId, OperationNotFound> {
let op_name = self.options.normalization().operation(self.operation);
self.resolved_query
.select_operation(&op_name, *self.options.normalization())
.map(|op| op.0)
.ok_or_else(|| OperationNotFound {
operation_name: op_name.into(),
})
}
/// Generate the module and all the code inside.
pub(crate) fn to_token_stream(&self) -> Result<TokenStream, BoxError> {
let module_name = Ident::new(&self.operation.to_snake_case(), Span::call_site());
let module_visibility = &self.options.module_visibility();
let operation_name = self.operation;
let operation_name_ident = self.options.normalization().operation(self.operation);
let operation_name_ident = Ident::new(&operation_name_ident, Span::call_site());
// Force cargo to refresh the generated code when the query file changes.
let query_include = self
.options
.query_file()
.map(|path| {
let path = path.to_str();
quote!(
const __QUERY_WORKAROUND: &str = include_str!(#path);
)
})
.unwrap_or_default();
let query_string = &self.query_string;
let impls = self.build_impls()?;
let struct_declaration: Option<_> = match self.options.mode {
CodegenMode::Cli => Some(quote!(#module_visibility struct #operation_name_ident;)),
// The struct is already present in derive mode.
CodegenMode::Derive => None,
};
Ok(quote!(
#struct_declaration
#module_visibility mod #module_name {
#![allow(dead_code)]
use std::result::Result;
pub const OPERATION_NAME: &str = #operation_name;
pub const QUERY: &str = #query_string;
#query_include
#impls
}
impl graphql_client::GraphQLQuery for #operation_name_ident {
type Variables = #module_name::Variables;
type ResponseData = #module_name::ResponseData;
fn build_query(variables: Self::Variables) -> graphql_client::QueryBody<Self::Variables> {
graphql_client::QueryBody {
variables,
query: #module_name::QUERY,
operation_name: #module_name::OPERATION_NAME,
}
}
fn build_batch_query(variables: Vec<Self::Variables>) -> Vec<graphql_client::QueryBody<Self::Variables>> {
variables.into_iter().map(|variable|
graphql_client::QueryBody {
variables: variable,
query: #module_name::QUERY,
operation_name: #module_name::OPERATION_NAME,
}
).collect()
}
}
))
}
}