juniper/tests/fixtures/starwars/
schema.rs

1#![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)]
11/// The root query object of the schema
12impl 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)]
43/// Super basic subscription fixture
44impl 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)]
59/// A character in the Star Wars Trilogy
60pub trait Character {
61    /// The id of the character
62    fn id(&self) -> &str;
63
64    /// The name of the character
65    fn name(&self) -> Option<&str>;
66
67    /// The friends of the character
68    fn friends(&self, ctx: &Database) -> Vec<CharacterValue>;
69
70    /// Which movies they appear in
71    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/// A humanoid creature in the Star Wars universe.
109#[graphql_object(context = Database, impl = CharacterValue)]
110impl Human {
111    /// The id of the human
112    pub fn id(&self) -> &str {
113        &self.id
114    }
115
116    /// The name of the human
117    pub fn name(&self) -> Option<&str> {
118        Some(self.name.as_str())
119    }
120
121    /// The friends of the human
122    pub fn friends(&self, ctx: &Database) -> Vec<CharacterValue> {
123        ctx.get_friends(&self.friend_ids)
124    }
125
126    /// Which movies they appear in
127    pub fn appears_in(&self) -> &[Episode] {
128        &self.appears_in
129    }
130
131    /// The home planet of the human
132    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/// A mechanical creature in the Star Wars universe.
169#[graphql_object(context = Database, impl = CharacterValue)]
170impl Droid {
171    /// The id of the droid
172    pub fn id(&self) -> &str {
173        &self.id
174    }
175
176    /// The name of the droid
177    pub fn name(&self) -> Option<&str> {
178        Some(self.name.as_str())
179    }
180
181    /// The friends of the droid
182    pub fn friends(&self, ctx: &Database) -> Vec<CharacterValue> {
183        ctx.get_friends(&self.friend_ids)
184    }
185
186    /// Which movies they appear in
187    pub fn appears_in(&self) -> &[Episode] {
188        &self.appears_in
189    }
190
191    /// The primary function of the droid
192    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}