diff --git a/sync-server/src/main.rs b/sync-server/src/main.rs index 880ce7b27..a8e4cc9de 100644 --- a/sync-server/src/main.rs +++ b/sync-server/src/main.rs @@ -1,6 +1,6 @@ use actix_web::{App, HttpServer}; use api::ServerState; -use server::{NullSyncServer, SyncServer}; +use server::{InMemorySyncServer, SyncServer}; mod api; mod server; @@ -9,7 +9,7 @@ mod server; #[actix_web::main] async fn main() -> std::io::Result<()> { - let server_box: Box = Box::new(NullSyncServer::new()); + let server_box: Box = Box::new(InMemorySyncServer::new()); let server_state = ServerState::new(server_box); HttpServer::new(move || { diff --git a/sync-server/src/server/inmemory.rs b/sync-server/src/server/inmemory.rs new file mode 100644 index 000000000..37302282e --- /dev/null +++ b/sync-server/src/server/inmemory.rs @@ -0,0 +1,108 @@ +use super::{ + AddVersionResult, ClientId, GetVersionResult, HistorySegment, SyncServer, VersionId, + NO_VERSION_ID, +}; +use failure::Fallible; +use std::collections::HashMap; +use std::sync::{Mutex, RwLock}; +use taskchampion::Uuid; + +/// An in-memory server backend that can be useful for testing. +pub(crate) struct InMemorySyncServer { + clients: RwLock>>, +} + +struct Version { + version_id: VersionId, + history_segment: HistorySegment, +} + +struct Client { + latest_version_id: VersionId, + // NOTE: indexed by parent_version_id! + versions: HashMap, +} + +impl InMemorySyncServer { + pub(crate) fn new() -> Self { + Self { + clients: RwLock::new(HashMap::new()), + } + } +} + +impl SyncServer for InMemorySyncServer { + fn get_child_version( + &self, + client_id: ClientId, + parent_version_id: VersionId, + ) -> Fallible> { + let clients = self.clients.read().expect("poisoned lock"); + if let Some(client) = clients.get(&client_id) { + let client = client.lock().expect("poisoned lock"); + if let Some(version) = client.versions.get(&parent_version_id) { + return Ok(Some(GetVersionResult { + version_id: version.version_id, + parent_version_id, + history_segment: version.history_segment.clone(), + })); + } + } + Ok(None) + } + + fn add_version( + &self, + client_id: ClientId, + parent_version_id: VersionId, + history_segment: HistorySegment, + ) -> Fallible { + let mut clients = self.clients.write().expect("poisoned lock"); + if let Some(client) = clients.get_mut(&client_id) { + let mut client = client.lock().expect("poisoned lock"); + if client.latest_version_id != NO_VERSION_ID { + if parent_version_id != client.latest_version_id { + return Ok(AddVersionResult::ExpectedParentVersion( + client.latest_version_id, + )); + } + } + + // invent a new ID for this version + let version_id = Uuid::new_v4(); + + client.versions.insert( + parent_version_id, + Version { + version_id, + history_segment, + }, + ); + client.latest_version_id = version_id; + + Ok(AddVersionResult::Ok(version_id)) + } else { + // new client, so insert a client with just this new version + + let latest_version_id = Uuid::new_v4(); + let mut versions = HashMap::new(); + versions.insert( + parent_version_id, + Version { + version_id: latest_version_id, + history_segment, + }, + ); + + clients.insert( + client_id, + Mutex::new(Client { + latest_version_id, + versions, + }), + ); + + Ok(AddVersionResult::Ok(latest_version_id)) + } + } +} diff --git a/sync-server/src/server/mod.rs b/sync-server/src/server/mod.rs index 5bda024f0..6d8593c03 100644 --- a/sync-server/src/server/mod.rs +++ b/sync-server/src/server/mod.rs @@ -1,6 +1,13 @@ use failure::Fallible; use taskchampion::Uuid; +mod inmemory; + +pub(crate) use inmemory::InMemorySyncServer; + +/// The distinguished value for "no version" +pub const NO_VERSION_ID: VersionId = Uuid::nil(); + pub(crate) type HistorySegment = Vec; pub(crate) type ClientId = Uuid; pub(crate) type VersionId = Uuid; @@ -33,38 +40,3 @@ pub(crate) trait SyncServer: Sync + Send { history_segment: HistorySegment, ) -> Fallible; } - -// TODO: temporary -/// A "null" sync server's implementation; HTTP API methods call through to methods on a single -/// instance of this type. -pub(crate) struct NullSyncServer {} - -impl NullSyncServer { - pub(crate) fn new() -> Self { - Self {} - } -} - -impl SyncServer for NullSyncServer { - fn get_child_version( - &self, - _client_id: ClientId, - parent_version_id: VersionId, - ) -> Fallible> { - Ok(Some(GetVersionResult { - version_id: Uuid::new_v4(), - parent_version_id, - history_segment: b"abcd".to_vec(), - })) - } - - fn add_version( - &self, - _client_id: ClientId, - _parent_version_id: VersionId, - _history_segment: HistorySegment, - ) -> Fallible { - //Ok(AddVersionResult::Ok(Uuid::new_v4())) - Ok(AddVersionResult::ExpectedParentVersion(Uuid::new_v4())) - } -}