diff options
| author | Jomar Milan <jomarm@jomarm.com> | 2026-05-30 23:06:35 -0700 |
|---|---|---|
| committer | Jomar Milan <jomarm@jomarm.com> | 2026-05-30 23:06:35 -0700 |
| commit | 90d97e089d3b56e0e9efde3693f106d035bb88e8 (patch) | |
| tree | e7e68fa9559d5da1e55338991bd293cb36ccbf5e | |
| parent | 21be142ed526c5ec88c14c3ea6cad043a075a2b9 (diff) | |
Add route to accept player hand updates
| -rw-r--r-- | Cargo.lock | 2 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/main.rs | 84 | ||||
| -rw-r--r-- | src/session.rs | 32 | ||||
| -rw-r--r-- | src/template.rs | 10 | ||||
| -rw-r--r-- | templates/index.html | 4 |
6 files changed, 95 insertions, 38 deletions
@@ -497,6 +497,7 @@ dependencies = [ "askama", "axum", "rust-embed", + "serde", "tokio", ] @@ -528,6 +529,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", + "serde_derive", ] [[package]] @@ -7,4 +7,5 @@ edition = "2024" askama = "0.16.0" axum = "0.8.9" rust-embed = "8.11.0" +serde = { version = "1.0.228", features = ["derive"] } tokio = { version = "1.52.3", features = ["full"] } diff --git a/src/main.rs b/src/main.rs index 82c4f19..ec09da2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,32 +1,33 @@ mod session; mod template; -use std::collections::HashMap; -use std::net::SocketAddr; -use std::sync::{Arc, Mutex}; -use std::time::{SystemTime, UNIX_EPOCH}; -use askama::{Template}; +use crate::session::{HandObject, Session}; +use crate::template::{IndexTemplate, SessionTemplate}; +use askama::Template; use axum::extract::{Path, Query, State}; use axum::http::{header, StatusCode}; use axum::response::{Html, IntoResponse, Response}; -use axum::Router; -use axum::routing::{get}; +use axum::routing::{get, put}; +use axum::{Json, Router}; use rust_embed::Embed; -use crate::session::{Session}; -use crate::template::{IndexTemplate, SessionTemplate}; +use serde::Deserialize; +use std::collections::HashMap; +use std::net::SocketAddr; +use std::sync::{Arc, Mutex}; +use std::time::{SystemTime, UNIX_EPOCH}; #[derive(Embed)] #[folder = "assets/"] struct Asset; struct AppState { - sessions: Mutex<HashMap<String, Session>> + sessions: Mutex<HashMap<String, Session>>, } impl AppState { fn new() -> Self { AppState { - sessions: Mutex::new(HashMap::new()) + sessions: Mutex::new(HashMap::new()), } } } @@ -37,6 +38,7 @@ async fn main() { .route("/", get(serve_index)) .route("/dist/{*path}", get(serve_static)) .route("/session/{id}", get(visit_session).put(create_session)) + .route("/session/{id}/hands", put(update_hands)) .with_state(Arc::new(AppState::new())); let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); @@ -57,7 +59,7 @@ fn serve_template(template: impl Template) -> Response { async fn serve_index(State(state): State<Arc<AppState>>) -> Response { let sessions = state.sessions.lock().unwrap(); serve_template(IndexTemplate { - sessions: &sessions + sessions: &sessions, }) } @@ -66,36 +68,48 @@ async fn serve_static(Path(path): Path<String>) -> Response { Some(content) => { let mime = match path.split('.').last() { Some("js") => "application/javascript", - _ => "application/octet-stream" + _ => "application/octet-stream", }; ([(header::CONTENT_TYPE, mime)], content.data).into_response() } - None => (StatusCode::NOT_FOUND, "404 Not Found").into_response() + None => (StatusCode::NOT_FOUND, "404 Not Found").into_response(), } } -async fn visit_session(Path(id): Path<String>, Query(query): Query<HashMap<String, String>>, State(state): State<Arc<AppState>>) -> Response { +async fn visit_session( + Path(id): Path<String>, + Query(query): Query<HashMap<String, String>>, + State(state): State<Arc<AppState>>, +) -> Response { let passcode = query.get("passcode"); let sessions = state.sessions.lock().unwrap(); match sessions.get(&id) { - Some(session) => { - match passcode { - Some(passcode) if passcode.as_str() == session.passcode => - serve_template(SessionTemplate { - session: &session - }), - _ => (StatusCode::FORBIDDEN, "Incorrect session passcode").into_response() + Some(session) => match passcode { + Some(passcode) if passcode.as_str() == session.passcode => { + serve_template(SessionTemplate { session: &session }) } + _ => (StatusCode::FORBIDDEN, "Incorrect session passcode").into_response(), }, - None => (StatusCode::NOT_FOUND, "Session does not exist").into_response() + None => (StatusCode::NOT_FOUND, "Session does not exist").into_response(), } } -async fn create_session(Path(id): Path<String>, Query(query): Query<HashMap<String, String>>, State(state): State<Arc<AppState>>) -> Response { - let name = query.get("name").map(|name| name.clone()).unwrap_or("Unknown".to_string()); - let passcode = SystemTime::now().duration_since(UNIX_EPOCH).map(|duration| duration.subsec_nanos()).unwrap_or(675603000).to_string(); +async fn create_session( + Path(id): Path<String>, + Query(query): Query<HashMap<String, String>>, + State(state): State<Arc<AppState>>, +) -> Response { + let name = query + .get("name") + .map(|name| name.clone()) + .unwrap_or("Unknown".to_string()); + let passcode = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.subsec_nanos()) + .unwrap_or(675603000) + .to_string(); let mut sessions = state.sessions.lock().unwrap(); @@ -103,4 +117,20 @@ async fn create_session(Path(id): Path<String>, Query(query): Query<HashMap<Stri sessions.insert(id, session); (StatusCode::CREATED, passcode).into_response() -}
\ No newline at end of file +} + +async fn update_hands( + Path(id): Path<String>, + State(state): State<Arc<AppState>>, + Json(payload): Json<HashMap<String, Vec<HandObject>>>, +) -> Response { + let mut sessions = state.sessions.lock().unwrap(); + + match sessions.get_mut(&id) { + Some(session) => { + session.hands = payload; + (StatusCode::NO_CONTENT, ()).into_response() + } + None => (StatusCode::NOT_FOUND, "Session does not exist").into_response(), + } +} diff --git a/src/session.rs b/src/session.rs index cac100e..11dd5b8 100644 --- a/src/session.rs +++ b/src/session.rs @@ -1,12 +1,36 @@ +use serde::Deserialize; use std::collections::HashMap; pub struct Session { pub steam_name: String, pub passcode: String, - pub hands: HashMap<String, Hand> + pub hands: HashMap<String, Vec<HandObject>>, } -pub struct Hand { +#[derive(Deserialize)] +pub enum HandObject { + CustomDeck(CustomDeck), +} + +#[derive(Deserialize)] +pub struct CustomDeck { + /// The path/URL of the face cardsheet. + face: String, + /// The path/URL of the back cardsheet or card back. + back: String, + /// If each card has a unique card back (via a cardsheet). + unique_back: bool, + /// The number of columns on the cardsheet. + width: f64, + /// The number of rows on the cardsheet. + height: f64, + /// The number of cards on the cardsheet. + number: f64, + /// Whether the cards are horizontal, instead of vertical. + sideways: bool, + /// Whether the card back should be used as the hidden image (instead of the last slot of the + /// `face` image). + back_is_hidden: bool, } impl Session { @@ -14,7 +38,7 @@ impl Session { Session { steam_name, passcode, - hands: HashMap::new() + hands: HashMap::new(), } } -}
\ No newline at end of file +} diff --git a/src/template.rs b/src/template.rs index c02a3df..5424883 100644 --- a/src/template.rs +++ b/src/template.rs @@ -1,15 +1,15 @@ -use std::collections::HashMap; -use askama::Template; use crate::session::Session; +use askama::Template; +use std::collections::HashMap; #[derive(Template)] #[template(path = "index.html")] pub struct IndexTemplate<'a> { - pub sessions: &'a HashMap<String, Session> + pub sessions: &'a HashMap<String, Session>, } #[derive(Template)] #[template(path = "session.html")] pub struct SessionTemplate<'a> { - pub session: &'a Session -}
\ No newline at end of file + pub session: &'a Session, +} diff --git a/templates/index.html b/templates/index.html index 78d60e9..b2c777f 100644 --- a/templates/index.html +++ b/templates/index.html @@ -7,7 +7,7 @@ <body> <h1>Sessions</h1> <ul> -{% for (id, session) in sessions %} + {% for (id, session) in sessions %} <li> <span>{{session.steam_name}}'s game ({{id}})</span> <form action="/session/{{id}}"> @@ -15,7 +15,7 @@ <input type="submit"> </form> </li> -{% endfor %} + {% endfor %} </ul> </body> </html>
\ No newline at end of file |
