starchart/src/stars.rs

139 lines
3.8 KiB
Rust

use std::hash::Hash;
use aide::axum::{
routing::{get_with, post_with},
ApiRouter,
};
use axum::{extract::Query, http::StatusCode, Json};
use cached::proc_macro::cached;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
mod description;
mod kind;
mod names;
mod position;
mod size;
pub fn routes() -> ApiRouter {
ApiRouter::new()
.api_route(
"/discover",
post_with(discover, |docs| {
docs.response::<200, Json<Star>>()
.summary("add a new planet")
.id("discover")
.tag("stars")
}),
)
.api_route(
"/chart",
get_with(chart, |docs| {
docs.response::<200, Json<Vec<Protostar>>>()
.summary("the whole chart")
.id("chart")
.tag("stars")
}),
)
.api_route_with(
"/visit/",
get_with(visit, |docs| {
docs.response::<200, Json<Star>>()
.summary("visit a single planet")
.id("visit")
.tag("stars")
}),
|docs| docs,
)
}
/// this gets stored in the db,
/// the other stuff is generated using
/// the id as a seed
#[derive(Serialize, FromRow, Clone, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
pub struct Protostar {
pub id: i32,
/// hex code of the stars color
pub color: String,
}
#[derive(Serialize, Deserialize, JsonSchema, Clone)]
pub struct Star {
pub core: Protostar,
pub position: position::Position,
pub kind: kind::Kind,
pub name: String,
pub description: String,
}
#[derive(Serialize, Deserialize, JsonSchema)]
pub struct DiscoveryLog {
pub color: String,
}
impl Into<Star> for Protostar {
fn into(self) -> Star {
star_from_seed(self.id.abs() as u64, self.clone())
}
}
#[cached]
fn star_from_seed(seed: u64, protostar: Protostar) -> Star {
let mut rng = fastrand::Rng::with_seed(seed);
let range = 0..u64::MAX;
let kind = kind::Kind::random(rng.u64(range.clone()));
Star {
core: protostar.clone(),
position: position::Position::random(rng.u64(range.clone())),
name: format!("{}-{}", names::random_name(rng.u64(range.clone())), seed,).into(),
description: protostar.generate_description(&kind, rng.u64(range.clone())),
kind,
}
}
/// discover a new star in the solar system
async fn discover(Json(log): Json<DiscoveryLog>) -> Result<Json<Star>, StatusCode> {
let query = "INSERT INTO stars (color) VALUES ($1) RETURNING id, color";
let protostar: Protostar = sqlx::query_as(query)
.bind(log.color)
.fetch_one(crate::db::pool().await)
.await
.or(Err(StatusCode::INTERNAL_SERVER_ERROR))?;
let star: Star = protostar.into();
Ok(Json(star))
}
/// show all stars
async fn chart() -> Json<Vec<Star>> {
let query = "SELECT * FROM stars ORDER BY id DESC";
let protostars: Vec<Protostar> = sqlx::query_as(query)
.fetch_all(crate::db::pool().await)
.await
.unwrap_or_default();
Json(
protostars
.iter()
.map(|proto| Into::<Star>::into(proto.clone()))
.collect::<Vec<Star>>(),
)
}
#[derive(Serialize, Deserialize, JsonSchema)]
struct VisitorData {
/// the planet to visit
planet_id: i32,
}
async fn visit(data: Query<VisitorData>) -> Result<Json<Star>, StatusCode> {
let query = "SELECT * FROM stars WHERE id = ($1)";
let star: Protostar = sqlx::query_as(query)
.bind(data.planet_id)
.fetch_one(crate::db::pool().await)
.await
.or(Err(StatusCode::NOT_FOUND))?;
let star: Star = star.into();
Ok(Json(star))
}