summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJomar Milan <jomarm@jomarm.com>2026-05-30 23:06:35 -0700
committerJomar Milan <jomarm@jomarm.com>2026-05-30 23:06:35 -0700
commit90d97e089d3b56e0e9efde3693f106d035bb88e8 (patch)
treee7e68fa9559d5da1e55338991bd293cb36ccbf5e /src
parent21be142ed526c5ec88c14c3ea6cad043a075a2b9 (diff)
Add route to accept player hand updates
Diffstat (limited to 'src')
-rw-r--r--src/main.rs84
-rw-r--r--src/session.rs32
-rw-r--r--src/template.rs10
3 files changed, 90 insertions, 36 deletions
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,
+}