summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml1
-rw-r--r--src/main.rs84
-rw-r--r--src/session.rs32
-rw-r--r--src/template.rs10
-rw-r--r--templates/index.html4
6 files changed, 95 insertions, 38 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 7a3a826..2da6bb1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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]]
diff --git a/Cargo.toml b/Cargo.toml
index bb840e6..593b989 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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