Skip to content

Commit 2d90c70

Browse files
authored
feat: add directive support to generator (#1139)
In #900 I added directives support to the QueryFragment derive. However I didn't add support to the generator. This adds that support. Fixes #1137
1 parent 345c01a commit 2d90c70

30 files changed

+524
-64
lines changed

Cargo.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,15 @@ version = "3.10.0"
4040
rust-version = "1.80"
4141

4242
[workspace.dependencies]
43-
cynic-parser = { path = "cynic-parser", version = "0.9" }
44-
cynic-parser-deser-macros = { path = "cynic-parser-deser-macros", version = "0.9" }
4543
darling = "0.20"
46-
graphql-mocks.path = "graphql-mocks"
44+
insta = { version = "1", features = ["yaml", "json"] }
4745
rstest = "0.23"
4846
syn = "2"
4947

48+
cynic-parser = { path = "cynic-parser", version = "0.9" }
49+
cynic-parser-deser-macros = { path = "cynic-parser-deser-macros", version = "0.9" }
50+
graphql-mocks.path = "graphql-mocks"
51+
5052
[profile.dev]
5153
# Disabling debug info speeds up builds a bunch,
5254
# and we don't rely on it for debugging that much.

cynic-codegen/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ ouroboros = "0.18"
2626
proc-macro2 = "1.0"
2727
quote = "1.0"
2828
strsim = "0.10.0"
29-
syn = { workspace = true , features = ["visit-mut"] }
29+
syn = { workspace = true, features = ["visit-mut"] }
3030
thiserror = "1"
3131

3232
rkyv = { version = "0.7.41", features = ["validation"], optional = true }
3333

3434
[dev-dependencies]
3535
assert_matches = "1.4.0"
36-
insta = "1.17"
36+
insta.workspace = true
3737
maplit = "1.0.2"
3838
rstest.workspace = true
3939

cynic-introspection/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ version = "3"
2828
[dev-dependencies]
2929
assert_matches = "1.4"
3030
graphql-mocks.workspace = true
31-
insta = "1.4"
31+
insta.workspace = true
3232
maplit = "1.0.2"
3333
tokio = { version = "1", features = ["macros"] }
3434
reqwest = "0.12"

cynic-parser/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ ariadne = { version = "0.4", optional = true }
3232
pretty = { version = "0.12", optional = true }
3333

3434
[dev-dependencies]
35-
insta = "1.29"
35+
insta.workspace = true
3636
similar-asserts = "1.5"
3737

3838
# Tests need the `pretty` functionality so enable it here

cynic-querygen/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ cynic-parser.workspace = true
2323

2424
[dev-dependencies]
2525
assert_matches = "1.4"
26-
insta = "1.17.1"
26+
insta.workspace = true
2727
rstest.workspace = true

cynic-querygen/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ pub enum Error {
2929
#[error("could not find type `{0}`")]
3030
UnknownType(String),
3131

32+
#[error("could not find directive `{0}`")]
33+
UnknownDirective(String),
34+
3235
#[error("expected type `{0}` to be an object")]
3336
ExpectedObject(String),
3437

cynic-querygen/src/output/query_fragment.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::fmt::Write;
33
use crate::{
44
casings::CasingExt,
55
output::{attr_output::Attributes, field::rust_field_name},
6-
query_parsing::LiteralContext,
6+
query_parsing::{Directive, LiteralContext},
77
schema::{InputType, TypeSpec},
88
};
99

@@ -56,6 +56,7 @@ pub struct OutputField<'query, 'schema> {
5656
pub field_type: RustOutputFieldType,
5757

5858
pub arguments: Vec<FieldArgument<'query, 'schema>>,
59+
pub directives: Vec<Directive<'query, 'schema>>,
5960
}
6061

6162
impl std::fmt::Display for OutputField<'_, '_> {
@@ -72,6 +73,35 @@ impl std::fmt::Display for OutputField<'_, '_> {
7273

7374
writeln!(f, "#[arguments({})]", arguments_string)?;
7475
}
76+
if !self.directives.is_empty() {
77+
let directive_string = self
78+
.directives
79+
.iter()
80+
.map(|directive| {
81+
let Directive { name, arguments } = directive;
82+
if arguments.is_empty() {
83+
return name.to_string();
84+
}
85+
let argument_strings = arguments
86+
.iter()
87+
.map(|argument| {
88+
Ok(format!(
89+
"{}: {}",
90+
argument.name,
91+
argument.value.to_literal(LiteralContext::Argument)?
92+
))
93+
})
94+
.collect::<Result<Vec<_>, Error>>()
95+
.unwrap()
96+
.join(", ");
97+
98+
format!("{name}({argument_strings})")
99+
})
100+
.collect::<Vec<_>>()
101+
.join(", ");
102+
103+
writeln!(f, "#[directives({directive_string})]")?;
104+
}
75105

76106
let name = self.name.to_snake_case();
77107
let type_spec = TypeSpec {

cynic-querygen/src/query_parsing/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ mod variables;
99

1010
use variables::VariableStructDetails;
1111

12-
pub use normalisation::Variable;
12+
pub use normalisation::{Directive, Variable};
1313
pub use value::{LiteralContext, TypedValue};
1414

1515
use cynic_parser::{executable as parser, ExecutableDocument};
@@ -121,11 +121,12 @@ fn make_query_fragment<'text>(
121121
arguments: field
122122
.arguments
123123
.iter()
124-
.map(|(name, value)| -> Result<FieldArgument, Error> {
125-
Ok(FieldArgument::new(name, value.clone()))
124+
.map(|argument| -> Result<FieldArgument, Error> {
125+
Ok(FieldArgument::new(argument.name, argument.value.clone()))
126126
})
127127
.collect::<Result<Vec<_>, _>>()
128128
.unwrap(),
129+
directives: field.directives.clone(),
129130
}
130131
})
131132
.collect(),

cynic-querygen/src/query_parsing/normalisation.rs

Lines changed: 73 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,25 @@ pub struct FieldSelection<'query, 'schema> {
5757

5858
pub schema_field: OutputField<'schema>,
5959

60-
pub arguments: Vec<(&'schema str, TypedValue<'query, 'schema>)>,
60+
pub arguments: Vec<Argument<'query, 'schema>>,
61+
62+
pub directives: Vec<Directive<'query, 'schema>>,
6163

6264
pub field: Field<'query, 'schema>,
6365
}
6466

67+
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
68+
pub struct Argument<'query, 'schema> {
69+
pub name: &'schema str,
70+
pub value: TypedValue<'query, 'schema>,
71+
}
72+
73+
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
74+
pub struct Directive<'query, 'schema> {
75+
pub name: &'schema str,
76+
pub arguments: Vec<Argument<'query, 'schema>>,
77+
}
78+
6579
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
6680
pub enum Field<'query, 'schema> {
6781
/// A composite field contains another selection set.
@@ -94,14 +108,16 @@ impl<'query, 'schema> FieldSelection<'query, 'schema> {
94108
fn new(
95109
name: &'query str,
96110
alias: Option<&'query str>,
97-
arguments: Vec<(&'schema str, TypedValue<'query, 'schema>)>,
111+
arguments: Vec<Argument<'query, 'schema>>,
112+
directives: Vec<Directive<'query, 'schema>>,
98113
schema_field: OutputField<'schema>,
99114
field: Field<'query, 'schema>,
100115
) -> FieldSelection<'query, 'schema> {
101116
FieldSelection {
102117
name,
103118
alias,
104119
arguments,
120+
directives,
105121
schema_field,
106122
field,
107123
}
@@ -278,29 +294,43 @@ impl<'a, 'docs> Normaliser<'a, 'docs> {
278294

279295
let mut arguments = Vec::new();
280296
for argument in field.arguments() {
281-
let name = argument.name();
282-
let value = argument.value();
283-
284-
let schema_arg = schema_field
285-
.arguments
286-
.iter()
287-
.find(|arg| arg.name == name)
288-
.ok_or_else(|| dbg!(Error::UnknownArgument(name.to_string())))?;
289-
290-
arguments.push((
291-
schema_arg.name,
292-
TypedValue::from_query_value(
293-
value,
294-
schema_arg.value_type.clone(),
295-
&self.variables,
296-
)?,
297-
));
297+
arguments.push(self.convert_argument(&schema_field, argument)?);
298298
}
299299

300+
let directives = field
301+
.directives()
302+
.map(|directive| {
303+
let name = directive.name();
304+
let schema_directive = self.type_index.directive(name)?;
305+
306+
let mut arguments = Vec::new();
307+
for argument in directive.arguments() {
308+
let name = argument.name();
309+
let schema_arg = schema_directive
310+
.arguments()
311+
.find(|arg| arg.name() == name)
312+
.ok_or_else(|| Error::UnknownArgument(name.to_string()))?;
313+
314+
arguments.push(Argument {
315+
name,
316+
value: TypedValue::from_query_value(
317+
argument.value(),
318+
InputFieldType::from_parser(schema_arg.ty(), self.type_index),
319+
&self.variables,
320+
)?,
321+
});
322+
}
323+
324+
dbg!(&arguments);
325+
Ok(Directive { name, arguments })
326+
})
327+
.collect::<Result<_, Error>>()?;
328+
300329
Ok(vec![Selection::Field(FieldSelection::new(
301330
field.name(),
302331
field.alias(),
303332
arguments,
333+
directives,
304334
schema_field,
305335
inner_field,
306336
))])
@@ -343,6 +373,29 @@ impl<'a, 'docs> Normaliser<'a, 'docs> {
343373
}
344374
}
345375

376+
fn convert_argument(
377+
&self,
378+
schema_field: &OutputField<'docs>,
379+
argument: parser::Argument<'docs>,
380+
) -> Result<Argument<'docs, 'docs>, Error> {
381+
let name = argument.name();
382+
let value = argument.value();
383+
let schema_arg = schema_field
384+
.arguments
385+
.iter()
386+
.find(|arg| arg.name == name)
387+
.ok_or_else(|| dbg!(Error::UnknownArgument(name.to_string())))?;
388+
389+
Ok(Argument {
390+
name: schema_arg.name,
391+
value: TypedValue::from_query_value(
392+
value,
393+
schema_arg.value_type.clone(),
394+
&self.variables,
395+
)?,
396+
})
397+
}
398+
346399
fn normalise_abstract_selection_set(
347400
&mut self,
348401
selection_set: Iter<'docs, parser::Selection<'docs>>,
@@ -489,7 +542,7 @@ impl<'schema> SelectionSet<'_, 'schema> {
489542
field
490543
.arguments
491544
.iter()
492-
.map(|(_, arg)| arg.value_type().inner_ref().clone())
545+
.map(|arg| arg.value.value_type().inner_ref().clone())
493546
.collect::<Vec<_>>()
494547
})
495548
.collect()

cynic-querygen/src/query_parsing/snapshots/cynic_querygen__query_parsing__normalisation__tests__check_fragment_spread_output.snap

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
source: cynic-querygen/src/query_parsing/normalisation.rs
3-
assertion_line: 700
4-
expression: film_selections.get(0).unwrap().selections
3+
expression: film_selections.first().unwrap().selections
4+
snapshot_kind: text
55
---
66
[
77
Field(
@@ -20,6 +20,7 @@ expression: film_selections.get(0).unwrap().selections
2020
arguments: [],
2121
},
2222
arguments: [],
23+
directives: [],
2324
field: Leaf,
2425
},
2526
),
@@ -37,6 +38,7 @@ expression: film_selections.get(0).unwrap().selections
3738
arguments: [],
3839
},
3940
arguments: [],
41+
directives: [],
4042
field: Leaf,
4143
},
4244
),

0 commit comments

Comments
 (0)