api rebuild
This commit is contained in:
parent
50c4ac387a
commit
cecaecb92e
11 changed files with 782 additions and 175 deletions
21
Cargo.lock
generated
21
Cargo.lock
generated
|
@ -49,6 +49,7 @@ dependencies = [
|
||||||
"async-graphql-axum",
|
"async-graphql-axum",
|
||||||
"axum",
|
"axum",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
|
@ -64,9 +65,9 @@ checksum = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-graphql"
|
name = "async-graphql"
|
||||||
version = "5.0.2"
|
version = "5.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5005cfd364b44d9cb55486b44184fe41a57b97339e17ce10db05f6b6093571d9"
|
checksum = "42bb92ffef089e5b61e90bcc004c9689554dfb5a150d88e81c7f6fef9e76eeae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-graphql-derive",
|
"async-graphql-derive",
|
||||||
"async-graphql-parser",
|
"async-graphql-parser",
|
||||||
|
@ -96,9 +97,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-graphql-axum"
|
name = "async-graphql-axum"
|
||||||
version = "5.0.2"
|
version = "5.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5554f6f1578ba1c65942ff7103c4c5421a47890a64666e1863d38bb9194ab091"
|
checksum = "077bf197d54397cff2324b79d86a7a21b2a83260e62e33eccae33009427897c9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-graphql",
|
"async-graphql",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
@ -113,9 +114,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-graphql-derive"
|
name = "async-graphql-derive"
|
||||||
version = "5.0.2"
|
version = "5.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ff995b9d89198740d3701f1e5f7101e2822d5f4f1f4db19e9a1a9314cb14364"
|
checksum = "4fa579c7cea32030600994d579554b257e10d5ad87705f3d150b49ee08bd629d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"Inflector",
|
"Inflector",
|
||||||
"async-graphql-parser",
|
"async-graphql-parser",
|
||||||
|
@ -129,9 +130,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-graphql-parser"
|
name = "async-graphql-parser"
|
||||||
version = "5.0.2"
|
version = "5.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e73570e2270b9921a183df47760bea67a65afb145eaaa1b82a9c34b0c6209ff"
|
checksum = "3b67a5bea60997ca72908854655ae87f7970dc7d786d9a42fd1d17069fa42ebc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-graphql-value",
|
"async-graphql-value",
|
||||||
"pest",
|
"pest",
|
||||||
|
@ -141,9 +142,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-graphql-value"
|
name = "async-graphql-value"
|
||||||
version = "5.0.2"
|
version = "5.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97013c726c11f29262f52e9487025a72bae58262bad3c26389936ce3bf143b11"
|
checksum = "79c2721eb88245ca055e148a3f03cb11a88535c206ac5a7c59e9edb22816320a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
|
|
|
@ -8,10 +8,11 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde_json = "1.0.89"
|
serde_json = "1.0.89"
|
||||||
serde = "1.0.149"
|
serde = "1.0.149"
|
||||||
async-graphql = { version = "5.0.2" }
|
async-graphql = { version = "5.0.3" }
|
||||||
async-graphql-axum = "5.0.2"
|
async-graphql-axum = "5.0.3"
|
||||||
axum = "0.6.1"
|
axum = "0.6.1"
|
||||||
sqlx = { version = "0.6.2", features = [ "runtime-tokio-native-tls", "postgres" ] }
|
sqlx = { version = "0.6.2", features = [ "runtime-tokio-native-tls", "postgres" ] }
|
||||||
tokio = { version = "1.23.0", features = [ "full" ] }
|
tokio = { version = "1.23.0", features = [ "full" ] }
|
||||||
tower-http = { version = "0.3.5", features = ["cors"] }
|
tower-http = { version = "0.3.5", features = ["cors"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
reqwest = "0.11.13"
|
|
@ -1,36 +1,140 @@
|
||||||
|
use crate::utils::{Filters, IdOrNameBy};
|
||||||
use async_graphql::{Context, Object};
|
use async_graphql::{Context, Object};
|
||||||
|
use sqlx::{Pool, Postgres, Row};
|
||||||
|
|
||||||
|
/// A specific with optional faction filter.
|
||||||
|
pub struct Class {
|
||||||
|
filters: Filters,
|
||||||
|
class_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Class {
|
||||||
|
async fn fetch<'ctx>(&self, ctx: &Context<'ctx>, filters: Filters) -> i64 {
|
||||||
|
let pool = ctx.data::<Pool<Postgres>>().unwrap();
|
||||||
|
|
||||||
|
let sql = format!(
|
||||||
|
"SELECT count(distinct character_id) FROM classes WHERE time > now() - interval '15 minutes' AND class_id = $1 {};",
|
||||||
|
filters.sql(),
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("{}", sql);
|
||||||
|
|
||||||
|
let query: i64 = sqlx::query(sql.as_str())
|
||||||
|
.bind(self.class_name.as_str())
|
||||||
|
.fetch_one(pool)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.get(0);
|
||||||
|
|
||||||
|
query
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Class {
|
||||||
|
async fn total<'ctx>(&self, ctx: &Context<'ctx>) -> i64 {
|
||||||
|
self.fetch(ctx, self.filters.clone()).await
|
||||||
|
}
|
||||||
|
async fn nc<'ctx>(&self, ctx: &Context<'ctx>) -> i64 {
|
||||||
|
self.fetch(
|
||||||
|
ctx,
|
||||||
|
Filters {
|
||||||
|
faction: Some(IdOrNameBy::Id(1)),
|
||||||
|
..self.filters.clone()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
async fn tr<'ctx>(&self, ctx: &Context<'ctx>) -> i64 {
|
||||||
|
self.fetch(
|
||||||
|
ctx,
|
||||||
|
Filters {
|
||||||
|
faction: Some(IdOrNameBy::Id(2)),
|
||||||
|
..self.filters.clone()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
async fn vs<'ctx>(&self, ctx: &Context<'ctx>) -> i64 {
|
||||||
|
self.fetch(
|
||||||
|
ctx,
|
||||||
|
Filters {
|
||||||
|
faction: Some(IdOrNameBy::Id(3)),
|
||||||
|
..self.filters.clone()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Super-struct of each class.
|
||||||
pub struct Classes {
|
pub struct Classes {
|
||||||
world_id: String,
|
filters: Filters,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Classes {
|
impl Classes {
|
||||||
pub fn new(world_id: String) -> Self {
|
pub fn new(filters: Option<Filters>) -> Self {
|
||||||
Self { world_id }
|
Self {
|
||||||
|
filters: filters.unwrap_or_default(),
|
||||||
}
|
}
|
||||||
async fn by_class<'ctx>(&self, ctx: &Context<'ctx>, class_name: &str) -> u32 {
|
|
||||||
0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Object]
|
#[Object]
|
||||||
impl Classes {
|
impl Classes {
|
||||||
async fn infiltrator<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
async fn infiltrator(&self) -> Class {
|
||||||
self.by_class(ctx, "infiltrator").await
|
Class {
|
||||||
}
|
filters: self.filters.clone(),
|
||||||
async fn light_assault<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
class_name: "infiltrator".to_string(),
|
||||||
self.by_class(ctx, "light_assault").await
|
}
|
||||||
}
|
}
|
||||||
async fn combat_medic<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
async fn light_assault(&self) -> Class {
|
||||||
self.by_class(ctx, "combat_medic").await
|
Class {
|
||||||
}
|
filters: self.filters.clone(),
|
||||||
async fn engineer<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
class_name: "light_assault".to_string(),
|
||||||
self.by_class(ctx, "engineer").await
|
}
|
||||||
}
|
}
|
||||||
async fn heavy_assault<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
async fn combat_medic(&self) -> Class {
|
||||||
self.by_class(ctx, "heavy_assault").await
|
Class {
|
||||||
}
|
filters: self.filters.clone(),
|
||||||
async fn max<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
class_name: "combat_medic".to_string(),
|
||||||
self.by_class(ctx, "max").await
|
}
|
||||||
|
}
|
||||||
|
async fn engineer(&self) -> Class {
|
||||||
|
Class {
|
||||||
|
filters: self.filters.clone(),
|
||||||
|
class_name: "engineer".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn heavy_assault(&self) -> Class {
|
||||||
|
Class {
|
||||||
|
filters: self.filters.clone(),
|
||||||
|
class_name: "heavy_assault".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn max(&self) -> Class {
|
||||||
|
Class {
|
||||||
|
filters: self.filters.clone(),
|
||||||
|
class_name: "max".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ClassesQuery;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl ClassesQuery {
|
||||||
|
/// Get all classes
|
||||||
|
pub async fn classes(&self, filter: Option<Filters>) -> Classes {
|
||||||
|
Classes::new(filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a specific class
|
||||||
|
pub async fn class(&self, filter: Option<Filters>, class_name: String) -> Class {
|
||||||
|
Class {
|
||||||
|
filters: filter.unwrap_or_default(),
|
||||||
|
class_name,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,3 +91,14 @@ impl Health {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct HealthQuery;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl HealthQuery {
|
||||||
|
/// Reports on the health of Saerro Listening Post
|
||||||
|
pub async fn health(&self) -> Health {
|
||||||
|
Health {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
mod classes;
|
mod classes;
|
||||||
mod health;
|
mod health;
|
||||||
|
mod population;
|
||||||
mod query;
|
mod query;
|
||||||
|
mod utils;
|
||||||
mod vehicles;
|
mod vehicles;
|
||||||
mod world;
|
mod world;
|
||||||
|
mod zone;
|
||||||
|
|
||||||
use async_graphql::{
|
use async_graphql::{
|
||||||
http::GraphiQLSource, EmptyMutation, EmptySubscription, Request, Response, Schema,
|
http::GraphiQLSource, EmptyMutation, EmptySubscription, Request, Response, Schema,
|
||||||
|
@ -61,7 +64,7 @@ async fn main() {
|
||||||
.unwrap_or("postgres://saerrouser:saerro321@localhost:5432/data".to_string());
|
.unwrap_or("postgres://saerrouser:saerro321@localhost:5432/data".to_string());
|
||||||
let db = sqlx::PgPool::connect(&db_url).await.unwrap();
|
let db = sqlx::PgPool::connect(&db_url).await.unwrap();
|
||||||
|
|
||||||
let schema = Schema::build(query::Query, EmptyMutation, EmptySubscription)
|
let schema = Schema::build(query::Query::default(), EmptyMutation, EmptySubscription)
|
||||||
.data(db.clone())
|
.data(db.clone())
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
|
|
85
services/api/src/population.rs
Normal file
85
services/api/src/population.rs
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
use crate::utils::Filters;
|
||||||
|
use async_graphql::{Context, Object};
|
||||||
|
use sqlx::{Pool, Postgres, Row};
|
||||||
|
|
||||||
|
/// A filterable list of currently active players.
|
||||||
|
pub struct Population {
|
||||||
|
filters: Filters,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Population {
|
||||||
|
pub fn new(filters: Option<Filters>) -> Self {
|
||||||
|
Self {
|
||||||
|
filters: filters.unwrap_or_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Population {
|
||||||
|
async fn by_faction<'ctx>(&self, ctx: &Context<'ctx>, faction: i32) -> i64 {
|
||||||
|
let pool = ctx.data::<Pool<Postgres>>().unwrap();
|
||||||
|
|
||||||
|
let sql = format!(
|
||||||
|
"SELECT count(distinct character_id) FROM players WHERE time > now() - interval '15 minutes' AND faction_id = $1 {};",
|
||||||
|
self.filters.sql(),
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("{}", sql);
|
||||||
|
|
||||||
|
let query: i64 = sqlx::query(sql.as_str())
|
||||||
|
.bind(faction)
|
||||||
|
.fetch_one(pool)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.get(0);
|
||||||
|
|
||||||
|
query
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Population {
|
||||||
|
async fn total<'ctx>(&self, ctx: &Context<'ctx>) -> i64 {
|
||||||
|
let pool = ctx.data::<Pool<Postgres>>().unwrap();
|
||||||
|
|
||||||
|
let sql = format!(
|
||||||
|
"SELECT count(distinct character_id) FROM players WHERE time > now() - interval '15 minutes' {};",
|
||||||
|
self.filters.sql(),
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("{}", sql);
|
||||||
|
|
||||||
|
let query: i64 = sqlx::query(sql.as_str())
|
||||||
|
.fetch_one(pool)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.get(0);
|
||||||
|
|
||||||
|
query
|
||||||
|
}
|
||||||
|
async fn nc<'ctx>(&self, ctx: &Context<'ctx>) -> i64 {
|
||||||
|
self.by_faction(ctx, 1).await
|
||||||
|
}
|
||||||
|
async fn vs<'ctx>(&self, ctx: &Context<'ctx>) -> i64 {
|
||||||
|
self.by_faction(ctx, 2).await
|
||||||
|
}
|
||||||
|
async fn tr<'ctx>(&self, ctx: &Context<'ctx>) -> i64 {
|
||||||
|
self.by_faction(ctx, 3).await
|
||||||
|
}
|
||||||
|
async fn ns<'ctx>(&self, ctx: &Context<'ctx>) -> i64 {
|
||||||
|
self.by_faction(ctx, 4).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct PopulationQuery;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl PopulationQuery {
|
||||||
|
/// A filterable list of currently active players.
|
||||||
|
/// This is a core query that others will use to filter by,
|
||||||
|
/// i.e. `emerald { population { total } }` is equivalent to `population(filter: { world: { name: "emerald" } }) { total }`
|
||||||
|
pub async fn population(&self, filter: Option<Filters>) -> Population {
|
||||||
|
Population::new(filter)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,30 +1,15 @@
|
||||||
use crate::health::Health;
|
use crate::{
|
||||||
use crate::world::World;
|
classes::ClassesQuery, health::HealthQuery, population::PopulationQuery,
|
||||||
use async_graphql::Object;
|
vehicles::VehicleQuery, world::WorldQuery, zone::ZoneQuery,
|
||||||
|
};
|
||||||
|
use async_graphql::MergedObject;
|
||||||
|
|
||||||
pub struct Query;
|
#[derive(MergedObject, Default)]
|
||||||
|
pub struct Query(
|
||||||
#[Object]
|
PopulationQuery,
|
||||||
impl Query {
|
VehicleQuery,
|
||||||
/// Returns a graph for the world with the given ID.
|
ClassesQuery,
|
||||||
/// If the world does not exist, this will not fail.
|
WorldQuery,
|
||||||
async fn world(&self, id: String) -> World {
|
ZoneQuery,
|
||||||
World { id: id.clone() }
|
HealthQuery,
|
||||||
}
|
);
|
||||||
|
|
||||||
/// Returns a graph for the world specified by it's human name.
|
|
||||||
/// This is case-insensitive; but will not fail.
|
|
||||||
async fn world_by_name(&self, name: String) -> World {
|
|
||||||
World::from_name(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a graph of all known live play worlds.
|
|
||||||
async fn all_worlds(&self) -> Vec<World> {
|
|
||||||
World::all_worlds()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reports on the health of Saerro Listening Post
|
|
||||||
async fn health(&self) -> Health {
|
|
||||||
Health {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
100
services/api/src/utils.rs
Normal file
100
services/api/src/utils.rs
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
use async_graphql::{InputObject, OneofObject};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref WORLD_IDS: HashMap<String, i32> = HashMap::from([
|
||||||
|
("connery".to_string(), 1),
|
||||||
|
("miller".to_string(), 10),
|
||||||
|
("cobalt".to_string(), 13),
|
||||||
|
("emerald".to_string(), 17),
|
||||||
|
("jaeger".to_string(), 19),
|
||||||
|
("soltech".to_string(), 40),
|
||||||
|
("genudine".to_string(), 1000),
|
||||||
|
("ceres".to_string(), 2000),
|
||||||
|
]);
|
||||||
|
pub static ref ID_TO_WORLD: HashMap<i32, String> = WORLD_IDS
|
||||||
|
.iter()
|
||||||
|
.map(|(name, id)| (id.to_owned(), name.to_owned()))
|
||||||
|
.collect();
|
||||||
|
pub static ref FACTION_IDS: HashMap<String, i32> = HashMap::from([
|
||||||
|
("vs".to_string(), 1),
|
||||||
|
("nc".to_string(), 2),
|
||||||
|
("tr".to_string(), 3),
|
||||||
|
("ns".to_string(), 4),
|
||||||
|
]);
|
||||||
|
pub static ref ID_TO_FACTION: HashMap<i32, String> = FACTION_IDS
|
||||||
|
.iter()
|
||||||
|
.map(|(name, id)| (id.to_owned(), name.to_owned()))
|
||||||
|
.collect();
|
||||||
|
pub static ref ZONE_IDS: HashMap<String, i32> = HashMap::from([
|
||||||
|
("indar".to_string(), 2),
|
||||||
|
("hossin".to_string(), 4),
|
||||||
|
("amerish".to_string(), 6),
|
||||||
|
("esamir".to_string(), 8),
|
||||||
|
("oshur".to_string(), 344),
|
||||||
|
]);
|
||||||
|
pub static ref ID_TO_ZONE: HashMap<i32, String> = ZONE_IDS
|
||||||
|
.iter()
|
||||||
|
.map(|(name, id)| (id.to_owned(), name.to_owned()))
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allows for one of the following:
|
||||||
|
/// - By ID, example: `{ id: 1 }`
|
||||||
|
/// - By name (case-insensitive), example: `{ name: "Connery" }`
|
||||||
|
#[derive(OneofObject, Clone)]
|
||||||
|
pub enum IdOrNameBy {
|
||||||
|
Id(i32),
|
||||||
|
Name(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id_or_name_to_id(map: &HashMap<String, i32>, by: &IdOrNameBy) -> Option<i32> {
|
||||||
|
match by {
|
||||||
|
IdOrNameBy::Id(id) => Some(*id),
|
||||||
|
IdOrNameBy::Name(name) => map.get(&name.to_lowercase()).map(|id| *id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id_or_name_to_name(map: &HashMap<i32, String>, id: &IdOrNameBy) -> Option<String> {
|
||||||
|
match id {
|
||||||
|
IdOrNameBy::Id(id) => map.get(id).map(|name| name.to_owned()),
|
||||||
|
IdOrNameBy::Name(name) => Some(name.to_owned()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A filter for core queries, allows for filtering by world, faction, and zone.
|
||||||
|
/// Omitting a field will not filter by that field, so for example:
|
||||||
|
/// `{ world: { id: 1 }, faction: { name: "VS" } }`
|
||||||
|
/// will filter by world ID 1 and faction name "VS", but also search in every continent.
|
||||||
|
#[derive(InputObject, Default, Clone)]
|
||||||
|
pub struct Filters {
|
||||||
|
/// The world to filter by, like Connery, Emerald, etc.
|
||||||
|
pub world: Option<IdOrNameBy>,
|
||||||
|
/// The faction to filter by, like VS, NC, TR, or NS
|
||||||
|
pub faction: Option<IdOrNameBy>,
|
||||||
|
/// The zone or continent to filter by, like Indar, Amerish, etc.
|
||||||
|
pub zone: Option<IdOrNameBy>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Filters {
|
||||||
|
pub fn sql(&self) -> String {
|
||||||
|
let mut sql = String::new();
|
||||||
|
if let Some(world) = &self.world {
|
||||||
|
if let Some(world_id) = id_or_name_to_id(&WORLD_IDS, world) {
|
||||||
|
sql.push_str(&format!(" AND world_id = {}", world_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(faction) = &self.faction {
|
||||||
|
if let Some(faction_id) = id_or_name_to_id(&FACTION_IDS, faction) {
|
||||||
|
sql.push_str(&format!(" AND faction_id = {}", faction_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(zone) = &self.zone {
|
||||||
|
if let Some(zone_id) = id_or_name_to_id(&ZONE_IDS, zone) {
|
||||||
|
sql.push_str(&format!(" AND zone_id = {}", zone_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sql
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,73 +1,221 @@
|
||||||
|
use crate::utils::{Filters, IdOrNameBy};
|
||||||
use async_graphql::{Context, Object};
|
use async_graphql::{Context, Object};
|
||||||
|
use sqlx::{Pool, Postgres, Row};
|
||||||
|
|
||||||
|
/// A specific vehicle
|
||||||
|
pub struct Vehicle {
|
||||||
|
filters: Filters,
|
||||||
|
vehicle_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vehicle {
|
||||||
|
async fn fetch<'ctx>(&self, ctx: &Context<'ctx>, filters: Filters) -> i64 {
|
||||||
|
let pool = ctx.data::<Pool<Postgres>>().unwrap();
|
||||||
|
|
||||||
|
let sql = format!(
|
||||||
|
"SELECT count(distinct character_id) FROM vehicles WHERE time > now() - interval '15 minutes' AND vehicle_id = $1 {};",
|
||||||
|
filters.sql(),
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("{}", sql);
|
||||||
|
|
||||||
|
let query: i64 = sqlx::query(sql.as_str())
|
||||||
|
.bind(self.vehicle_name.as_str())
|
||||||
|
.fetch_one(pool)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.get(0);
|
||||||
|
|
||||||
|
query
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Vehicle {
|
||||||
|
async fn total<'ctx>(&self, ctx: &Context<'ctx>) -> i64 {
|
||||||
|
self.fetch(ctx, self.filters.clone()).await
|
||||||
|
}
|
||||||
|
async fn nc<'ctx>(&self, ctx: &Context<'ctx>) -> i64 {
|
||||||
|
self.fetch(
|
||||||
|
ctx,
|
||||||
|
Filters {
|
||||||
|
faction: Some(IdOrNameBy::Id(1)),
|
||||||
|
..self.filters.clone()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
async fn tr<'ctx>(&self, ctx: &Context<'ctx>) -> i64 {
|
||||||
|
self.fetch(
|
||||||
|
ctx,
|
||||||
|
Filters {
|
||||||
|
faction: Some(IdOrNameBy::Id(2)),
|
||||||
|
..self.filters.clone()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
async fn vs<'ctx>(&self, ctx: &Context<'ctx>) -> i64 {
|
||||||
|
self.fetch(
|
||||||
|
ctx,
|
||||||
|
Filters {
|
||||||
|
faction: Some(IdOrNameBy::Id(3)),
|
||||||
|
..self.filters.clone()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Super-struct for all vehicles.
|
||||||
pub struct Vehicles {
|
pub struct Vehicles {
|
||||||
world_id: String,
|
filters: Filters,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vehicles {
|
impl Vehicles {
|
||||||
pub fn new(world_id: String) -> Self {
|
pub fn new(filters: Option<Filters>) -> Self {
|
||||||
Self { world_id }
|
Self {
|
||||||
|
filters: filters.unwrap_or_default(),
|
||||||
}
|
}
|
||||||
async fn by_vehicle<'ctx>(&self, ctx: &Context<'ctx>, vehicle_name: &str) -> u32 {
|
|
||||||
0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Object]
|
#[Object]
|
||||||
impl Vehicles {
|
impl Vehicles {
|
||||||
async fn flash<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
async fn total<'ctx>(&self, ctx: &Context<'ctx>) -> i64 {
|
||||||
self.by_vehicle(ctx, "flash").await
|
let pool = ctx.data::<Pool<Postgres>>().unwrap();
|
||||||
|
|
||||||
|
let sql = format!(
|
||||||
|
"SELECT count(distinct character_id) FROM vehicles WHERE time > now() - interval '15 minutes' {};",
|
||||||
|
self.filters.sql(),
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("{}", sql);
|
||||||
|
|
||||||
|
let query: i64 = sqlx::query(sql.as_str())
|
||||||
|
.fetch_one(pool)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.get(0);
|
||||||
|
|
||||||
|
query
|
||||||
}
|
}
|
||||||
async fn sunderer<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
|
||||||
self.by_vehicle(ctx, "sunderer").await
|
// Transport
|
||||||
|
async fn flash(&self) -> Vehicle {
|
||||||
|
Vehicle {
|
||||||
|
filters: self.filters.clone(),
|
||||||
|
vehicle_name: "flash".to_string(),
|
||||||
}
|
}
|
||||||
async fn ant<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
|
||||||
self.by_vehicle(ctx, "ant").await
|
|
||||||
}
|
}
|
||||||
async fn harasser<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
async fn sunderer(&self) -> Vehicle {
|
||||||
self.by_vehicle(ctx, "harasser").await
|
Vehicle {
|
||||||
|
filters: self.filters.clone(),
|
||||||
|
vehicle_name: "sunderer".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn ant(&self) -> Vehicle {
|
||||||
|
Vehicle {
|
||||||
|
filters: self.filters.clone(),
|
||||||
|
vehicle_name: "ant".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn harasser(&self) -> Vehicle {
|
||||||
|
Vehicle {
|
||||||
|
filters: self.filters.clone(),
|
||||||
|
vehicle_name: "harasser".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn javelin(&self) -> Vehicle {
|
||||||
|
Vehicle {
|
||||||
|
filters: self.filters.clone(),
|
||||||
|
vehicle_name: "javelin".to_string(),
|
||||||
}
|
}
|
||||||
async fn javelin<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
|
||||||
self.by_vehicle(ctx, "javelin").await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tanks
|
// Tanks
|
||||||
async fn lightning<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
async fn lightning(&self) -> Vehicle {
|
||||||
self.by_vehicle(ctx, "lightning").await
|
Vehicle {
|
||||||
|
filters: self.filters.clone(),
|
||||||
|
vehicle_name: "javelin".to_string(),
|
||||||
}
|
}
|
||||||
async fn prowler<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
|
||||||
self.by_vehicle(ctx, "prowler").await
|
|
||||||
}
|
}
|
||||||
async fn vanguard<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
async fn prowler(&self) -> Vehicle {
|
||||||
self.by_vehicle(ctx, "vanguard").await
|
Vehicle {
|
||||||
|
filters: self.filters.clone(),
|
||||||
|
vehicle_name: "prowler".to_string(),
|
||||||
}
|
}
|
||||||
async fn magrider<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
|
||||||
self.by_vehicle(ctx, "magrider").await
|
|
||||||
}
|
}
|
||||||
async fn chimera<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
async fn vanguard(&self) -> Vehicle {
|
||||||
self.by_vehicle(ctx, "chimera").await
|
Vehicle {
|
||||||
|
filters: self.filters.clone(),
|
||||||
|
vehicle_name: "vanguard".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn magrider(&self) -> Vehicle {
|
||||||
|
Vehicle {
|
||||||
|
filters: self.filters.clone(),
|
||||||
|
vehicle_name: "magrider".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn chimera(&self) -> Vehicle {
|
||||||
|
Vehicle {
|
||||||
|
filters: self.filters.clone(),
|
||||||
|
vehicle_name: "chimera".to_string(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Air
|
// Air
|
||||||
async fn mosquito<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
async fn mosquito(&self) -> Vehicle {
|
||||||
self.by_vehicle(ctx, "mosquito").await
|
Vehicle {
|
||||||
}
|
filters: self.filters.clone(),
|
||||||
async fn liberator<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
vehicle_name: "mosquito".to_string(),
|
||||||
self.by_vehicle(ctx, "liberator").await
|
}
|
||||||
}
|
}
|
||||||
async fn galaxy<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
async fn liberator(&self) -> Vehicle {
|
||||||
self.by_vehicle(ctx, "galaxy").await
|
Vehicle {
|
||||||
}
|
filters: self.filters.clone(),
|
||||||
async fn valkyrie<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
vehicle_name: "liberator".to_string(),
|
||||||
self.by_vehicle(ctx, "valkyrie").await
|
}
|
||||||
}
|
}
|
||||||
async fn reaver<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
async fn galaxy(&self) -> Vehicle {
|
||||||
self.by_vehicle(ctx, "reaver").await
|
Vehicle {
|
||||||
}
|
filters: self.filters.clone(),
|
||||||
async fn scythe<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
vehicle_name: "galaxy".to_string(),
|
||||||
self.by_vehicle(ctx, "scythe").await
|
}
|
||||||
}
|
}
|
||||||
async fn dervish<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
async fn valkyrie(&self) -> Vehicle {
|
||||||
self.by_vehicle(ctx, "dervish").await
|
Vehicle {
|
||||||
|
filters: self.filters.clone(),
|
||||||
|
vehicle_name: "valkyrie".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn reaver(&self) -> Vehicle {
|
||||||
|
Vehicle {
|
||||||
|
filters: self.filters.clone(),
|
||||||
|
vehicle_name: "reaver".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn scythe(&self) -> Vehicle {
|
||||||
|
Vehicle {
|
||||||
|
filters: self.filters.clone(),
|
||||||
|
vehicle_name: "scythe".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn dervish(&self) -> Vehicle {
|
||||||
|
Vehicle {
|
||||||
|
filters: self.filters.clone(),
|
||||||
|
vehicle_name: "dervish".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct VehicleQuery;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl VehicleQuery {
|
||||||
|
pub async fn vehicles(&self, filter: Option<Filters>) -> Vehicles {
|
||||||
|
Vehicles::new(filter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,48 +1,25 @@
|
||||||
use crate::{classes::Classes, vehicles::Vehicles};
|
use crate::{
|
||||||
use async_graphql::{Context, Object};
|
classes::Classes,
|
||||||
use lazy_static::lazy_static;
|
population::Population,
|
||||||
use std::collections::HashMap;
|
utils::{id_or_name_to_id, id_or_name_to_name, Filters, IdOrNameBy, ID_TO_WORLD, WORLD_IDS},
|
||||||
|
vehicles::Vehicles,
|
||||||
|
zone::Zones,
|
||||||
|
};
|
||||||
|
use async_graphql::Object;
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref WORLD_NAME_TO_ID: HashMap<&'static str, &'static str> = HashMap::from([
|
|
||||||
("connery", "1"),
|
|
||||||
("miller", "10"),
|
|
||||||
("cobalt", "13"),
|
|
||||||
("emerald", "17"),
|
|
||||||
("jaeger", "19"),
|
|
||||||
("soltech", "40"),
|
|
||||||
("genudine", "1000"),
|
|
||||||
("ceres", "2000"),
|
|
||||||
]);
|
|
||||||
static ref WORLD_ID_TO_NAME: HashMap<&'static str, &'static str> = HashMap::from([
|
|
||||||
("1", "Connery"),
|
|
||||||
("10", "Miller"),
|
|
||||||
("13", "Cobalt"),
|
|
||||||
("17", "Emerald"),
|
|
||||||
("19", "Jaeger"),
|
|
||||||
("40", "SolTech"),
|
|
||||||
("1000", "Genudine"),
|
|
||||||
("2000", "Ceres"),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
pub struct World {
|
pub struct World {
|
||||||
pub id: String,
|
filter: Filters,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
pub fn from_name(name: String) -> World {
|
pub fn new(filter: IdOrNameBy) -> Self {
|
||||||
let id = WORLD_NAME_TO_ID
|
Self {
|
||||||
.get(name.to_lowercase().as_str())
|
filter: Filters {
|
||||||
.unwrap_or(&"-1");
|
world: Some(filter),
|
||||||
|
faction: None,
|
||||||
World { id: id.to_string() }
|
zone: None,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_worlds() -> Vec<World> {
|
|
||||||
WORLD_ID_TO_NAME
|
|
||||||
.keys()
|
|
||||||
.map(|id| World { id: id.to_string() })
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,61 +31,121 @@ impl World {
|
||||||
/// If World.id is not valid or known to the API, World.name will return "Unknown".
|
/// If World.id is not valid or known to the API, World.name will return "Unknown".
|
||||||
#[Object]
|
#[Object]
|
||||||
impl World {
|
impl World {
|
||||||
async fn id(&self) -> &str {
|
/// The ID of the world.
|
||||||
&self.id
|
async fn id(&self) -> i32 {
|
||||||
|
id_or_name_to_id(&WORLD_IDS, self.filter.world.as_ref().unwrap()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The name of the world, in official game capitalization.
|
||||||
async fn name(&self) -> String {
|
async fn name(&self) -> String {
|
||||||
WORLD_ID_TO_NAME
|
let name = id_or_name_to_name(&ID_TO_WORLD, self.filter.world.as_ref().unwrap()).unwrap();
|
||||||
.get(self.id.as_str())
|
|
||||||
.unwrap_or(&"Unknown")
|
// Special case for SolTech, lol.
|
||||||
.to_string()
|
if name == "soltech" {
|
||||||
|
return "SolTech".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn population<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
// Capitalize the first letter
|
||||||
0
|
name[0..1].to_uppercase() + &name[1..]
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn faction_population(&self) -> FactionPopulation {
|
/// Population filtered to this world.
|
||||||
FactionPopulation {
|
async fn population(&self) -> Population {
|
||||||
world_id: self.id.clone(),
|
Population::new(Some(Filters {
|
||||||
}
|
world: self.filter.world.clone(),
|
||||||
|
faction: None,
|
||||||
|
zone: None,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Vehicles filtered to this world.
|
||||||
async fn vehicles(&self) -> Vehicles {
|
async fn vehicles(&self) -> Vehicles {
|
||||||
Vehicles::new(self.id.clone())
|
Vehicles::new(Some(Filters {
|
||||||
|
world: self.filter.world.clone(),
|
||||||
|
faction: None,
|
||||||
|
zone: None,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Classes filtered to this world.
|
||||||
async fn classes(&self) -> Classes {
|
async fn classes(&self) -> Classes {
|
||||||
Classes::new(self.id.clone())
|
Classes::new(Some(Filters {
|
||||||
|
world: self.filter.world.clone(),
|
||||||
|
faction: None,
|
||||||
|
zone: None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a specific zone/continent on this world.
|
||||||
|
async fn zones(&self) -> Zones {
|
||||||
|
Zones::new(Some(self.filter.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FactionPopulation {
|
#[derive(Default)]
|
||||||
world_id: String,
|
pub struct WorldQuery;
|
||||||
}
|
|
||||||
|
|
||||||
impl FactionPopulation {
|
|
||||||
async fn by_faction<'ctx>(&self, ctx: &Context<'ctx>, faction: u8) -> u32 {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[Object]
|
#[Object]
|
||||||
impl FactionPopulation {
|
impl WorldQuery {
|
||||||
async fn vs<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
/// A world by ID or name.
|
||||||
self.by_faction(ctx, 1).await
|
pub async fn world(&self, by: IdOrNameBy) -> World {
|
||||||
|
World::new(by)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn nc<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
/// All worlds. This is a convenience method for getting all worlds in one query.
|
||||||
self.by_faction(ctx, 2).await
|
/// If you want all of them as aggregate instead of as individual units, use `population`, `vehicles`, `classes` directly instead.
|
||||||
|
pub async fn all_worlds(&self) -> Vec<World> {
|
||||||
|
ID_TO_WORLD
|
||||||
|
.iter()
|
||||||
|
.map(|(id, _)| World::new(IdOrNameBy::Id(*id)))
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn tr<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
/// The Connery world in US West on PC
|
||||||
self.by_faction(ctx, 3).await
|
/// Shorthand for `world(by: { id: 1 }})`
|
||||||
|
pub async fn connery(&self) -> World {
|
||||||
|
World::new(IdOrNameBy::Id(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn ns<'ctx>(&self, ctx: &Context<'ctx>) -> u32 {
|
/// The Miller world in EU on PC
|
||||||
self.by_faction(ctx, 4).await
|
/// Shorthand for `world(by: { id: 10 }})`
|
||||||
|
pub async fn miller(&self) -> World {
|
||||||
|
World::new(IdOrNameBy::Id(10))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Cobalt world in EU on PC
|
||||||
|
/// Shorthand for `world(by: { id: 13 }})`
|
||||||
|
pub async fn cobalt(&self) -> World {
|
||||||
|
World::new(IdOrNameBy::Id(13))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Emerald world in US East on PC
|
||||||
|
/// Shorthand for `world(by: { id: 17 }})`
|
||||||
|
pub async fn emerald(&self) -> World {
|
||||||
|
World::new(IdOrNameBy::Id(17))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Jaeger world in US East on PC
|
||||||
|
/// Shorthand for `world(by: { id: 19 }})`
|
||||||
|
pub async fn jaeger(&self) -> World {
|
||||||
|
World::new(IdOrNameBy::Id(19))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The SolTech world in Japan on PC
|
||||||
|
/// Shorthand for `world(by: { id: 40 }})`
|
||||||
|
pub async fn soltech(&self) -> World {
|
||||||
|
World::new(IdOrNameBy::Id(40))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Genudine world in US East on PS4
|
||||||
|
/// Shorthand for `world(by: { id: 1000 }})`
|
||||||
|
pub async fn genudine(&self) -> World {
|
||||||
|
World::new(IdOrNameBy::Id(1000))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Ceres world in EU on PS4
|
||||||
|
/// Shorthand for `world(by: { id: 2000 }})`
|
||||||
|
pub async fn ceres(&self) -> World {
|
||||||
|
World::new(IdOrNameBy::Id(2000))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
132
services/api/src/zone.rs
Normal file
132
services/api/src/zone.rs
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
use crate::{
|
||||||
|
classes::Classes,
|
||||||
|
population::Population,
|
||||||
|
utils::{id_or_name_to_id, id_or_name_to_name, Filters, IdOrNameBy, ID_TO_ZONE, ZONE_IDS},
|
||||||
|
vehicles::Vehicles,
|
||||||
|
};
|
||||||
|
use async_graphql::Object;
|
||||||
|
|
||||||
|
/// An individual zone/continent.
|
||||||
|
pub struct Zone {
|
||||||
|
filters: Filters,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Zone {
|
||||||
|
pub fn new(filters: Option<Filters>) -> Self {
|
||||||
|
Self {
|
||||||
|
filters: filters.unwrap_or_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Zone {
|
||||||
|
/// The ID of the zone/continent.
|
||||||
|
async fn id(&self) -> i32 {
|
||||||
|
id_or_name_to_id(&ZONE_IDS, self.filters.zone.as_ref().unwrap()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The name of the continent, in official game capitalization.
|
||||||
|
async fn name(&self) -> String {
|
||||||
|
let name = id_or_name_to_name(&ID_TO_ZONE, self.filters.zone.as_ref().unwrap()).unwrap();
|
||||||
|
|
||||||
|
// Capitalize the first letter
|
||||||
|
name[0..1].to_uppercase() + &name[1..]
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn population(&self) -> Population {
|
||||||
|
Population::new(Some(self.filters.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn vehicles(&self) -> Vehicles {
|
||||||
|
Vehicles::new(Some(self.filters.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn classes(&self) -> Classes {
|
||||||
|
Classes::new(Some(self.filters.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Super-struct for querying zones/continents.
|
||||||
|
pub struct Zones {
|
||||||
|
filters: Filters,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Zones {
|
||||||
|
pub fn new(filters: Option<Filters>) -> Self {
|
||||||
|
Self {
|
||||||
|
filters: filters.unwrap_or_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Zones {
|
||||||
|
/// Every zone/continent individually.
|
||||||
|
async fn all(&self) -> Vec<Zone> {
|
||||||
|
ID_TO_ZONE
|
||||||
|
.iter()
|
||||||
|
.map(|(id, _)| {
|
||||||
|
Zone::new(Some(Filters {
|
||||||
|
world: self.filters.world.clone(),
|
||||||
|
faction: self.filters.faction.clone(),
|
||||||
|
zone: Some(IdOrNameBy::Id(*id)),
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn indar(&self) -> Zone {
|
||||||
|
Zone::new(Some(Filters {
|
||||||
|
world: self.filters.world.clone(),
|
||||||
|
faction: self.filters.faction.clone(),
|
||||||
|
zone: Some(IdOrNameBy::Id(2)),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn hossin(&self) -> Zone {
|
||||||
|
Zone::new(Some(Filters {
|
||||||
|
world: self.filters.world.clone(),
|
||||||
|
faction: self.filters.faction.clone(),
|
||||||
|
zone: Some(IdOrNameBy::Id(4)),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn amerish(&self) -> Zone {
|
||||||
|
Zone::new(Some(Filters {
|
||||||
|
world: self.filters.world.clone(),
|
||||||
|
faction: self.filters.faction.clone(),
|
||||||
|
zone: Some(IdOrNameBy::Id(6)),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn esamir(&self) -> Zone {
|
||||||
|
Zone::new(Some(Filters {
|
||||||
|
world: self.filters.world.clone(),
|
||||||
|
faction: self.filters.faction.clone(),
|
||||||
|
zone: Some(IdOrNameBy::Id(8)),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn oshur(&self) -> Zone {
|
||||||
|
Zone::new(Some(Filters {
|
||||||
|
world: self.filters.world.clone(),
|
||||||
|
faction: self.filters.faction.clone(),
|
||||||
|
zone: Some(IdOrNameBy::Id(344)),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ZoneQuery;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl ZoneQuery {
|
||||||
|
pub async fn zone(&self, filter: Option<Filters>) -> Zone {
|
||||||
|
Zone::new(filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn zones(&self, filter: Option<Filters>) -> Zones {
|
||||||
|
Zones::new(filter)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue