ascend/crates/ascend/src/models.rs
2025-02-19 21:49:34 +01:00

226 lines
6.1 KiB
Rust

//! Shared models between server and client code.
pub use v1::HoldPosition;
pub use v1::HoldRole;
pub use v2::Hold;
pub use v2::Image;
pub use v2::ImageFilename;
pub use v2::ImageResolution;
pub use v2::ImageUid;
pub use v2::Method;
pub use v2::Problem;
pub use v2::ProblemUid;
pub use v2::Root;
pub use v2::Wall;
pub use v2::WallDimensions;
pub use v2::WallUid;
pub mod v2 {
use super::v1;
use derive_more::Display;
use serde::Deserialize;
use serde::Serialize;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::fmt::Display;
#[derive(Serialize, Deserialize, Debug)]
pub struct Root {
pub walls: BTreeSet<WallUid>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Wall {
pub uid: WallUid,
// TODO: Replace by walldimensions
pub rows: u64,
pub cols: u64,
pub holds: BTreeMap<v1::HoldPosition, Hold>,
pub problems: BTreeSet<ProblemUid>,
}
impl Wall {
pub fn random_problem(&self) -> Option<ProblemUid> {
use rand::seq::IteratorRandom;
let mut rng = rand::rng();
self.problems.iter().choose(&mut rng).copied()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct WallDimensions {
pub rows: u64,
pub cols: u64,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, derive_more::FromStr, derive_more::Display)]
pub struct WallUid(pub uuid::Uuid);
impl WallUid {
pub fn create() -> Self {
Self(uuid::Uuid::new_v4())
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Problem {
pub uid: ProblemUid,
pub name: String,
pub set_by: String,
pub holds: BTreeMap<v1::HoldPosition, v1::HoldRole>,
pub method: Method,
pub date_added: chrono::DateTime<chrono::Utc>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, derive_more::FromStr, derive_more::Display)]
pub struct ProblemUid(pub uuid::Uuid);
impl ProblemUid {
pub fn create() -> Self {
Self(uuid::Uuid::new_v4())
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Display)]
pub enum Method {
#[display("Feet follow hands")]
FeetFollowHands,
#[display("Footless")]
Footless,
#[display("Footless plus kickboard")]
FootlessPlusKickboard,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Hold {
pub position: v1::HoldPosition,
pub image: Option<Image>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Image {
pub uid: ImageUid,
pub resolutions: BTreeMap<ImageResolution, ImageFilename>,
}
impl Image {
pub(crate) fn srcset(&self) -> String {
self.resolutions
.iter()
.map(|(res, filename)| format!("/files/holds/{} {}w", filename.filename, res.width))
.collect::<Vec<String>>()
.join(", ")
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct ImageResolution {
pub width: u64,
pub height: u64,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct ImageFilename {
pub filename: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, derive_more::FromStr, derive_more::Display)]
pub struct ImageUid(pub uuid::Uuid);
impl ImageUid {
pub fn create() -> Self {
Self(uuid::Uuid::new_v4())
}
}
}
pub mod v1 {
use serde::Deserialize;
use serde::Serialize;
use smart_default::SmartDefault;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
const STATE_VERSION: u64 = 1;
#[derive(Serialize, Deserialize, Clone, Debug, SmartDefault)]
pub struct PersistentState {
/// State schema version
#[default(STATE_VERSION)]
pub version: u64,
pub wall: Wall,
pub problems: Problems,
}
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct Problems {
pub problems: BTreeSet<Problem>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Wall {
pub rows: u64,
pub cols: u64,
pub holds: BTreeMap<HoldPosition, Hold>,
}
impl Wall {
pub fn new(rows: u64, cols: u64) -> Self {
let mut holds = BTreeMap::new();
for row in 0..rows {
for col in 0..cols {
let position = HoldPosition { row, col };
let hold = Hold { position, image: None };
holds.insert(position, hold);
}
}
Self { rows, cols, holds }
}
}
impl Default for Wall {
fn default() -> Self {
Self::new(12, 12)
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
pub struct HoldPosition {
/// Starting from 0
pub row: u64,
/// Starting from 0
pub col: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Problem {
pub holds: BTreeMap<HoldPosition, HoldRole>,
}
/// The role of a hold on a route
#[derive(Serialize, Deserialize, Clone, Debug, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum HoldRole {
/// Start hold
Start,
/// Any hold on the route without a specific role
Normal,
/// Zone hold
Zone,
/// End hold
End,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Hold {
pub position: HoldPosition,
pub image: Option<Image>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Image {
pub filename: String,
}
}