2022-07-09 23:35:09 +01:00
|
|
|
use async_graphql::{
|
|
|
|
connection::{query, Connection, Edge},
|
|
|
|
Context, Enum, Error, Interface, Object, OutputType, Result,
|
|
|
|
};
|
2020-09-16 08:12:41 +08:00
|
|
|
|
2022-07-09 23:35:09 +01:00
|
|
|
use super::{StarWars, StarWarsChar};
|
2022-02-17 21:29:55 +00:00
|
|
|
|
2020-09-16 08:12:41 +08:00
|
|
|
/// One of the films in the Star Wars Trilogy
|
2022-07-09 23:35:09 +01:00
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Enum)]
|
2020-09-16 08:12:41 +08:00
|
|
|
pub enum Episode {
|
|
|
|
/// Released in 1977.
|
|
|
|
NewHope,
|
|
|
|
|
|
|
|
/// Released in 1980.
|
|
|
|
Empire,
|
|
|
|
|
|
|
|
/// Released in 1983.
|
|
|
|
Jedi,
|
|
|
|
}
|
|
|
|
|
2022-07-09 23:35:09 +01:00
|
|
|
pub struct Human<'a>(&'a StarWarsChar);
|
2020-09-16 08:12:41 +08:00
|
|
|
|
|
|
|
/// A humanoid creature in the Star Wars universe.
|
2020-09-20 06:17:56 +09:00
|
|
|
#[Object]
|
2022-07-09 23:35:09 +01:00
|
|
|
impl<'a> Human<'a> {
|
2020-09-16 08:12:41 +08:00
|
|
|
/// The id of the human.
|
2022-07-09 23:35:09 +01:00
|
|
|
async fn id(&self) -> &str {
|
|
|
|
self.0.id
|
2020-09-16 08:12:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// The name of the human.
|
2022-07-09 23:35:09 +01:00
|
|
|
async fn name(&self) -> &str {
|
|
|
|
self.0.name
|
2020-09-16 08:12:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// The friends of the human, or an empty list if they have none.
|
2022-07-09 23:35:09 +01:00
|
|
|
async fn friends<'ctx>(&self, ctx: &Context<'ctx>) -> Vec<Character<'ctx>> {
|
|
|
|
ctx.data_unchecked::<StarWars>()
|
|
|
|
.friends(self.0)
|
|
|
|
.into_iter()
|
|
|
|
.map(|ch| {
|
|
|
|
if ch.is_human {
|
|
|
|
Human(ch).into()
|
|
|
|
} else {
|
|
|
|
Droid(ch).into()
|
|
|
|
}
|
|
|
|
})
|
2020-09-16 08:12:41 +08:00
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Which movies they appear in.
|
2022-07-09 23:35:09 +01:00
|
|
|
async fn appears_in(&self) -> &[Episode] {
|
|
|
|
&self.0.appears_in
|
2020-09-16 08:12:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// The home planet of the human, or null if unknown.
|
2022-07-09 23:35:09 +01:00
|
|
|
async fn home_planet(&self) -> &Option<&str> {
|
|
|
|
&self.0.home_planet
|
2020-09-16 08:12:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-09 23:35:09 +01:00
|
|
|
pub struct Droid<'a>(&'a StarWarsChar);
|
2020-09-16 08:12:41 +08:00
|
|
|
|
|
|
|
/// A mechanical creature in the Star Wars universe.
|
2020-09-20 06:17:56 +09:00
|
|
|
#[Object]
|
2022-07-09 23:35:09 +01:00
|
|
|
impl<'a> Droid<'a> {
|
2020-09-16 08:12:41 +08:00
|
|
|
/// The id of the droid.
|
2022-07-09 23:35:09 +01:00
|
|
|
async fn id(&self) -> &str {
|
|
|
|
self.0.id
|
2020-09-16 08:12:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// The name of the droid.
|
2022-07-09 23:35:09 +01:00
|
|
|
async fn name(&self) -> &str {
|
|
|
|
self.0.name
|
2020-09-16 08:12:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// The friends of the droid, or an empty list if they have none.
|
2022-07-09 23:35:09 +01:00
|
|
|
async fn friends<'ctx>(&self, ctx: &Context<'ctx>) -> Vec<Character<'ctx>> {
|
|
|
|
ctx.data_unchecked::<StarWars>()
|
|
|
|
.friends(self.0)
|
|
|
|
.into_iter()
|
|
|
|
.map(|ch| {
|
|
|
|
if ch.is_human {
|
|
|
|
Human(ch).into()
|
|
|
|
} else {
|
|
|
|
Droid(ch).into()
|
|
|
|
}
|
|
|
|
})
|
2020-09-16 08:12:41 +08:00
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Which movies they appear in.
|
2022-07-09 23:35:09 +01:00
|
|
|
async fn appears_in(&self) -> &[Episode] {
|
|
|
|
&self.0.appears_in
|
2020-09-16 08:12:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// The primary function of the droid.
|
2022-07-09 23:35:09 +01:00
|
|
|
async fn primary_function(&self) -> &Option<&str> {
|
|
|
|
&self.0.primary_function
|
2020-09-16 08:12:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct QueryRoot;
|
|
|
|
|
2020-09-20 06:17:56 +09:00
|
|
|
#[Object]
|
2020-09-16 08:12:41 +08:00
|
|
|
impl QueryRoot {
|
2022-07-09 23:35:09 +01:00
|
|
|
async fn hero<'a>(
|
2020-09-16 08:12:41 +08:00
|
|
|
&self,
|
2022-07-09 23:35:09 +01:00
|
|
|
ctx: &Context<'a>,
|
2020-10-13 15:45:19 +08:00
|
|
|
#[graphql(
|
2020-09-16 08:12:41 +08:00
|
|
|
desc = "If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode."
|
|
|
|
)]
|
2022-07-09 23:35:09 +01:00
|
|
|
episode: Option<Episode>,
|
|
|
|
) -> Character<'a> {
|
|
|
|
let star_wars = ctx.data_unchecked::<StarWars>();
|
|
|
|
|
|
|
|
match episode {
|
|
|
|
Some(episode_name) => {
|
|
|
|
if episode_name == Episode::Empire {
|
|
|
|
Human(star_wars.chars.get(star_wars.luke).unwrap()).into()
|
|
|
|
} else {
|
|
|
|
Droid(star_wars.chars.get(star_wars.artoo).unwrap()).into()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None => Human(star_wars.chars.get(star_wars.luke).unwrap()).into(),
|
2020-09-16 08:12:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-09 23:35:09 +01:00
|
|
|
async fn human<'a>(
|
2020-09-16 08:12:41 +08:00
|
|
|
&self,
|
2022-07-09 23:35:09 +01:00
|
|
|
ctx: &Context<'a>,
|
2020-10-13 15:45:19 +08:00
|
|
|
#[graphql(desc = "id of the human")] id: String,
|
2022-07-09 23:35:09 +01:00
|
|
|
) -> Option<Human<'a>> {
|
2020-09-16 08:12:41 +08:00
|
|
|
ctx.data_unchecked::<StarWars>().human(&id).map(Human)
|
|
|
|
}
|
|
|
|
|
2022-07-09 23:35:09 +01:00
|
|
|
async fn humans<'a>(
|
2020-09-16 08:12:41 +08:00
|
|
|
&self,
|
2022-07-09 23:35:09 +01:00
|
|
|
ctx: &Context<'a>,
|
2020-09-16 08:12:41 +08:00
|
|
|
after: Option<String>,
|
|
|
|
before: Option<String>,
|
|
|
|
first: Option<i32>,
|
|
|
|
last: Option<i32>,
|
2022-07-09 23:35:09 +01:00
|
|
|
) -> Result<Connection<usize, Human<'a>>> {
|
2022-02-25 21:07:22 +00:00
|
|
|
let humans = ctx.data_unchecked::<StarWars>().humans().to_vec();
|
2022-07-09 23:35:09 +01:00
|
|
|
query_characters(after, before, first, last, &humans, Human).await
|
2020-09-16 08:12:41 +08:00
|
|
|
}
|
|
|
|
|
2022-07-09 23:35:09 +01:00
|
|
|
async fn droid<'a>(
|
2020-09-16 08:12:41 +08:00
|
|
|
&self,
|
2022-07-09 23:35:09 +01:00
|
|
|
ctx: &Context<'a>,
|
2020-10-13 15:45:19 +08:00
|
|
|
#[graphql(desc = "id of the droid")] id: String,
|
2022-07-09 23:35:09 +01:00
|
|
|
) -> Option<Droid<'a>> {
|
2020-09-16 08:12:41 +08:00
|
|
|
ctx.data_unchecked::<StarWars>().droid(&id).map(Droid)
|
|
|
|
}
|
|
|
|
|
2022-07-09 23:35:09 +01:00
|
|
|
async fn droids<'a>(
|
2020-09-16 08:12:41 +08:00
|
|
|
&self,
|
2022-07-09 23:35:09 +01:00
|
|
|
ctx: &Context<'a>,
|
2020-09-16 08:12:41 +08:00
|
|
|
after: Option<String>,
|
|
|
|
before: Option<String>,
|
|
|
|
first: Option<i32>,
|
|
|
|
last: Option<i32>,
|
2022-07-09 23:35:09 +01:00
|
|
|
) -> Result<Connection<usize, Droid<'a>>> {
|
2022-02-25 21:07:22 +00:00
|
|
|
let droids = ctx.data_unchecked::<StarWars>().droids().to_vec();
|
2022-07-09 23:35:09 +01:00
|
|
|
query_characters(after, before, first, last, &droids, Droid).await
|
2020-09-16 08:12:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-05 04:56:46 +01:00
|
|
|
#[allow(clippy::duplicated_attributes)] // false positive
|
2020-09-20 06:17:56 +09:00
|
|
|
#[derive(Interface)]
|
2020-09-16 08:12:41 +08:00
|
|
|
#[graphql(
|
2023-10-29 01:10:31 +01:00
|
|
|
field(name = "id", ty = "&str"),
|
|
|
|
field(name = "name", ty = "&str"),
|
|
|
|
field(name = "friends", ty = "Vec<Character<'ctx>>"),
|
|
|
|
field(name = "appears_in", ty = "&[Episode]")
|
2020-09-16 08:12:41 +08:00
|
|
|
)]
|
2022-07-09 23:35:09 +01:00
|
|
|
pub enum Character<'a> {
|
|
|
|
Human(Human<'a>),
|
|
|
|
Droid(Droid<'a>),
|
2020-09-16 08:12:41 +08:00
|
|
|
}
|
|
|
|
|
2022-07-09 23:35:09 +01:00
|
|
|
async fn query_characters<'a, F, T>(
|
2020-09-16 08:12:41 +08:00
|
|
|
after: Option<String>,
|
|
|
|
before: Option<String>,
|
|
|
|
first: Option<i32>,
|
|
|
|
last: Option<i32>,
|
2022-07-09 23:35:09 +01:00
|
|
|
characters: &[&'a StarWarsChar],
|
|
|
|
map_to: F,
|
|
|
|
) -> Result<Connection<usize, T>>
|
|
|
|
where
|
|
|
|
F: Fn(&'a StarWarsChar) -> T,
|
|
|
|
T: OutputType,
|
|
|
|
{
|
2020-09-16 08:12:41 +08:00
|
|
|
query(
|
|
|
|
after,
|
|
|
|
before,
|
|
|
|
first,
|
|
|
|
last,
|
|
|
|
|after, before, first, last| async move {
|
|
|
|
let mut start = 0usize;
|
|
|
|
let mut end = characters.len();
|
|
|
|
|
|
|
|
if let Some(after) = after {
|
|
|
|
if after >= characters.len() {
|
|
|
|
return Ok(Connection::new(false, false));
|
|
|
|
}
|
|
|
|
start = after + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(before) = before {
|
|
|
|
if before == 0 {
|
|
|
|
return Ok(Connection::new(false, false));
|
|
|
|
}
|
|
|
|
end = before;
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut slice = &characters[start..end];
|
|
|
|
|
|
|
|
if let Some(first) = first {
|
|
|
|
slice = &slice[..first.min(slice.len())];
|
|
|
|
end -= first.min(slice.len());
|
|
|
|
} else if let Some(last) = last {
|
|
|
|
slice = &slice[slice.len() - last.min(slice.len())..];
|
|
|
|
start = end - last.min(slice.len());
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut connection = Connection::new(start > 0, end < characters.len());
|
2022-07-09 23:35:09 +01:00
|
|
|
|
|
|
|
connection.edges.extend(
|
2020-09-16 08:12:41 +08:00
|
|
|
slice
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
2022-10-16 19:14:40 +01:00
|
|
|
.map(|(idx, item)| Edge::new(start + idx, (map_to)(item))),
|
2020-09-16 08:12:41 +08:00
|
|
|
);
|
2022-02-17 21:29:55 +00:00
|
|
|
|
2022-07-09 23:35:09 +01:00
|
|
|
Ok::<_, Error>(connection)
|
2020-09-16 08:12:41 +08:00
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
}
|