From a2e55df0eeea9709175dd0a26c1e09bdaa60841a Mon Sep 17 00:00:00 2001 From: Jomar Milan Date: Sun, 31 May 2026 12:22:41 -0700 Subject: Add websocket route --- src/main.rs | 44 +++++++++++++++++++++++++++++++------------- src/session.rs | 6 ++++++ src/template.rs | 1 + 3 files changed, 38 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/main.rs b/src/main.rs index ec09da2..32e5d58 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,13 +4,13 @@ mod 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::extract::ws::WebSocket; +use axum::extract::{Path, Query, State, WebSocketUpgrade}; +use axum::http::{StatusCode, header}; use axum::response::{Html, IntoResponse, Response}; -use axum::routing::{get, put}; +use axum::routing::{any, get}; use axum::{Json, Router}; use rust_embed::Embed; -use serde::Deserialize; use std::collections::HashMap; use std::net::SocketAddr; use std::sync::{Arc, Mutex}; @@ -18,7 +18,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; #[derive(Embed)] #[folder = "assets/"] -struct Asset; +struct EmbedAsset; struct AppState { sessions: Mutex>, @@ -38,7 +38,8 @@ 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)) + .route("/session/{id}/hands", get(serve_hands).put(update_hands)) + .route("/session/{id}/play", any(upgrade_play)) .with_state(Arc::new(AppState::new())); let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); @@ -64,9 +65,9 @@ async fn serve_index(State(state): State>) -> Response { } async fn serve_static(Path(path): Path) -> Response { - match Asset::get(path.as_str()) { + match EmbedAsset::get(path.as_str()) { Some(content) => { - let mime = match path.split('.').last() { + let mime = match path.split('.').next_back() { Some("js") => "application/javascript", _ => "application/octet-stream", }; @@ -88,7 +89,7 @@ async fn visit_session( match sessions.get(&id) { Some(session) => match passcode { Some(passcode) if passcode.as_str() == session.passcode => { - serve_template(SessionTemplate { session: &session }) + serve_template(SessionTemplate { id: &id, session }) } _ => (StatusCode::FORBIDDEN, "Incorrect session passcode").into_response(), }, @@ -101,10 +102,7 @@ async fn create_session( Query(query): Query>, State(state): State>, ) -> Response { - let name = query - .get("name") - .map(|name| name.clone()) - .unwrap_or("Unknown".to_string()); + let name = query.get("name").cloned().unwrap_or("Unknown".to_string()); let passcode = SystemTime::now() .duration_since(UNIX_EPOCH) .map(|duration| duration.subsec_nanos()) @@ -119,6 +117,15 @@ async fn create_session( (StatusCode::CREATED, passcode).into_response() } +async fn serve_hands(Path(id): Path, State(state): State>) -> Response { + let sessions = state.sessions.lock().unwrap(); + + match sessions.get(&id) { + Some(session) => Json(session.hands.keys().collect::>()).into_response(), + None => (StatusCode::NOT_FOUND, "Session does not exist").into_response(), + } +} + async fn update_hands( Path(id): Path, State(state): State>, @@ -134,3 +141,14 @@ async fn update_hands( None => (StatusCode::NOT_FOUND, "Session does not exist").into_response(), } } + +async fn upgrade_play(ws: WebSocketUpgrade, State(state): State>) -> Response { + ws.on_upgrade(|socket| handle_play(socket, state)) +} + +#[allow(unused_variables)] +async fn handle_play(mut socket: WebSocket, state: Arc) { + while let Some(msg) = socket.recv().await { + todo!() + } +} diff --git a/src/session.rs b/src/session.rs index 11dd5b8..c105af3 100644 --- a/src/session.rs +++ b/src/session.rs @@ -7,12 +7,18 @@ pub struct Session { pub hands: HashMap>, } +// TODO: The values on these variants will be used in the future and there will be more variants. +// Once this happens, the dead_code lint should no longer be suppressed. #[derive(Deserialize)] +#[allow(dead_code)] pub enum HandObject { CustomDeck(CustomDeck), } +// TODO: These fields will be used in the future. When they are, the dead_code lint should no longer +// be suppressed. #[derive(Deserialize)] +#[allow(dead_code)] pub struct CustomDeck { /// The path/URL of the face cardsheet. face: String, diff --git a/src/template.rs b/src/template.rs index 5424883..8a36a09 100644 --- a/src/template.rs +++ b/src/template.rs @@ -11,5 +11,6 @@ pub struct IndexTemplate<'a> { #[derive(Template)] #[template(path = "session.html")] pub struct SessionTemplate<'a> { + pub id: &'a String, pub session: &'a Session, } -- cgit v1.2.3