From 8256550cc0e98055e5a270556819da75961b8e9a Mon Sep 17 00:00:00 2001 From: austaras Date: Fri, 14 Oct 2022 18:06:34 +0800 Subject: [PATCH] let workspace own doc --- apps/keck/src/server/api/blocks/block.rs | 72 ++-- apps/keck/src/server/api/blocks/workspace.rs | 47 ++- apps/keck/src/server/api/mod.rs | 7 +- apps/keck/src/server/collaboration.rs | 20 +- apps/keck/src/server/utils/mod.rs | 20 +- apps/keck/src/sync/pool.rs | 8 +- libs/jwst-ffi/src/lib.rs | 28 +- libs/jwst/src/block.rs | 337 +++++++++---------- libs/jwst/src/workspace.rs | 115 +++++-- 9 files changed, 341 insertions(+), 313 deletions(-) diff --git a/apps/keck/src/server/api/blocks/block.rs b/apps/keck/src/server/api/blocks/block.rs index 017226df6..81f32c23a 100644 --- a/apps/keck/src/server/api/blocks/block.rs +++ b/apps/keck/src/server/api/blocks/block.rs @@ -1,5 +1,5 @@ use super::*; -use jwst::{BlockHistory, InsertChildren, RemoveChildren, Workspace}; +use jwst::{BlockHistory, InsertChildren, RemoveChildren}; #[utoipa::path( get, @@ -22,11 +22,9 @@ pub async fn get_block( ) -> impl IntoResponse { let (workspace, block) = params; info!("get_block: {}, {}", workspace, block); - if let Some(doc) = context.doc.get(&workspace) { - let doc = doc.value().lock().await; - let mut trx = doc.transact(); - let workspace = Workspace::new(&mut trx, workspace); - if let Some(block) = workspace.get(block, doc.client_id) { + if let Some(workspace) = context.workspace.get(&workspace) { + let workspace = workspace.value().lock().await; + if let Some(block) = workspace.get(block) { Json(block).into_response() } else { StatusCode::NOT_FOUND.into_response() @@ -64,19 +62,20 @@ pub async fn set_block( ) -> impl IntoResponse { let (workspace, block) = params; info!("set_block: {}, {}", workspace, block); - if let Some(doc) = context.doc.get(&workspace) { + if let Some(workspace) = context.workspace.get(&workspace) { // init block instance - let doc = doc.value().lock().await; - let mut trx = doc.transact(); - let workspace = Workspace::new(&mut trx, workspace); - let mut block = workspace.create(&mut trx, &block, "text", doc.client_id); - + let workspace = workspace.lock().await; // set block content - if let Some(block_content) = payload.as_object() { - for (key, value) in block_content.iter() { - block.set(&mut trx, key, value.clone()); + let block = workspace.with_trx(|mut t| { + let mut block = t.create(&block, "text"); + // set block content + if let Some(block_content) = payload.as_object() { + for (key, value) in block_content.iter() { + block.set(&mut t.trx, key, value.clone()); + } } - } + block + }); // response block content Json(block.block().to_json()).into_response() @@ -107,12 +106,10 @@ pub async fn get_block_history( ) -> impl IntoResponse { let (workspace, block) = params; info!("get_block_history: {}, {}", workspace, block); - if let Some(doc) = context.doc.get(&workspace) { + if let Some(workspace) = context.workspace.get(&workspace) { // init block instance - let doc = doc.value().lock().await; - let mut trx = doc.transact(); - let workspace = Workspace::new(&mut trx, workspace); - if let Some(block) = workspace.get(block, doc.client_id) { + let workspace = workspace.value().lock().await; + if let Some(block) = workspace.get(block) { Json(&block.history()).into_response() } else { StatusCode::NOT_FOUND.into_response() @@ -143,12 +140,9 @@ pub async fn delete_block( ) -> impl IntoResponse { let (workspace, block) = params; info!("delete_block: {}, {}", workspace, block); - if let Some(doc) = context.doc.get(&workspace) { - let doc = doc.value().lock().await; - let mut trx = doc.transact(); - let workspace = Workspace::new(&mut trx, workspace); - if workspace.remove(&mut trx, &block) { - trx.commit(); + if let Some(workspace) = context.workspace.get(&workspace) { + let workspace = workspace.value().lock().await; + if workspace.get_trx().remove(&block) { StatusCode::NO_CONTENT } else { StatusCode::NOT_FOUND @@ -186,13 +180,13 @@ pub async fn insert_block( ) -> impl IntoResponse { let (workspace, block) = params; info!("insert_block: {}, {}", workspace, block); - if let Some(doc) = context.doc.get(&workspace) { + if let Some(workspace) = context.workspace.get(&workspace) { // init block instance - let doc = doc.value().lock().await; - let mut trx = doc.transact(); - let workspace = Workspace::new(&mut trx, workspace); - if let Some(mut block) = workspace.get(block, doc.client_id) { - block.insert_children(&mut trx, payload); + let workspace = workspace.value().lock().await; + if let Some(mut block) = workspace.get(block) { + workspace.with_trx(|mut t| { + block.insert_children(&mut t.trx, payload); + }); // response block content Json(block.block().to_json()).into_response() } else { @@ -231,13 +225,13 @@ pub async fn remove_block( ) -> impl IntoResponse { let (workspace, block) = params; info!("insert_block: {}, {}", workspace, block); - if let Some(doc) = context.doc.get(&workspace) { + if let Some(workspace) = context.workspace.get(&workspace) { // init block instance - let doc = doc.value().lock().await; - let mut trx = doc.transact(); - let workspace = Workspace::new(&mut trx, workspace); - if let Some(mut block) = workspace.get(&block, doc.client_id) { - block.remove_children(&mut trx, payload); + let workspace = workspace.value().lock().await; + if let Some(mut block) = workspace.get(&block) { + workspace.with_trx(|mut t| { + block.remove_children(&mut t.trx, payload); + }); // response block content Json(block.block().to_json()).into_response() } else { diff --git a/apps/keck/src/server/api/blocks/workspace.rs b/apps/keck/src/server/api/blocks/workspace.rs index 6fe12e344..45479d1c3 100644 --- a/apps/keck/src/server/api/blocks/workspace.rs +++ b/apps/keck/src/server/api/blocks/workspace.rs @@ -1,6 +1,5 @@ use super::*; use axum::{extract::Path, http::header}; -use jwst::Workspace; #[utoipa::path( get, @@ -22,11 +21,9 @@ pub async fn get_workspace( info!("get_workspace: {}", workspace); utils::init_doc(context.clone(), &workspace).await; - if let Some(doc) = context.doc.get(&workspace) { - let doc = doc.value().lock().await; - let mut trx = doc.transact(); - let workspace = Workspace::new(&mut trx, workspace); - Json(workspace).into_response() + if let Some(workspace) = context.workspace.get(&workspace) { + let workspace = workspace.lock().await; + Json(&*workspace).into_response() } else { StatusCode::NOT_FOUND.into_response() } @@ -52,10 +49,10 @@ pub async fn set_workspace( utils::init_doc(context.clone(), &workspace).await; - let doc = context.doc.get(&workspace).unwrap(); - let doc = doc.lock().await; + let workspace = context.workspace.get(&workspace).unwrap(); + let workspace = workspace.lock().await; - Json(doc.transact().get_map("blocks").to_json()).into_response() + Json(workspace.doc().transact().get_map("blocks").to_json()).into_response() } #[utoipa::path( @@ -76,7 +73,7 @@ pub async fn delete_workspace( Path(workspace): Path, ) -> impl IntoResponse { info!("delete_workspace: {}", workspace); - if context.doc.remove(&workspace).is_none() { + if context.workspace.remove(&workspace).is_none() { return StatusCode::NOT_FOUND; } if let Err(_) = context.db.drop(&workspace).await { @@ -103,13 +100,9 @@ pub async fn workspace_client( Extension(context): Extension>, Path(workspace): Path, ) -> impl IntoResponse { - if let Some(doc) = context.doc.get(&workspace) { - let doc = doc.lock().await; - ( - [(header::CONTENT_TYPE, "application/json")], - doc.client_id.to_string(), - ) - .into_response() + if let Some(workspace) = context.workspace.get(&workspace) { + let workspace = workspace.lock().await; + Json(workspace.client_id()).into_response() } else { StatusCode::NOT_FOUND.into_response() } @@ -132,12 +125,10 @@ pub async fn history_workspace_clients( Extension(context): Extension>, Path(workspace): Path, ) -> impl IntoResponse { - if let Some(doc) = context.doc.get(&workspace) { - let doc = doc.lock().await; - if let Some(json) = - parse_history_client(&doc).and_then(|clients| serde_json::to_string(&clients).ok()) - { - ([(header::CONTENT_TYPE, "application/json")], json).into_response() + if let Some(workspace) = context.workspace.get(&workspace) { + let workspace = workspace.lock().await; + if let Some(history) = parse_history_client(workspace.doc()) { + Json(history).into_response() } else { StatusCode::INTERNAL_SERVER_ERROR.into_response() } @@ -166,11 +157,11 @@ pub async fn history_workspace( Path(params): Path<(String, String)>, ) -> impl IntoResponse { let (workspace, client) = params; - if let Some(doc) = context.doc.get(&workspace) { - let doc = doc.lock().await; + if let Some(workspace) = context.workspace.get(&workspace) { + let workspace = workspace.lock().await; if let Ok(client) = client.parse::() { - if let Some(json) = - parse_history(&doc, client).and_then(|history| serde_json::to_string(&history).ok()) + if let Some(json) = parse_history(workspace.doc(), client) + .and_then(|history| serde_json::to_string(&history).ok()) { ([(header::CONTENT_TYPE, "application/json")], json).into_response() } else { @@ -206,7 +197,7 @@ mod test { let resp = client.post("/block/test").send().await; assert_eq!(resp.status(), StatusCode::OK); - assert_eq!(resp.text().await, "{}"); + assert_eq!(resp.text().await, "{\"content\":{},\"updated\":{}}"); let resp = client.get("/block/test").send().await; assert_eq!(resp.status(), StatusCode::OK); diff --git a/apps/keck/src/server/api/mod.rs b/apps/keck/src/server/api/mod.rs index c88c2af3f..26556ebe6 100644 --- a/apps/keck/src/server/api/mod.rs +++ b/apps/keck/src/server/api/mod.rs @@ -11,13 +11,12 @@ use axum::{ Router, }; use dashmap::DashMap; -use jwst::{parse_history, parse_history_client, RawHistory}; +use jwst::{parse_history, parse_history_client, RawHistory, Workspace}; use serde_json::Value as JsonValue; use tokio::sync::{mpsc::Sender, Mutex}; -use yrs::Doc; pub struct Context { - pub doc: DashMap>, + pub workspace: DashMap>, pub storage: DashMap>, pub channel: DashMap<(String, String), Sender>, pub db: DbPool, @@ -26,7 +25,7 @@ pub struct Context { impl Context { pub async fn new() -> Self { Context { - doc: DashMap::new(), + workspace: DashMap::new(), storage: DashMap::new(), channel: DashMap::new(), db: DbPool::new(init_pool("jwst").await.expect("Cannot create database!")), diff --git a/apps/keck/src/server/collaboration.rs b/apps/keck/src/server/collaboration.rs index c8f44b92b..21c20d270 100644 --- a/apps/keck/src/server/collaboration.rs +++ b/apps/keck/src/server/collaboration.rs @@ -104,10 +104,12 @@ async fn handle_socket(socket: WebSocket, workspace: String, context: Arc, workspace: &str) { - if let Entry::Vacant(entry) = context.doc.entry(workspace.to_owned()) { + if let Entry::Vacant(entry) = context.workspace.entry(workspace.to_owned()) { let doc = context.db.create_doc(workspace).await.unwrap(); let (tx, mut rx) = channel::(100); @@ -34,7 +35,7 @@ pub async fn init_doc(context: Arc, workspace: &str) { context.storage.insert(workspace.to_owned(), tx); - entry.insert(Mutex::new(doc)); + entry.insert(Mutex::new(Workspace::from_doc(doc, workspace))); }; } @@ -44,18 +45,17 @@ mod tests { #[tokio::test] async fn doc_load_test() -> anyhow::Result<()> { + use jwst::Workspace; use yrs::{updates::decoder::Decode, Doc, StateVector, Update}; - let doc = Doc::default(); - { - let mut trx = doc.transact(); + let workspace = Workspace::new("test"); + workspace.with_trx(|mut t| { + let mut block = t.create("test", "text"); - let workspace = jwst::Workspace::new(&mut trx, "test"); - let mut block = workspace.create(&mut trx, "test", "text", doc.client_id); + block.set(&mut t.trx, "test", "test"); + }); - block.set(&mut trx, "test", "test"); - trx.commit(); - } + let doc = workspace.doc(); let new_doc = { let update = doc.encode_state_as_update_v1(&StateVector::default()); diff --git a/apps/keck/src/sync/pool.rs b/apps/keck/src/sync/pool.rs index 2f5b9f632..535f9adcd 100644 --- a/apps/keck/src/sync/pool.rs +++ b/apps/keck/src/sync/pool.rs @@ -71,15 +71,15 @@ impl DbPool { } pub async fn create_doc(&self, workspace: &str) -> Result { - let mut conn = self.get_conn(workspace).await?; - - conn.create().await?; - let mut doc = Doc::with_options(Options { skip_gc: true, ..Default::default() }); + let mut conn = self.get_conn(workspace).await?; + + conn.create().await?; + let all_data = conn.all().await.unwrap(); if all_data.is_empty() { diff --git a/libs/jwst-ffi/src/lib.rs b/libs/jwst-ffi/src/lib.rs index b461c1644..4aa0cbeda 100644 --- a/libs/jwst-ffi/src/lib.rs +++ b/libs/jwst-ffi/src/lib.rs @@ -35,9 +35,8 @@ pub unsafe extern "C" fn block_get_updated(block: *const Block) -> u64 { } #[no_mangle] -pub unsafe extern "C" fn workspace_new(trx: *mut Transaction, id: *const c_char) -> *mut Workspace { +pub unsafe extern "C" fn workspace_new(id: *const c_char) -> *mut Workspace { Box::into_raw(Box::new(Workspace::new( - trx.as_mut().unwrap(), CStr::from_ptr(id).to_str().unwrap(), ))) } @@ -51,12 +50,11 @@ pub unsafe extern "C" fn workspace_destroy(workspace: *mut Workspace) { pub unsafe extern "C" fn workspace_get_block( workspace: *const Workspace, block_id: *const c_char, - operator: u64, ) -> *mut Block { let block = workspace .as_ref() .unwrap() - .get(CStr::from_ptr(block_id).to_str().unwrap(), operator); + .get(CStr::from_ptr(block_id).to_str().unwrap()); if let Some(block) = block { Box::leak(Box::new(block)) @@ -68,18 +66,16 @@ pub unsafe extern "C" fn workspace_get_block( #[no_mangle] pub unsafe extern "C" fn workspace_create_block( workspace: *const Workspace, - trx: *mut Transaction, block_id: *const c_char, flavor: *const c_char, - operator: u64, ) -> *mut Block { let block_id = CStr::from_ptr(block_id).to_str().unwrap(); let flavor = CStr::from_ptr(flavor).to_str().unwrap(); - let block = - workspace - .as_ref() - .unwrap() - .create(trx.as_mut().unwrap(), block_id, flavor, operator); + let block = workspace + .as_ref() + .unwrap() + .get_trx() + .create(block_id, flavor); Box::into_raw(Box::new(block)) } @@ -87,13 +83,13 @@ pub unsafe extern "C" fn workspace_create_block( #[no_mangle] pub unsafe extern "C" fn workspace_remove_block( workspace: *const Workspace, - trx: *mut Transaction, block_id: *const c_char, ) -> bool { - workspace.as_ref().unwrap().remove( - trx.as_mut().unwrap(), - CStr::from_ptr(block_id).to_str().unwrap(), - ) + workspace + .as_ref() + .unwrap() + .get_trx() + .remove(CStr::from_ptr(block_id).to_str().unwrap()) } #[no_mangle] pub unsafe extern "C" fn workspace_exists_block( diff --git a/libs/jwst/src/block.rs b/libs/jwst/src/block.rs index 1301e221a..74a9e2a09 100644 --- a/libs/jwst/src/block.rs +++ b/libs/jwst/src/block.rs @@ -367,21 +367,16 @@ mod tests { #[test] fn init_block() { - use yrs::Doc; - - let doc = Doc::default(); - let mut trx = doc.transact(); - - let workspace = Workspace::new(&mut trx, "test"); + let workspace = Workspace::new("test"); // new block - let block = workspace.create(&mut trx, "test", "affine:text", 123); + let block = workspace.get_trx().create("test", "affine:text"); assert_eq!(block.id(), "test"); assert_eq!(block.flavor(), "affine:text"); assert_eq!(block.version(), [1, 0]); // get exist block - let block = workspace.get("test", 123).unwrap(); + let block = workspace.get("test").unwrap(); assert_eq!(block.flavor(), "affine:text"); assert_eq!(block.version(), [1, 0]); } @@ -390,41 +385,43 @@ mod tests { fn set_value() { use super::Any; use serde_json::{Number, Value}; - use yrs::Doc; - let doc = Doc::default(); - let mut trx = doc.transact(); + let workspace = Workspace::new("test"); - let workspace = Workspace::new(&mut trx, "test"); + let block = workspace.with_trx(|mut t| { + let mut block = t.create("test", "affine:text"); - let mut block = workspace.create(&mut trx, "test", "affine:text", doc.client_id); + let trx = &mut t.trx; - // normal type set - block.set(&mut trx, "bool", true); - block.set(&mut trx, "text", "hello world"); - block.set(&mut trx, "text_owned", "hello world".to_owned()); - block.set(&mut trx, "num", 123); - assert_eq!(block.content.get("bool").unwrap().to_string(), "true"); - assert_eq!( - block.content.get("text").unwrap().to_string(), - "hello world" - ); - assert_eq!( - block.content.get("text_owned").unwrap().to_string(), - "hello world" - ); - assert_eq!(block.content.get("num").unwrap().to_string(), "123"); - - // json type set - block.set(&mut trx, "json_bool", Value::Bool(false)); - block.set( - &mut trx, - "json_f64", - Value::Number(Number::from_f64(1.23).unwrap()), - ); - block.set(&mut trx, "json_i64", Value::Number(i64::MAX.into())); - block.set(&mut trx, "json_u64", Value::Number(u64::MAX.into())); - block.set(&mut trx, "json_str", Value::String("test".into())); + // normal type set + block.set(trx, "bool", true); + block.set(trx, "text", "hello world"); + block.set(trx, "text_owned", "hello world".to_owned()); + block.set(trx, "num", 123); + assert_eq!(block.content.get("bool").unwrap().to_string(), "true"); + assert_eq!( + block.content.get("text").unwrap().to_string(), + "hello world" + ); + assert_eq!( + block.content.get("text_owned").unwrap().to_string(), + "hello world" + ); + assert_eq!(block.content.get("num").unwrap().to_string(), "123"); + + // json type set + block.set(trx, "json_bool", Value::Bool(false)); + block.set( + trx, + "json_f64", + Value::Number(Number::from_f64(1.23).unwrap()), + ); + block.set(trx, "json_i64", Value::Number(i64::MAX.into())); + block.set(trx, "json_u64", Value::Number(u64::MAX.into())); + block.set(trx, "json_str", Value::String("test".into())); + + block + }); assert_eq!( block.content.get("json_bool").unwrap().to_json(), Any::Bool(false) @@ -450,99 +447,96 @@ mod tests { #[test] fn insert_remove_children() { use super::{InsertChildren, RemoveChildren}; - use yrs::Doc; - let doc = Doc::default(); - let mut trx = doc.transact(); + let workspace = Workspace::new("text"); - let workspace = Workspace::new(&mut trx, "text"); + workspace.with_trx(|mut t| { + let mut block = t.create("a", "affine:text"); + let trx = &mut t.trx; - let mut block = workspace.create(&mut trx, "a", "affine:text", 123); - - block.insert_children( - &mut trx, - InsertChildren { - block_id: "b".to_owned(), - ..Default::default() - }, - ); - block.insert_children( - &mut trx, - InsertChildren { - block_id: "c".to_owned(), - pos: Some(0), - ..Default::default() - }, - ); - block.insert_children( - &mut trx, - InsertChildren { - block_id: "d".to_owned(), - before: Some("b".to_owned()), - ..Default::default() - }, - ); - block.insert_children( - &mut trx, - InsertChildren { - block_id: "e".to_owned(), - after: Some("b".to_owned()), - ..Default::default() - }, - ); - block.insert_children( - &mut trx, - InsertChildren { - block_id: "f".to_owned(), - after: Some("c".to_owned()), - ..Default::default() - }, - ); + block.insert_children( + trx, + InsertChildren { + block_id: "b".to_owned(), + ..Default::default() + }, + ); + block.insert_children( + trx, + InsertChildren { + block_id: "c".to_owned(), + pos: Some(0), + ..Default::default() + }, + ); + block.insert_children( + trx, + InsertChildren { + block_id: "d".to_owned(), + before: Some("b".to_owned()), + ..Default::default() + }, + ); + block.insert_children( + trx, + InsertChildren { + block_id: "e".to_owned(), + after: Some("b".to_owned()), + ..Default::default() + }, + ); + block.insert_children( + trx, + InsertChildren { + block_id: "f".to_owned(), + after: Some("c".to_owned()), + ..Default::default() + }, + ); - assert_eq!( - block.children(), - vec![ - "c".to_owned(), - "f".to_owned(), - "d".to_owned(), - "b".to_owned(), - "e".to_owned() - ] - ); + assert_eq!( + block.children(), + vec![ + "c".to_owned(), + "f".to_owned(), + "d".to_owned(), + "b".to_owned(), + "e".to_owned() + ] + ); - block.remove_children( - &mut trx, - RemoveChildren { - block_id: "d".to_owned(), - }, - ); + block.remove_children( + trx, + RemoveChildren { + block_id: "d".to_owned(), + }, + ); - assert_eq!( - block - .children - .iter() - .map(|i| i.to_string()) - .collect::>(), - vec![ - "c".to_owned(), - "f".to_owned(), - "b".to_owned(), - "e".to_owned() - ] - ); + assert_eq!( + block + .children + .iter() + .map(|i| i.to_string()) + .collect::>(), + vec![ + "c".to_owned(), + "f".to_owned(), + "b".to_owned(), + "e".to_owned() + ] + ); + }); } #[test] fn updated() { - use yrs::Doc; - - let doc = Doc::default(); - let mut trx = doc.transact(); - - let workspace = Workspace::new(&mut trx, "test"); - let mut block = workspace.create(&mut trx, "a", "affine:text", 123); + let workspace = Workspace::new("test"); + let block = workspace.with_trx(|mut t| { + let mut block = t.create("a", "affine:text"); + block.set(&mut t.trx, "test", 1); - block.set(&mut trx, "test", 1); + block + }); assert!(block.created() <= block.updated()) } @@ -554,67 +548,70 @@ mod tests { }; use yrs::Doc; - let doc = Doc::default(); - let mut trx = doc.transact(); + let doc = Doc::with_client_id(123); - let workspace = Workspace::new(&mut trx, "test"); + let workspace = Workspace::from_doc(doc, "test"); - let mut block = workspace.create(&mut trx, "a", "affine:text", 123); + let block = workspace.with_trx(|mut t| { + let mut block = t.create("a", "affine:text"); + let trx = &mut t.trx; + block.set(trx, "test", 1); - block.set(&mut trx, "test", 1); + let history = block.history(); - let history = block.history(); + assert_eq!(history.len(), 2); - assert_eq!(history.len(), 2); + // let history = history.last().unwrap(); - // let history = history.last().unwrap(); + assert_eq!( + history, + vec![ + BlockHistory { + block_id: "a".to_owned(), + client: 123, + timestamp: history.get(0).unwrap().timestamp, + operation: HistoryOperation::Add, + }, + BlockHistory { + block_id: "a".to_owned(), + client: 123, + timestamp: history.get(1).unwrap().timestamp, + operation: HistoryOperation::Update, + } + ] + ); - assert_eq!( - history, - vec![ - BlockHistory { - block_id: "a".to_owned(), - client: 123, - timestamp: history.get(0).unwrap().timestamp, - operation: HistoryOperation::Add, + block.insert_children( + trx, + InsertChildren { + block_id: "b".to_owned(), + ..Default::default() }, - BlockHistory { - block_id: "a".to_owned(), - client: 123, - timestamp: history.get(1).unwrap().timestamp, - operation: HistoryOperation::Update, - } - ] - ); + ); - block.insert_children( - &mut trx, - InsertChildren { - block_id: "b".to_owned(), - ..Default::default() - }, - ); + assert_eq!( + block.exists_children(ExistsChildren { + block_id: "b".to_owned(), + }), + Some(0) + ); - assert_eq!( - block.exists_children(ExistsChildren { - block_id: "b".to_owned(), - }), - Some(0) - ); + block.remove_children( + trx, + RemoveChildren { + block_id: "b".to_owned(), + }, + ); - block.remove_children( - &mut trx, - RemoveChildren { - block_id: "b".to_owned(), - }, - ); + assert_eq!( + block.exists_children(ExistsChildren { + block_id: "b".to_owned(), + }), + None + ); - assert_eq!( - block.exists_children(ExistsChildren { - block_id: "b".to_owned(), - }), - None - ); + block + }); let history = block.history(); assert_eq!(history.len(), 4); diff --git a/libs/jwst/src/workspace.rs b/libs/jwst/src/workspace.rs index 2e74fdc2c..6dd423cb0 100644 --- a/libs/jwst/src/workspace.rs +++ b/libs/jwst/src/workspace.rs @@ -1,23 +1,31 @@ use super::*; use lib0::any::Any; use serde::{ser::SerializeMap, Serialize, Serializer}; -use yrs::{Map, PrelimMap, Transaction}; +use yrs::{Doc, Map, PrelimMap, Transaction}; pub struct Workspace { id: String, blocks: Map, updated: Map, + doc: Doc, } +unsafe impl Send for Workspace {} + impl Workspace { - pub fn new>(trx: &mut Transaction, id: S) -> Self { - let blocks = trx.get_map("blocks"); + pub fn new>(id: S) -> Self { + let doc = Doc::new(); + Self::from_doc(doc, id) + } + pub fn from_doc>(doc: Doc, id: S) -> Workspace { + let mut trx = doc.transact(); + let blocks = trx.get_map("blocks"); // blocks.content let content = blocks .get("content") .or_else(|| { - blocks.insert(trx, "content", PrelimMap::::new()); + blocks.insert(&mut trx, "content", PrelimMap::::new()); blocks.get("content") }) .and_then(|b| b.to_ymap()) @@ -27,7 +35,7 @@ impl Workspace { let updated = blocks .get("updated") .or_else(|| { - blocks.insert(trx, "updated", PrelimMap::::new()); + blocks.insert(&mut trx, "updated", PrelimMap::::new()); blocks.get("updated") }) .and_then(|b| b.to_ymap()) @@ -36,6 +44,7 @@ impl Workspace { id: id.as_ref().to_string(), blocks: content, updated, + doc, } } @@ -51,32 +60,40 @@ impl Workspace { &self.updated } - // create a block with specified flavor - // if block exists, return the exists block - pub fn create( - &self, - trx: &mut Transaction, - block_id: B, - flavor: &str, - operator: u64, - ) -> Block - where - B: AsRef, - { - Block::new(self, trx, block_id, flavor, operator) + pub fn doc(&self) -> &Doc { + &self.doc + } + + pub fn doc_mut(&mut self) -> &mut Doc { + &mut self.doc + } + + pub fn client_id(&self) -> u64 { + self.doc.client_id + } + + pub fn with_trx(&self, f: impl FnOnce(WorkspaceTranscation) -> T) -> T { + let trx = WorkspaceTranscation { + trx: self.doc.transact(), + ws: self, + }; + + f(trx) + } + + pub fn get_trx(&self) -> WorkspaceTranscation { + WorkspaceTranscation { + trx: self.doc.transact(), + ws: self, + } } // get a block if exists - pub fn get(&self, block_id: S, operator: u64) -> Option + pub fn get(&self, block_id: S) -> Option where S: AsRef, { - Block::from(self, block_id, operator) - } - - pub fn remove(&self, trx: &mut Transaction, block_id: &str) -> bool { - self.blocks.remove(trx, block_id.as_ref()).is_some() - && self.updated().remove(trx, block_id.as_ref()).is_some() + Block::from(self, block_id, self.doc.client_id) } pub fn exists(&self, block_id: &str) -> bool { @@ -96,39 +113,69 @@ impl Serialize for Workspace { } } +pub struct WorkspaceTranscation<'a> { + pub ws: &'a Workspace, + pub trx: Transaction, +} + +impl WorkspaceTranscation<'_> { + pub fn remove(&mut self, block_id: &str) -> bool { + self.ws + .blocks + .remove(&mut self.trx, block_id.as_ref()) + .is_some() + && self + .ws + .updated() + .remove(&mut self.trx, block_id.as_ref()) + .is_some() + } + + // create a block with specified flavor + // if block exists, return the exists block + pub fn create(&mut self, block_id: B, flavor: &str) -> Block + where + B: AsRef, + { + Block::new( + self.ws, + &mut self.trx, + block_id, + flavor, + self.ws.doc.client_id, + ) + } +} + #[cfg(test)] mod test { use super::Workspace; - use yrs::Doc; #[test] fn workspace() { - let doc = Doc::default(); - let mut trx = doc.transact(); - - let workspace = Workspace::new(&mut trx, "test"); + let workspace = Workspace::new("test"); assert_eq!(workspace.id(), "test"); assert_eq!(workspace.blocks().len(), 0); assert_eq!(workspace.updated().len(), 0); - let block = workspace.create(&mut trx, "block", "text", 0); + let block = workspace.get_trx().create("block", "text"); assert_eq!(workspace.blocks().len(), 1); assert_eq!(workspace.updated().len(), 1); assert_eq!(block.id(), "block"); assert_eq!(block.flavor(), "text"); assert_eq!( - workspace.get("block", doc.client_id).map(|b| b.id()), + workspace.get("block").map(|b| b.id()), Some("block".to_owned()) ); assert_eq!(workspace.exists("block"), true); - assert_eq!(workspace.remove(&mut trx, "block"), true); + assert_eq!(workspace.get_trx().remove("block"), true); assert_eq!(workspace.blocks().len(), 0); assert_eq!(workspace.updated().len(), 0); - assert_eq!(workspace.get("block", doc.client_id), None); + assert_eq!(workspace.get("block"), None); assert_eq!(workspace.exists("block"), false); }