1
0
mirror of https://github.com/actix/examples synced 2025-02-13 14:02:19 +01:00

242 lines
6.3 KiB
Rust
Raw Normal View History

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.
#[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.
#[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;
#[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>,
#[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>,
#[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>,
#[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
}
}
#[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
}