juniper/tests/fixtures/starwars/
schema.rs1#![allow(missing_docs)]
2
3use std::{collections::HashMap, pin::Pin};
4
5use crate::{graphql_interface, graphql_object, graphql_subscription, Context, GraphQLEnum};
6
7#[derive(Clone, Copy, Debug)]
8pub struct Query;
9
10#[graphql_object(context = Database)]
11impl Query {
13 fn human(
14 #[graphql(context)] database: &Database,
15 #[graphql(description = "id of the human")] id: String,
16 ) -> Option<&Human> {
17 database.get_human(&id)
18 }
19
20 fn droid(
21 #[graphql(context)] database: &Database,
22 #[graphql(description = "id of the droid")] id: String,
23 ) -> Option<&Droid> {
24 database.get_droid(&id)
25 }
26
27 fn hero(
28 #[graphql(context)] database: &Database,
29 #[graphql(description = "If omitted, returns the hero of the whole saga. \
30 If provided, returns the hero of that particular episode")]
31 episode: Option<Episode>,
32 ) -> Option<CharacterValue> {
33 Some(database.get_hero(episode))
34 }
35}
36
37#[derive(Clone, Copy, Debug)]
38pub struct Subscription;
39
40type HumanStream = Pin<Box<dyn futures::Stream<Item = Human> + Send>>;
41
42#[graphql_subscription(context = Database)]
43impl Subscription {
45 async fn async_human(context: &Database) -> HumanStream {
46 let human = context.get_human("1000").unwrap().clone();
47 Box::pin(futures::stream::once(futures::future::ready(human)))
48 }
49}
50#[derive(GraphQLEnum, Clone, Copy, Debug, Eq, PartialEq)]
51pub enum Episode {
52 #[graphql(name = "NEW_HOPE")]
53 NewHope,
54 Empire,
55 Jedi,
56}
57
58#[graphql_interface(for = [Human, Droid], context = Database)]
59pub trait Character {
61 fn id(&self) -> &str;
63
64 fn name(&self) -> Option<&str>;
66
67 fn friends(&self, ctx: &Database) -> Vec<CharacterValue>;
69
70 fn appears_in(&self) -> &[Episode];
72
73 #[graphql(ignore)]
74 fn friends_ids(&self) -> &[String];
75}
76
77#[derive(Clone)]
78pub struct Human {
79 id: String,
80 name: String,
81 friend_ids: Vec<String>,
82 appears_in: Vec<Episode>,
83 #[allow(dead_code)]
84 secret_backstory: Option<String>,
85 home_planet: Option<String>,
86}
87
88impl Human {
89 pub fn new(
90 id: &str,
91 name: &str,
92 friend_ids: &[&str],
93 appears_in: &[Episode],
94 secret_backstory: Option<&str>,
95 home_planet: Option<&str>,
96 ) -> Self {
97 Self {
98 id: id.into(),
99 name: name.into(),
100 friend_ids: friend_ids.iter().copied().map(Into::into).collect(),
101 appears_in: appears_in.to_vec(),
102 secret_backstory: secret_backstory.map(Into::into),
103 home_planet: home_planet.map(Into::into),
104 }
105 }
106}
107
108#[graphql_object(context = Database, impl = CharacterValue)]
110impl Human {
111 pub fn id(&self) -> &str {
113 &self.id
114 }
115
116 pub fn name(&self) -> Option<&str> {
118 Some(self.name.as_str())
119 }
120
121 pub fn friends(&self, ctx: &Database) -> Vec<CharacterValue> {
123 ctx.get_friends(&self.friend_ids)
124 }
125
126 pub fn appears_in(&self) -> &[Episode] {
128 &self.appears_in
129 }
130
131 pub fn home_planet(&self) -> &Option<String> {
133 &self.home_planet
134 }
135}
136
137#[derive(Clone)]
138pub struct Droid {
139 id: String,
140 name: String,
141 friend_ids: Vec<String>,
142 appears_in: Vec<Episode>,
143 #[allow(dead_code)]
144 secret_backstory: Option<String>,
145 primary_function: Option<String>,
146}
147
148impl Droid {
149 pub fn new(
150 id: &str,
151 name: &str,
152 friend_ids: &[&str],
153 appears_in: &[Episode],
154 secret_backstory: Option<&str>,
155 primary_function: Option<&str>,
156 ) -> Self {
157 Self {
158 id: id.into(),
159 name: name.into(),
160 friend_ids: friend_ids.iter().copied().map(Into::into).collect(),
161 appears_in: appears_in.to_vec(),
162 secret_backstory: secret_backstory.map(Into::into),
163 primary_function: primary_function.map(Into::into),
164 }
165 }
166}
167
168#[graphql_object(context = Database, impl = CharacterValue)]
170impl Droid {
171 pub fn id(&self) -> &str {
173 &self.id
174 }
175
176 pub fn name(&self) -> Option<&str> {
178 Some(self.name.as_str())
179 }
180
181 pub fn friends(&self, ctx: &Database) -> Vec<CharacterValue> {
183 ctx.get_friends(&self.friend_ids)
184 }
185
186 pub fn appears_in(&self) -> &[Episode] {
188 &self.appears_in
189 }
190
191 pub fn primary_function(&self) -> &Option<String> {
193 &self.primary_function
194 }
195}
196
197#[derive(Clone, Default)]
198pub struct Database {
199 humans: HashMap<String, Human>,
200 droids: HashMap<String, Droid>,
201}
202
203impl Context for Database {}
204
205impl Database {
206 pub fn new() -> Database {
207 let mut humans = HashMap::new();
208 let mut droids = HashMap::new();
209
210 humans.insert(
211 "1000".into(),
212 Human::new(
213 "1000",
214 "Luke Skywalker",
215 &["1002", "1003", "2000", "2001"],
216 &[Episode::NewHope, Episode::Empire, Episode::Jedi],
217 None,
218 Some("Tatooine"),
219 ),
220 );
221
222 humans.insert(
223 "1001".into(),
224 Human::new(
225 "1001",
226 "Darth Vader",
227 &["1004"],
228 &[Episode::NewHope, Episode::Empire, Episode::Jedi],
229 None,
230 Some("Tatooine"),
231 ),
232 );
233
234 humans.insert(
235 "1002".into(),
236 Human::new(
237 "1002",
238 "Han Solo",
239 &["1000", "1003", "2001"],
240 &[Episode::NewHope, Episode::Empire, Episode::Jedi],
241 None,
242 None,
243 ),
244 );
245
246 humans.insert(
247 "1003".into(),
248 Human::new(
249 "1003",
250 "Leia Organa",
251 &["1000", "1002", "2000", "2001"],
252 &[Episode::NewHope, Episode::Empire, Episode::Jedi],
253 None,
254 Some("Alderaan"),
255 ),
256 );
257
258 humans.insert(
259 "1004".into(),
260 Human::new(
261 "1004",
262 "Wilhuff Tarkin",
263 &["1001"],
264 &[Episode::NewHope],
265 None,
266 None,
267 ),
268 );
269
270 droids.insert(
271 "2000".into(),
272 Droid::new(
273 "2000",
274 "C-3PO",
275 &["1000", "1002", "1003", "2001"],
276 &[Episode::NewHope, Episode::Empire, Episode::Jedi],
277 None,
278 Some("Protocol"),
279 ),
280 );
281
282 droids.insert(
283 "2001".into(),
284 Droid::new(
285 "2001",
286 "R2-D2",
287 &["1000", "1002", "1003"],
288 &[Episode::NewHope, Episode::Empire, Episode::Jedi],
289 None,
290 Some("Astromech"),
291 ),
292 );
293
294 Database { humans, droids }
295 }
296
297 pub fn get_hero(&self, episode: Option<Episode>) -> CharacterValue {
298 if episode == Some(Episode::Empire) {
299 self.get_human("1000").unwrap().clone().into()
300 } else {
301 self.get_droid("2001").unwrap().clone().into()
302 }
303 }
304
305 pub fn get_human(&self, id: &str) -> Option<&Human> {
306 self.humans.get(id)
307 }
308
309 pub fn get_droid(&self, id: &str) -> Option<&Droid> {
310 self.droids.get(id)
311 }
312
313 pub fn get_character(&self, id: &str) -> Option<CharacterValue> {
314 #[allow(clippy::manual_map)]
315 if let Some(h) = self.humans.get(id) {
316 Some(h.clone().into())
317 } else if let Some(d) = self.droids.get(id) {
318 Some(d.clone().into())
319 } else {
320 None
321 }
322 }
323
324 pub fn get_friends(&self, ids: &[String]) -> Vec<CharacterValue> {
325 ids.iter().flat_map(|id| self.get_character(id)).collect()
326 }
327}