Skip to content

Commit a40aadd

Browse files
authored
Merge pull request #209 from graphql-rust/recursive-inputs
Implement support for recursive input types
2 parents 708781a + e4761a5 commit a40aadd

File tree

5 files changed

+56
-1
lines changed

5 files changed

+56
-1
lines changed

graphql_client/tests/input_object_variables.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ type Email = String;
3434
#[graphql(
3535
query_path = "tests/input_object_variables/input_object_variables_query_defaults.graphql",
3636
schema_path = "tests/input_object_variables/input_object_variables_schema.graphql",
37-
response_derives = "Debug"
37+
response_derives = "Debug, PartialEq"
3838
)]
3939
pub struct DefaultInputObjectVariablesQuery;
4040

@@ -48,3 +48,29 @@ fn input_object_variables_default() {
4848

4949
assert_eq!(out, r#"{"msg":{"content":null,"to":{"category":null,"email":"[email protected]","name":null}}}"#);
5050
}
51+
52+
#[derive(GraphQLQuery)]
53+
#[graphql(
54+
query_path = "tests/input_object_variables/input_object_variables_query.graphql",
55+
schema_path = "tests/input_object_variables/input_object_variables_schema.graphql",
56+
response_derives = "Debug, PartialEq"
57+
)]
58+
pub struct RecursiveInputQuery;
59+
60+
#[test]
61+
fn recursive_input_objects_can_be_constructed() {
62+
use recursive_input_query::*;
63+
64+
RecursiveInput {
65+
head: "hello".to_string(),
66+
tail: Box::new(None),
67+
};
68+
69+
RecursiveInput {
70+
head: "hi".to_string(),
71+
tail: Box::new(Some(RecursiveInput {
72+
head: "this is crazy".to_string(),
73+
tail: Box::new(None),
74+
})),
75+
};
76+
}

graphql_client/tests/input_object_variables/input_object_variables_query.graphql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@ query VariablesQuery($msg: Message) {
33
result
44
}
55
}
6+
7+
query RecursiveInputQuery($input: RecursiveInput!) {
8+
saveRecursiveInput(recursiveInput: $input)
9+
}

graphql_client/tests/input_object_variables/input_object_variables_schema.graphql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,14 @@ input Options {
2424
pgpSignature: Boolean
2525
}
2626

27+
input RecursiveInput {
28+
head: String!
29+
tail: RecursiveInput
30+
}
31+
2732
type InputObjectVariablesQuery {
2833
echo(message: Message!, options: Options = { pgpSignature: true }): EchoResult
34+
saveRecursiveInput(recursiveInput: RecursiveInput!): Category
2935
}
3036

3137
type EchoResult {

graphql_client_codegen/src/field_type.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,17 @@ impl<'a> FieldType<'a> {
7575
_ => false,
7676
}
7777
}
78+
79+
/// A type is indirected if it is a (flat or nested) list type, optional or not.
80+
///
81+
/// We use this to determine whether a type needs to be boxed for recursion.
82+
pub fn is_indirected(&self) -> bool {
83+
match self {
84+
FieldType::Vector(_) => true,
85+
FieldType::Named(_) => false,
86+
FieldType::Optional(inner) => inner.is_indirected(),
87+
}
88+
}
7889
}
7990

8091
impl<'schema> ::std::convert::From<&'schema graphql_parser::schema::Type> for FieldType<'schema> {

graphql_client_codegen/src/inputs.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ impl<'schema> GqlInput<'schema> {
3636
fields.sort_unstable_by(|a, b| a.name.cmp(&b.name));
3737
let fields = fields.iter().map(|field| {
3838
let ty = field.type_.to_rust(&context, "");
39+
40+
// If the type is recursive, we have to box it
41+
let ty = if field.type_.is_indirected() || field.type_.inner_name_str() != self.name {
42+
ty
43+
} else {
44+
quote! { Box<#ty> }
45+
};
46+
3947
context.schema.require(&field.type_.inner_name_str());
4048
let original_name = &field.name;
4149
let snake_case_name = field.name.to_snake_case();

0 commit comments

Comments
 (0)