Skip to content

Commit 93deefb

Browse files
committed
Add union helper macro and tests
1 parent 59e5381 commit 93deefb

File tree

5 files changed

+383
-2
lines changed

5 files changed

+383
-2
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,8 @@ as well.
127127
* [ ] Sending input objects and partial input objects in variables
128128
* [ ] Sending enums in variables
129129
* [ ] General input value type checking and validation
130-
* [ ] Improve helper macros
131-
* [ ] `graphql_union!` helper completely missing
130+
* [X] Improve helper macros
131+
* [X] `graphql_union!` helper completely missing
132132
* [X] `graphql_input_object!` helper completely missing
133133
* [X] Add support for deprecating things
134134
* [X] Custom enum values and descriptions

src/macros/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
#[macro_use] mod args;
66
#[macro_use] mod field;
77
#[macro_use] mod input_object;
8+
#[macro_use] mod union;
89

910
#[cfg(test)] mod tests;

src/macros/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ mod args;
55
mod field;
66
mod object;
77
mod interface;
8+
mod union;

src/macros/tests/union.rs

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
use std::collections::HashMap;
2+
use std::marker::PhantomData;
3+
4+
use ast::InputValue;
5+
use value::Value;
6+
use schema::model::RootNode;
7+
8+
/*
9+
10+
Syntax to validate:
11+
12+
* Order of items: description, instance resolvers
13+
* Optional Generics/lifetimes
14+
* Custom name vs. default name
15+
* Optional commas between items
16+
* Optional trailing commas on instance resolvers
17+
18+
*/
19+
20+
struct Concrete;
21+
22+
enum DefaultName { Concrete(Concrete) }
23+
24+
enum WithLifetime<'a> { Int(PhantomData<&'a i64>) }
25+
enum WithGenerics<T> { Generic(T) }
26+
27+
enum DescriptionFirst { Concrete(Concrete) }
28+
enum ResolversFirst { Concrete(Concrete) }
29+
30+
enum CommasWithTrailing { Concrete(Concrete) }
31+
enum ResolversWithTrailingComma { Concrete(Concrete) }
32+
33+
struct Root;
34+
35+
graphql_object!(Concrete: () |&self| {
36+
field simple() -> i64 { 123 }
37+
});
38+
39+
graphql_union!(DefaultName: () |&self| {
40+
instance_resolvers: |&_| {
41+
Concrete => match *self { DefaultName::Concrete(ref c) => Some(c) }
42+
}
43+
});
44+
45+
graphql_union!(<'a> WithLifetime<'a>: () as "WithLifetime" |&self| {
46+
instance_resolvers: |&_| {
47+
Concrete => match *self { WithLifetime::Int(_) => Some(Concrete) }
48+
}
49+
});
50+
51+
graphql_union!(<T> WithGenerics<T>: () as "WithGenerics" |&self| {
52+
instance_resolvers: |&_| {
53+
Concrete => match *self { WithGenerics::Generic(_) => Some(Concrete) }
54+
}
55+
});
56+
57+
graphql_union!(DescriptionFirst: () |&self| {
58+
description: "A description"
59+
instance_resolvers: |&_| {
60+
Concrete => match *self { DescriptionFirst::Concrete(ref c) => Some(c) }
61+
}
62+
});
63+
64+
graphql_union!(ResolversFirst: () |&self| {
65+
instance_resolvers: |&_| {
66+
Concrete => match *self { ResolversFirst::Concrete(ref c) => Some(c) }
67+
}
68+
description: "A description"
69+
});
70+
71+
graphql_union!(CommasWithTrailing: () |&self| {
72+
instance_resolvers: |&_| {
73+
Concrete => match *self { CommasWithTrailing::Concrete(ref c) => Some(c) }
74+
},
75+
description: "A description",
76+
});
77+
78+
graphql_union!(ResolversWithTrailingComma: () |&self| {
79+
instance_resolvers: |&_| {
80+
Concrete => match *self { ResolversWithTrailingComma::Concrete(ref c) => Some(c) },
81+
}
82+
description: "A description"
83+
});
84+
85+
graphql_object!(<'a> Root: () as "Root" |&self| {
86+
field default_name() -> DefaultName { DefaultName::Concrete(Concrete) }
87+
field with_lifetime() -> WithLifetime<'a> { WithLifetime::Int(PhantomData) }
88+
field with_generics() -> WithGenerics<i64> { WithGenerics::Generic(123) }
89+
field description_first() -> DescriptionFirst { DescriptionFirst::Concrete(Concrete) }
90+
field resolvers_first() -> ResolversFirst { ResolversFirst::Concrete(Concrete) }
91+
field commas_with_trailing() -> CommasWithTrailing { CommasWithTrailing::Concrete(Concrete) }
92+
field resolvers_with_trailing_comma() -> ResolversWithTrailingComma {
93+
ResolversWithTrailingComma::Concrete(Concrete)
94+
}
95+
});
96+
97+
98+
fn run_type_info_query<F>(type_name: &str, f: F)
99+
where F: Fn(&HashMap<String, Value>, &Vec<Value>) -> ()
100+
{
101+
let doc = r#"
102+
query ($typeName: String!) {
103+
__type(name: $typeName) {
104+
name
105+
description
106+
possibleTypes {
107+
name
108+
}
109+
}
110+
}
111+
"#;
112+
let schema = RootNode::new(Root {}, ());
113+
let vars = vec![
114+
("typeName".to_owned(), InputValue::string(type_name)),
115+
].into_iter().collect();
116+
117+
let (result, errs) = ::execute(doc, None, &schema, &vars, &())
118+
.expect("Execution failed");
119+
120+
assert_eq!(errs, []);
121+
122+
println!("Result: {:?}", result);
123+
124+
let type_info = result
125+
.as_object_value().expect("Result is not an object")
126+
.get("__type").expect("__type field missing")
127+
.as_object_value().expect("__type field not an object value");
128+
129+
let possible_types = type_info
130+
.get("possibleTypes").expect("possibleTypes field missing")
131+
.as_list_value().expect("possibleTypes field not a list value");
132+
133+
f(type_info, possible_types);
134+
}
135+
136+
137+
#[test]
138+
fn introspect_default_name() {
139+
run_type_info_query("DefaultName", |union, possible_types| {
140+
assert_eq!(union.get("name"), Some(&Value::string("DefaultName")));
141+
assert_eq!(union.get("description"), Some(&Value::null()));
142+
143+
assert!(possible_types.contains(&Value::object(vec![
144+
("name", Value::string("Concrete")),
145+
].into_iter().collect())));
146+
});
147+
}
148+
149+
#[test]
150+
fn introspect_with_lifetime() {
151+
run_type_info_query("WithLifetime", |union, possible_types| {
152+
assert_eq!(union.get("name"), Some(&Value::string("WithLifetime")));
153+
assert_eq!(union.get("description"), Some(&Value::null()));
154+
155+
assert!(possible_types.contains(&Value::object(vec![
156+
("name", Value::string("Concrete")),
157+
].into_iter().collect())));
158+
});
159+
}
160+
161+
#[test]
162+
fn introspect_with_generics() {
163+
run_type_info_query("WithGenerics", |union, possible_types| {
164+
assert_eq!(union.get("name"), Some(&Value::string("WithGenerics")));
165+
assert_eq!(union.get("description"), Some(&Value::null()));
166+
167+
assert!(possible_types.contains(&Value::object(vec![
168+
("name", Value::string("Concrete")),
169+
].into_iter().collect())));
170+
});
171+
}
172+
173+
#[test]
174+
fn introspect_description_first() {
175+
run_type_info_query("DescriptionFirst", |union, possible_types| {
176+
assert_eq!(union.get("name"), Some(&Value::string("DescriptionFirst")));
177+
assert_eq!(union.get("description"), Some(&Value::string("A description")));
178+
179+
assert!(possible_types.contains(&Value::object(vec![
180+
("name", Value::string("Concrete")),
181+
].into_iter().collect())));
182+
});
183+
}
184+
185+
#[test]
186+
fn introspect_resolvers_first() {
187+
run_type_info_query("ResolversFirst", |union, possible_types| {
188+
assert_eq!(union.get("name"), Some(&Value::string("ResolversFirst")));
189+
assert_eq!(union.get("description"), Some(&Value::string("A description")));
190+
191+
assert!(possible_types.contains(&Value::object(vec![
192+
("name", Value::string("Concrete")),
193+
].into_iter().collect())));
194+
});
195+
}
196+
197+
#[test]
198+
fn introspect_commas_with_trailing() {
199+
run_type_info_query("CommasWithTrailing", |union, possible_types| {
200+
assert_eq!(union.get("name"), Some(&Value::string("CommasWithTrailing")));
201+
assert_eq!(union.get("description"), Some(&Value::string("A description")));
202+
203+
assert!(possible_types.contains(&Value::object(vec![
204+
("name", Value::string("Concrete")),
205+
].into_iter().collect())));
206+
});
207+
}
208+
209+
#[test]
210+
fn introspect_resolvers_with_trailing_comma() {
211+
run_type_info_query("ResolversWithTrailingComma", |union, possible_types| {
212+
assert_eq!(union.get("name"), Some(&Value::string("ResolversWithTrailingComma")));
213+
assert_eq!(union.get("description"), Some(&Value::string("A description")));
214+
215+
assert!(possible_types.contains(&Value::object(vec![
216+
("name", Value::string("Concrete")),
217+
].into_iter().collect())));
218+
});
219+
}

0 commit comments

Comments
 (0)