diff options
author | Kjetil Orbekk <kj@orbekk.com> | 2022-11-15 12:25:25 -0500 |
---|---|---|
committer | Kjetil Orbekk <kj@orbekk.com> | 2022-11-15 12:25:25 -0500 |
commit | 6296e97fafd4bb5063541bee83061c398f31d19e (patch) | |
tree | acf9622d16df7d53cd42b4df0bd88b9721f78f8e | |
parent | 4c0109a8c40012f75e3d0d900c0ef41893cfb4bb (diff) |
Add journaling trait for game objects
-rw-r--r-- | protocol/src/lib.rs | 1 | ||||
-rw-r--r-- | server/migrations/20221008120534_init.down.sql | 2 | ||||
-rw-r--r-- | server/migrations/20221008120534_init.up.sql | 7 | ||||
-rw-r--r-- | server/src/error.rs | 3 | ||||
-rw-r--r-- | server/src/main.rs | 9 | ||||
-rw-r--r-- | server/src/play.rs | 56 |
6 files changed, 74 insertions, 4 deletions
diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index 4294e17..b10e7e1 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -14,4 +14,5 @@ pub struct Table { #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] pub struct TableView { + pub m: String, } diff --git a/server/migrations/20221008120534_init.down.sql b/server/migrations/20221008120534_init.down.sql index 385537a..0c9bbed 100644 --- a/server/migrations/20221008120534_init.down.sql +++ b/server/migrations/20221008120534_init.down.sql @@ -2,6 +2,8 @@ begin; drop table if exists sessions; drop table if exists table_players; +drop table if exists table_journal; drop table if exists active_tables; drop table if exists players; +drop type if exists player_position; commit; diff --git a/server/migrations/20221008120534_init.up.sql b/server/migrations/20221008120534_init.up.sql index 8c53bb8..e4b9eb1 100644 --- a/server/migrations/20221008120534_init.up.sql +++ b/server/migrations/20221008120534_init.up.sql @@ -16,6 +16,13 @@ create table active_tables ( id uuid primary key not null ); +create table table_journal ( + table_id uuid references active_tables(id) not null, + seq bigint not null, + payload jsonb +); +create unique index journal_entry on table_journal (table_id, seq); + create type player_position as enum ('west', 'north', 'east', 'south'); create table table_players ( diff --git a/server/src/error.rs b/server/src/error.rs index 9d82a54..cbd00c3 100644 --- a/server/src/error.rs +++ b/server/src/error.rs @@ -46,6 +46,9 @@ pub enum BridgeError { #[error("Duration out of range")] DurationOutOfRange(#[from] time::OutOfRangeError), + + #[error("Sequence number mismatch: {0} ≠ {1}")] + UpdateConflict(i64, i64), } impl BridgeError { diff --git a/server/src/main.rs b/server/src/main.rs index 9761afe..9b5adb7 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -17,6 +17,7 @@ use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; mod auth; mod error; mod server; +mod play; use crate::error::BridgeError; use crate::{ auth::{Authenticator, SessionId}, @@ -61,7 +62,7 @@ async fn main() { .route("/api/user/info", get(user_info)) .route("/api/table", post(create_table)) .route("/api/table", delete(leave_table)) - .route("/api/table/:id", get(get_table_state)) + .route("/api/table/:id", get(get_table_view)) // .route("/api/user/table", get(user_table)) .route("/api/login", get(login)) .route(auth::LOGIN_CALLBACK, get(login_callback)) @@ -75,13 +76,13 @@ async fn main() { .unwrap(); } -async fn get_table_state( +async fn get_table_view( session: AuthenticatedSession, extension: ContextExtension, Path(id): Path<Uuid> -) -> Result<(), BridgeError> { +) -> Result<Json<protocol::TableView>, BridgeError> { info!("Getting table state for table {id:}"); - Ok(()) + Ok(Json(protocol::TableView { m: format!("hello") })) } async fn leave_table( diff --git a/server/src/play.rs b/server/src/play.rs new file mode 100644 index 0000000..8fe1872 --- /dev/null +++ b/server/src/play.rs @@ -0,0 +1,56 @@ +use async_trait::async_trait; + +use crate::error::BridgeError; + +#[async_trait] +pub trait Journal { + // Append payload to the journal at sequence number `seq`. + async fn append(&mut self, seq: i64, payload: serde_json::Value) -> Result<(), BridgeError>; + + // Fetch all journal entries with sequence number greater or equal to `seq`. + async fn replay(&mut self, seq: i64) -> Result<Vec<serde_json::Value>, BridgeError>; +} + +pub struct Table {} + +#[cfg(test)] +mod test { + use super::*; + + #[derive(Default)] + pub struct TestJournal { + log: Vec<Option<serde_json::Value>>, + } + + #[async_trait] + impl Journal for TestJournal { + async fn append( + &mut self, + seq: i64, + payload: serde_json::Value, + ) -> Result<(), BridgeError> { + if seq != self.log.len() as i64 { + return Err(BridgeError::UpdateConflict(self.log.len() as i64, seq)); + } + self.log.push(Some(payload)); + Ok(()) + } + + async fn replay(&mut self, seq: i64) -> Result<Vec<serde_json::Value>, BridgeError> { + Ok(self.log[seq as usize..] + .into_iter() + .filter_map(|e| e.clone()) + .collect()) + } + } + + #[tokio::test] + async fn test_journal() { + use serde_json::json; + let mut jnl: TestJournal = Default::default(); + assert_eq!(jnl.append(0, json!(10)).await.unwrap(), ()); + assert!(jnl.append(0, json!(0)).await.is_err()); + assert_eq!(jnl.append(1, json!(20)).await.unwrap(), ()); + assert_eq!(jnl.replay(1).await.unwrap(), vec!(json!(20))); + } +} |