mirror of
https://github.com/actix/examples
synced 2025-02-13 14:02:19 +01:00
226 lines
6.4 KiB
Rust
226 lines
6.4 KiB
Rust
|
use super::StarWars;
|
||
|
use async_graphql::connection::{query, Connection, Edge, EmptyFields};
|
||
|
use async_graphql::{Context, FieldResult, GQLEnum, GQLInterface, GQLObject};
|
||
|
|
||
|
/// One of the films in the Star Wars Trilogy
|
||
|
#[derive(GQLEnum, Copy, Clone, Eq, PartialEq)]
|
||
|
pub enum Episode {
|
||
|
/// Released in 1977.
|
||
|
NewHope,
|
||
|
|
||
|
/// Released in 1980.
|
||
|
Empire,
|
||
|
|
||
|
/// Released in 1983.
|
||
|
Jedi,
|
||
|
}
|
||
|
|
||
|
pub struct Human(usize);
|
||
|
|
||
|
/// A humanoid creature in the Star Wars universe.
|
||
|
#[GQLObject]
|
||
|
impl Human {
|
||
|
/// The id of the human.
|
||
|
async fn id(&self, ctx: &Context<'_>) -> &str {
|
||
|
ctx.data_unchecked::<StarWars>().chars[self.0].id
|
||
|
}
|
||
|
|
||
|
/// The name of the human.
|
||
|
async fn name(&self, ctx: &Context<'_>) -> &str {
|
||
|
ctx.data_unchecked::<StarWars>().chars[self.0].name
|
||
|
}
|
||
|
|
||
|
/// The friends of the human, or an empty list if they have none.
|
||
|
async fn friends(&self, ctx: &Context<'_>) -> Vec<Character> {
|
||
|
ctx.data_unchecked::<StarWars>().chars[self.0]
|
||
|
.friends
|
||
|
.iter()
|
||
|
.map(|id| Human(*id).into())
|
||
|
.collect()
|
||
|
}
|
||
|
|
||
|
/// Which movies they appear in.
|
||
|
async fn appears_in<'a>(&self, ctx: &'a Context<'_>) -> &'a [Episode] {
|
||
|
&ctx.data_unchecked::<StarWars>().chars[self.0].appears_in
|
||
|
}
|
||
|
|
||
|
/// The home planet of the human, or null if unknown.
|
||
|
async fn home_planet<'a>(&self, ctx: &'a Context<'_>) -> &'a Option<&'a str> {
|
||
|
&ctx.data_unchecked::<StarWars>().chars[self.0].home_planet
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub struct Droid(usize);
|
||
|
|
||
|
/// A mechanical creature in the Star Wars universe.
|
||
|
#[GQLObject]
|
||
|
impl Droid {
|
||
|
/// The id of the droid.
|
||
|
async fn id(&self, ctx: &Context<'_>) -> &str {
|
||
|
ctx.data_unchecked::<StarWars>().chars[self.0].id
|
||
|
}
|
||
|
|
||
|
/// The name of the droid.
|
||
|
async fn name(&self, ctx: &Context<'_>) -> &str {
|
||
|
ctx.data_unchecked::<StarWars>().chars[self.0].name
|
||
|
}
|
||
|
|
||
|
/// The friends of the droid, or an empty list if they have none.
|
||
|
async fn friends(&self, ctx: &Context<'_>) -> Vec<Character> {
|
||
|
ctx.data_unchecked::<StarWars>().chars[self.0]
|
||
|
.friends
|
||
|
.iter()
|
||
|
.map(|id| Droid(*id).into())
|
||
|
.collect()
|
||
|
}
|
||
|
|
||
|
/// Which movies they appear in.
|
||
|
async fn appears_in<'a>(&self, ctx: &'a Context<'_>) -> &'a [Episode] {
|
||
|
&ctx.data_unchecked::<StarWars>().chars[self.0].appears_in
|
||
|
}
|
||
|
|
||
|
/// The primary function of the droid.
|
||
|
async fn primary_function<'a>(&self, ctx: &'a Context<'_>) -> &'a Option<&'a str> {
|
||
|
&ctx.data_unchecked::<StarWars>().chars[self.0].primary_function
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub struct QueryRoot;
|
||
|
|
||
|
#[GQLObject]
|
||
|
impl QueryRoot {
|
||
|
async fn hero(
|
||
|
&self,
|
||
|
ctx: &Context<'_>,
|
||
|
#[arg(
|
||
|
desc = "If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode."
|
||
|
)]
|
||
|
episode: Episode,
|
||
|
) -> Character {
|
||
|
if episode == Episode::Empire {
|
||
|
Human(ctx.data_unchecked::<StarWars>().luke).into()
|
||
|
} else {
|
||
|
Droid(ctx.data_unchecked::<StarWars>().artoo).into()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async fn human(
|
||
|
&self,
|
||
|
ctx: &Context<'_>,
|
||
|
#[arg(desc = "id of the human")] id: String,
|
||
|
) -> Option<Human> {
|
||
|
ctx.data_unchecked::<StarWars>().human(&id).map(Human)
|
||
|
}
|
||
|
|
||
|
async fn humans(
|
||
|
&self,
|
||
|
ctx: &Context<'_>,
|
||
|
after: Option<String>,
|
||
|
before: Option<String>,
|
||
|
first: Option<i32>,
|
||
|
last: Option<i32>,
|
||
|
) -> FieldResult<Connection<usize, Human, EmptyFields, EmptyFields>> {
|
||
|
let humans = ctx
|
||
|
.data_unchecked::<StarWars>()
|
||
|
.humans()
|
||
|
.iter()
|
||
|
.copied()
|
||
|
.collect::<Vec<_>>();
|
||
|
query_characters(after, before, first, last, &humans)
|
||
|
.await
|
||
|
.map(|conn| conn.map_node(Human))
|
||
|
}
|
||
|
|
||
|
async fn droid(
|
||
|
&self,
|
||
|
ctx: &Context<'_>,
|
||
|
#[arg(desc = "id of the droid")] id: String,
|
||
|
) -> Option<Droid> {
|
||
|
ctx.data_unchecked::<StarWars>().droid(&id).map(Droid)
|
||
|
}
|
||
|
|
||
|
async fn droids(
|
||
|
&self,
|
||
|
ctx: &Context<'_>,
|
||
|
after: Option<String>,
|
||
|
before: Option<String>,
|
||
|
first: Option<i32>,
|
||
|
last: Option<i32>,
|
||
|
) -> FieldResult<Connection<usize, Droid, EmptyFields, EmptyFields>> {
|
||
|
let droids = ctx
|
||
|
.data_unchecked::<StarWars>()
|
||
|
.droids()
|
||
|
.iter()
|
||
|
.copied()
|
||
|
.collect::<Vec<_>>();
|
||
|
query_characters(after, before, first, last, &droids)
|
||
|
.await
|
||
|
.map(|conn| conn.map_node(Droid))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(GQLInterface)]
|
||
|
#[graphql(
|
||
|
field(name = "id", type = "&str", context),
|
||
|
field(name = "name", type = "&str", context),
|
||
|
field(name = "friends", type = "Vec<Character>", context),
|
||
|
field(name = "appears_in", type = "&'ctx [Episode]", context)
|
||
|
)]
|
||
|
pub enum Character {
|
||
|
Human(Human),
|
||
|
Droid(Droid),
|
||
|
}
|
||
|
|
||
|
async fn query_characters(
|
||
|
after: Option<String>,
|
||
|
before: Option<String>,
|
||
|
first: Option<i32>,
|
||
|
last: Option<i32>,
|
||
|
characters: &[usize],
|
||
|
) -> FieldResult<Connection<usize, usize, EmptyFields, EmptyFields>> {
|
||
|
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());
|
||
|
connection.append(
|
||
|
slice
|
||
|
.iter()
|
||
|
.enumerate()
|
||
|
.map(|(idx, item)| Edge::new(start + idx, *item)),
|
||
|
);
|
||
|
Ok(connection)
|
||
|
},
|
||
|
)
|
||
|
.await
|
||
|
}
|