diff --git a/frontend/rust-lib/flowy-folder/src/services/folder_editor.rs b/frontend/rust-lib/flowy-folder/src/services/folder_editor.rs index de461379e1..af0ae132f1 100644 --- a/frontend/rust-lib/flowy-folder/src/services/folder_editor.rs +++ b/frontend/rust-lib/flowy-folder/src/services/folder_editor.rs @@ -6,7 +6,7 @@ use flowy_revision::{ }; use flowy_sync::util::make_delta_from_revisions; use flowy_sync::{ - client_folder::{FolderChange, FolderPad}, + client_folder::{FolderChangeset, FolderPad}, entities::{revision::Revision, ws_data::ServerRevisionWSData}, }; use lib_infra::future::FutureResult; @@ -77,8 +77,8 @@ impl FolderEditor { Ok(()) } - pub(crate) fn apply_change(&self, change: FolderChange) -> FlowyResult<()> { - let FolderChange { delta, md5 } = change; + pub(crate) fn apply_change(&self, change: FolderChangeset) -> FlowyResult<()> { + let FolderChangeset { delta, md5 } = change; let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair(); let delta_data = delta.json_bytes(); let revision = Revision::new( diff --git a/shared-lib/flowy-folder-data-model/Cargo.toml b/shared-lib/flowy-folder-data-model/Cargo.toml index bacd1c9509..15b8e47bac 100644 --- a/shared-lib/flowy-folder-data-model/Cargo.toml +++ b/shared-lib/flowy-folder-data-model/Cargo.toml @@ -17,7 +17,7 @@ log = "0.4.14" nanoid = "0.4.0" chrono = { version = "0.4" } flowy-error-code = { path = "../flowy-error-code"} -serde = { version = "1.0", features = ["derive"] } +serde = { version = "1.0", features = ["derive", "rc"] } serde_json = "1.0" serde_repr = "0.1" diff --git a/shared-lib/flowy-folder-data-model/src/revision/folder_rev.rs b/shared-lib/flowy-folder-data-model/src/revision/folder_rev.rs new file mode 100644 index 0000000000..f7d48b93fd --- /dev/null +++ b/shared-lib/flowy-folder-data-model/src/revision/folder_rev.rs @@ -0,0 +1,9 @@ +use crate::revision::{TrashRevision, WorkspaceRevision}; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; + +#[derive(Debug, Default, Deserialize, Serialize, Clone, Eq, PartialEq)] +pub struct FolderRevision { + pub workspaces: Vec>, + pub trash: Vec>, +} diff --git a/shared-lib/flowy-folder-data-model/src/revision/mod.rs b/shared-lib/flowy-folder-data-model/src/revision/mod.rs index 6a837ac035..a1e9d5e33e 100644 --- a/shared-lib/flowy-folder-data-model/src/revision/mod.rs +++ b/shared-lib/flowy-folder-data-model/src/revision/mod.rs @@ -1,9 +1,11 @@ mod app_rev; +mod folder_rev; mod trash_rev; mod view_rev; mod workspace_rev; pub use app_rev::*; +pub use folder_rev::*; pub use trash_rev::*; pub use view_rev::*; pub use workspace_rev::*; diff --git a/shared-lib/flowy-folder-data-model/src/revision/trash_rev.rs b/shared-lib/flowy-folder-data-model/src/revision/trash_rev.rs index c72fc61dad..0855fb3d29 100644 --- a/shared-lib/flowy-folder-data-model/src/revision/trash_rev.rs +++ b/shared-lib/flowy-folder-data-model/src/revision/trash_rev.rs @@ -33,7 +33,7 @@ impl<'de> serde::Deserialize<'de> for TrashTypeRevision { type Value = TrashTypeRevision; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("u8") + formatter.write_str("expected enum TrashTypeRevision with type: u8") } fn visit_i8(self, v: i8) -> Result diff --git a/shared-lib/flowy-sync/src/client_folder/builder.rs b/shared-lib/flowy-sync/src/client_folder/builder.rs index 3855d62834..75a1343570 100644 --- a/shared-lib/flowy-sync/src/client_folder/builder.rs +++ b/shared-lib/flowy-sync/src/client_folder/builder.rs @@ -3,18 +3,17 @@ use crate::util::make_delta_from_revisions; use crate::{ client_folder::{default_folder_delta, FolderPad}, entities::revision::Revision, - errors::{CollaborateError, CollaborateResult}, + errors::CollaborateResult, }; use flowy_folder_data_model::revision::{TrashRevision, WorkspaceRevision}; -use lib_ot::core::{PhantomAttributes, TextDelta, TextDeltaBuilder}; +use lib_ot::core::PhantomAttributes; use serde::{Deserialize, Serialize}; -use std::sync::Arc; #[derive(Serialize, Deserialize)] pub(crate) struct FolderPadBuilder { - workspaces: Vec>, - trash: Vec>, + workspaces: Vec, + trash: Vec, } impl FolderPadBuilder { @@ -25,43 +24,28 @@ impl FolderPadBuilder { } } + #[allow(dead_code)] pub(crate) fn with_workspace(mut self, workspaces: Vec) -> Self { - self.workspaces = workspaces.into_iter().map(Arc::new).collect(); + self.workspaces = workspaces; self } + #[allow(dead_code)] pub(crate) fn with_trash(mut self, trash: Vec) -> Self { - self.trash = trash.into_iter().map(Arc::new).collect::>(); + self.trash = trash; self } - pub(crate) fn build_with_delta(self, mut delta: TextDelta) -> CollaborateResult { - if delta.is_empty() { - delta = default_folder_delta(); - } - - // TODO: Reconvert from history if delta.to_str() failed. - let content = delta.content()?; - let mut folder: FolderPad = serde_json::from_str(&content).map_err(|e| { - tracing::error!("Deserialize folder from {} failed", content); - return CollaborateError::internal().context(format!("Deserialize delta to folder failed: {}", e)); - })?; - folder.delta = delta; - Ok(folder) - } - pub(crate) fn build_with_revisions(self, revisions: Vec) -> CollaborateResult { - let folder_delta: FolderDelta = make_delta_from_revisions::(revisions)?; - self.build_with_delta(folder_delta) + let mut folder_delta: FolderDelta = make_delta_from_revisions::(revisions)?; + if folder_delta.is_empty() { + folder_delta = default_folder_delta(); + } + FolderPad::from_delta(folder_delta) } + #[allow(dead_code)] pub(crate) fn build(self) -> CollaborateResult { - let json = serde_json::to_string(&self) - .map_err(|e| CollaborateError::internal().context(format!("Serialize to folder json str failed: {}", e)))?; - Ok(FolderPad { - workspaces: self.workspaces, - trash: self.trash, - delta: TextDeltaBuilder::new().insert(&json).build(), - }) + FolderPad::new(self.workspaces, self.trash) } } diff --git a/shared-lib/flowy-sync/src/client_folder/folder_pad.rs b/shared-lib/flowy-sync/src/client_folder/folder_pad.rs index 5927be2c34..28c8e087a1 100644 --- a/shared-lib/flowy-sync/src/client_folder/folder_pad.rs +++ b/shared-lib/flowy-sync/src/client_folder/folder_pad.rs @@ -8,26 +8,33 @@ use crate::{ }, errors::{CollaborateError, CollaborateResult}, }; -use flowy_folder_data_model::revision::{AppRevision, TrashRevision, ViewRevision, WorkspaceRevision}; +use flowy_folder_data_model::revision::{AppRevision, FolderRevision, TrashRevision, ViewRevision, WorkspaceRevision}; use lib_infra::util::move_vec_element; use lib_ot::core::*; -use serde::{Deserialize, Serialize}; + use std::sync::Arc; -#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct FolderPad { - pub(crate) workspaces: Vec>, - pub(crate) trash: Vec>, - #[serde(skip)] - pub(crate) delta: FolderDelta, + folder_rev: FolderRevision, + delta: FolderDelta, } impl FolderPad { pub fn new(workspaces: Vec, trash: Vec) -> CollaborateResult { - FolderPadBuilder::new() - .with_workspace(workspaces) - .with_trash(trash) - .build() + let folder_rev = FolderRevision { + workspaces: workspaces.into_iter().map(Arc::new).collect(), + trash: trash.into_iter().map(Arc::new).collect(), + }; + Self::from_folder_rev(folder_rev) + } + + pub fn from_folder_rev(folder_rev: FolderRevision) -> CollaborateResult { + let json = serde_json::to_string(&folder_rev) + .map_err(|e| CollaborateError::internal().context(format!("Serialize to folder json str failed: {}", e)))?; + let delta = TextDeltaBuilder::new().insert(&json).build(); + + Ok(Self { folder_rev, delta }) } pub fn from_revisions(revisions: Vec) -> CollaborateResult { @@ -35,7 +42,14 @@ impl FolderPad { } pub fn from_delta(delta: FolderDelta) -> CollaborateResult { - FolderPadBuilder::new().build_with_delta(delta) + // TODO: Reconvert from history if delta.to_str() failed. + let content = delta.content()?; + let folder_rev: FolderRevision = serde_json::from_str(&content).map_err(|e| { + tracing::error!("Deserialize folder from {} failed", content); + return CollaborateError::internal().context(format!("Deserialize delta to folder failed: {}", e)); + })?; + + Ok(Self { folder_rev, delta }) } pub fn delta(&self) -> &FolderDelta { @@ -44,8 +58,7 @@ impl FolderPad { pub fn reset_folder(&mut self, delta: FolderDelta) -> CollaborateResult { let folder = FolderPad::from_delta(delta)?; - self.workspaces = folder.workspaces; - self.trash = folder.trash; + self.folder_rev = folder.folder_rev; self.delta = folder.delta; Ok(self.md5()) @@ -57,13 +70,13 @@ impl FolderPad { } pub fn is_empty(&self) -> bool { - self.workspaces.is_empty() && self.trash.is_empty() + self.folder_rev.workspaces.is_empty() && self.folder_rev.trash.is_empty() } #[tracing::instrument(level = "trace", skip(self, workspace_rev), fields(workspace_name=%workspace_rev.name), err)] - pub fn create_workspace(&mut self, workspace_rev: WorkspaceRevision) -> CollaborateResult> { + pub fn create_workspace(&mut self, workspace_rev: WorkspaceRevision) -> CollaborateResult> { let workspace = Arc::new(workspace_rev); - if self.workspaces.contains(&workspace) { + if self.folder_rev.workspaces.contains(&workspace) { tracing::warn!("[RootFolder]: Duplicate workspace"); return Ok(None); } @@ -79,7 +92,7 @@ impl FolderPad { workspace_id: &str, name: Option, desc: Option, - ) -> CollaborateResult> { + ) -> CollaborateResult> { self.with_workspace(workspace_id, |workspace| { if let Some(name) = name { workspace.name = name; @@ -96,6 +109,7 @@ impl FolderPad { match workspace_id { None => { let workspaces = self + .folder_rev .workspaces .iter() .map(|workspace| workspace.as_ref().clone()) @@ -103,7 +117,12 @@ impl FolderPad { Ok(workspaces) } Some(workspace_id) => { - if let Some(workspace) = self.workspaces.iter().find(|workspace| workspace.id == workspace_id) { + if let Some(workspace) = self + .folder_rev + .workspaces + .iter() + .find(|workspace| workspace.id == workspace_id) + { Ok(vec![workspace.as_ref().clone()]) } else { Err(CollaborateError::record_not_found() @@ -114,7 +133,7 @@ impl FolderPad { } #[tracing::instrument(level = "trace", skip(self), err)] - pub fn delete_workspace(&mut self, workspace_id: &str) -> CollaborateResult> { + pub fn delete_workspace(&mut self, workspace_id: &str) -> CollaborateResult> { self.modify_workspaces(|workspaces| { workspaces.retain(|w| w.id != workspace_id); Ok(Some(())) @@ -122,7 +141,7 @@ impl FolderPad { } #[tracing::instrument(level = "trace", skip(self), fields(app_name=%app_rev.name), err)] - pub fn create_app(&mut self, app_rev: AppRevision) -> CollaborateResult> { + pub fn create_app(&mut self, app_rev: AppRevision) -> CollaborateResult> { let workspace_id = app_rev.workspace_id.clone(); self.with_workspace(&workspace_id, move |workspace| { if workspace.apps.contains(&app_rev) { @@ -135,7 +154,7 @@ impl FolderPad { } pub fn read_app(&self, app_id: &str) -> CollaborateResult { - for workspace in &self.workspaces { + for workspace in &self.folder_rev.workspaces { if let Some(app) = workspace.apps.iter().find(|app| app.id == app_id) { return Ok(app.clone()); } @@ -148,7 +167,7 @@ impl FolderPad { app_id: &str, name: Option, desc: Option, - ) -> CollaborateResult> { + ) -> CollaborateResult> { self.with_app(app_id, move |app| { if let Some(name) = name { app.name = name; @@ -162,7 +181,7 @@ impl FolderPad { } #[tracing::instrument(level = "trace", skip(self), err)] - pub fn delete_app(&mut self, app_id: &str) -> CollaborateResult> { + pub fn delete_app(&mut self, app_id: &str) -> CollaborateResult> { let app = self.read_app(app_id)?; self.with_workspace(&app.workspace_id, |workspace| { workspace.apps.retain(|app| app.id != app_id); @@ -171,7 +190,7 @@ impl FolderPad { } #[tracing::instrument(level = "trace", skip(self), err)] - pub fn move_app(&mut self, app_id: &str, from: usize, to: usize) -> CollaborateResult> { + pub fn move_app(&mut self, app_id: &str, from: usize, to: usize) -> CollaborateResult> { let app = self.read_app(app_id)?; self.with_workspace(&app.workspace_id, |workspace| { match move_vec_element(&mut workspace.apps, |app| app.id == app_id, from, to).map_err(internal_error)? { @@ -182,7 +201,7 @@ impl FolderPad { } #[tracing::instrument(level = "trace", skip(self), fields(view_name=%view_rev.name), err)] - pub fn create_view(&mut self, view_rev: ViewRevision) -> CollaborateResult> { + pub fn create_view(&mut self, view_rev: ViewRevision) -> CollaborateResult> { let app_id = view_rev.belong_to_id.clone(); self.with_app(&app_id, move |app| { if app.belongings.contains(&view_rev) { @@ -195,7 +214,7 @@ impl FolderPad { } pub fn read_view(&self, view_id: &str) -> CollaborateResult { - for workspace in &self.workspaces { + for workspace in &self.folder_rev.workspaces { for app in &(*workspace.apps) { if let Some(view) = app.belongings.iter().find(|b| b.id == view_id) { return Ok(view.clone()); @@ -206,7 +225,7 @@ impl FolderPad { } pub fn read_views(&self, belong_to_id: &str) -> CollaborateResult> { - for workspace in &self.workspaces { + for workspace in &self.folder_rev.workspaces { for app in &(*workspace.apps) { if app.id == belong_to_id { return Ok(app.belongings.to_vec()); @@ -222,7 +241,7 @@ impl FolderPad { name: Option, desc: Option, modified_time: i64, - ) -> CollaborateResult> { + ) -> CollaborateResult> { let view = self.read_view(view_id)?; self.with_view(&view.belong_to_id, view_id, |view| { if let Some(name) = name { @@ -239,7 +258,7 @@ impl FolderPad { } #[tracing::instrument(level = "trace", skip(self), err)] - pub fn delete_view(&mut self, view_id: &str) -> CollaborateResult> { + pub fn delete_view(&mut self, view_id: &str) -> CollaborateResult> { let view = self.read_view(view_id)?; self.with_app(&view.belong_to_id, |app| { app.belongings.retain(|view| view.id != view_id); @@ -248,7 +267,7 @@ impl FolderPad { } #[tracing::instrument(level = "trace", skip(self), err)] - pub fn move_view(&mut self, view_id: &str, from: usize, to: usize) -> CollaborateResult> { + pub fn move_view(&mut self, view_id: &str, from: usize, to: usize) -> CollaborateResult> { let view = self.read_view(view_id)?; self.with_app(&view.belong_to_id, |app| { match move_vec_element(&mut app.belongings, |view| view.id == view_id, from, to).map_err(internal_error)? { @@ -258,7 +277,7 @@ impl FolderPad { }) } - pub fn create_trash(&mut self, trash: Vec) -> CollaborateResult> { + pub fn create_trash(&mut self, trash: Vec) -> CollaborateResult> { self.with_trash(|t| { let mut new_trash = trash.into_iter().map(Arc::new).collect::>>(); t.append(&mut new_trash); @@ -270,18 +289,19 @@ impl FolderPad { pub fn read_trash(&self, trash_id: Option) -> CollaborateResult> { match trash_id { None => Ok(self + .folder_rev .trash .iter() .map(|t| t.as_ref().clone()) .collect::>()), - Some(trash_id) => match self.trash.iter().find(|t| t.id == trash_id) { + Some(trash_id) => match self.folder_rev.trash.iter().find(|t| t.id == trash_id) { Some(trash) => Ok(vec![trash.as_ref().clone()]), None => Ok(vec![]), }, } } - pub fn delete_trash(&mut self, trash_ids: Option>) -> CollaborateResult> { + pub fn delete_trash(&mut self, trash_ids: Option>) -> CollaborateResult> { match trash_ids { None => self.with_trash(|trash| { trash.clear(); @@ -299,18 +319,18 @@ impl FolderPad { } pub fn to_json(&self) -> CollaborateResult { - serde_json::to_string(self) + serde_json::to_string(&self.folder_rev) .map_err(|e| CollaborateError::internal().context(format!("serial trash to json failed: {}", e))) } } impl FolderPad { - fn modify_workspaces(&mut self, f: F) -> CollaborateResult> + fn modify_workspaces(&mut self, f: F) -> CollaborateResult> where F: FnOnce(&mut Vec>) -> CollaborateResult>, { let cloned_self = self.clone(); - match f(&mut self.workspaces)? { + match f(&mut self.folder_rev.workspaces)? { None => Ok(None), Some(_) => { let old = cloned_self.to_json()?; @@ -319,14 +339,14 @@ impl FolderPad { None => Ok(None), Some(delta) => { self.delta = self.delta.compose(&delta)?; - Ok(Some(FolderChange { delta, md5: self.md5() })) + Ok(Some(FolderChangeset { delta, md5: self.md5() })) } } } } } - fn with_workspace(&mut self, workspace_id: &str, f: F) -> CollaborateResult> + fn with_workspace(&mut self, workspace_id: &str, f: F) -> CollaborateResult> where F: FnOnce(&mut WorkspaceRevision) -> CollaborateResult>, { @@ -340,12 +360,12 @@ impl FolderPad { }) } - fn with_trash(&mut self, f: F) -> CollaborateResult> + fn with_trash(&mut self, f: F) -> CollaborateResult> where F: FnOnce(&mut Vec>) -> CollaborateResult>, { let cloned_self = self.clone(); - match f(&mut self.trash)? { + match f(&mut self.folder_rev.trash)? { None => Ok(None), Some(_) => { let old = cloned_self.to_json()?; @@ -354,18 +374,19 @@ impl FolderPad { None => Ok(None), Some(delta) => { self.delta = self.delta.compose(&delta)?; - Ok(Some(FolderChange { delta, md5: self.md5() })) + Ok(Some(FolderChangeset { delta, md5: self.md5() })) } } } } } - fn with_app(&mut self, app_id: &str, f: F) -> CollaborateResult> + fn with_app(&mut self, app_id: &str, f: F) -> CollaborateResult> where F: FnOnce(&mut AppRevision) -> CollaborateResult>, { let workspace_id = match self + .folder_rev .workspaces .iter() .find(|workspace| workspace.apps.iter().any(|app| app.id == app_id)) @@ -383,7 +404,7 @@ impl FolderPad { }) } - fn with_view(&mut self, belong_to_id: &str, view_id: &str, f: F) -> CollaborateResult> + fn with_view(&mut self, belong_to_id: &str, view_id: &str, f: F) -> CollaborateResult> where F: FnOnce(&mut ViewRevision) -> CollaborateResult>, { @@ -414,14 +435,13 @@ pub fn initial_folder_delta(folder_pad: &FolderPad) -> CollaborateResult Self { FolderPad { - workspaces: vec![], - trash: vec![], + folder_rev: FolderRevision::default(), delta: default_folder_delta(), } } } -pub struct FolderChange { +pub struct FolderChangeset { pub delta: FolderDelta, /// md5: the md5 of the FolderPad's delta after applying the change. pub md5: String, @@ -433,8 +453,11 @@ mod tests { use crate::{client_folder::folder_pad::FolderPad, entities::folder::FolderDelta}; use chrono::Utc; - use flowy_folder_data_model::revision::{AppRevision, TrashRevision, ViewRevision, WorkspaceRevision}; + use flowy_folder_data_model::revision::{ + AppRevision, FolderRevision, TrashRevision, ViewRevision, WorkspaceRevision, + }; use lib_ot::core::{OperationTransform, TextDelta, TextDeltaBuilder}; + use serde_json::json; #[test] fn folder_add_workspace() { @@ -747,14 +770,16 @@ mod tests { } fn test_folder() -> (FolderPad, FolderDelta, WorkspaceRevision) { - let mut folder = FolderPad::default(); - let folder_json = serde_json::to_string(&folder).unwrap(); + let folder_rev = FolderRevision::default(); + let folder_json = serde_json::to_string(&folder_rev).unwrap(); let mut delta = TextDeltaBuilder::new().insert(&folder_json).build(); let mut workspace_rev = WorkspaceRevision::default(); workspace_rev.name = "😁 my first workspace".to_owned(); workspace_rev.id = "1".to_owned(); + let mut folder = FolderPad::from_folder_rev(folder_rev).unwrap(); + delta = delta .compose(&folder.create_workspace(workspace_rev.clone()).unwrap().unwrap().delta) .unwrap(); @@ -763,16 +788,16 @@ mod tests { } fn test_app_folder() -> (FolderPad, FolderDelta, AppRevision) { - let (mut folder, mut initial_delta, workspace) = test_folder(); + let (mut folder_rev, mut initial_delta, workspace) = test_folder(); let mut app_rev = AppRevision::default(); app_rev.workspace_id = workspace.id; app_rev.name = "😁 my first app".to_owned(); initial_delta = initial_delta - .compose(&folder.create_app(app_rev.clone()).unwrap().unwrap().delta) + .compose(&folder_rev.create_app(app_rev.clone()).unwrap().unwrap().delta) .unwrap(); - (folder, initial_delta, app_rev) + (folder_rev, initial_delta, app_rev) } fn test_view_folder() -> (FolderPad, FolderDelta, ViewRevision) { @@ -789,14 +814,14 @@ mod tests { } fn test_trash() -> (FolderPad, FolderDelta, TrashRevision) { - let mut folder = FolderPad::default(); - let folder_json = serde_json::to_string(&folder).unwrap(); + let folder_rev = FolderRevision::default(); + let folder_json = serde_json::to_string(&folder_rev).unwrap(); let mut delta = TextDeltaBuilder::new().insert(&folder_json).build(); let mut trash_rev = TrashRevision::default(); trash_rev.name = "🚽 my first trash".to_owned(); trash_rev.id = "1".to_owned(); - + let mut folder = FolderPad::from_folder_rev(folder_rev).unwrap(); delta = delta .compose( &folder @@ -823,8 +848,11 @@ mod tests { let json1 = old.to_json().unwrap(); let json2 = new.to_json().unwrap(); - let expect_folder: FolderPad = serde_json::from_str(expected).unwrap(); - assert_eq!(json1, expect_folder.to_json().unwrap()); + // format the json str + let folder_rev: FolderRevision = serde_json::from_str(expected).unwrap(); + let expected = serde_json::to_string(&folder_rev).unwrap(); + + assert_eq!(json1, expected); assert_eq!(json1, json2); } } diff --git a/shared-lib/flowy-sync/src/client_grid/grid_meta_pad.rs b/shared-lib/flowy-sync/src/client_grid/grid_meta_pad.rs deleted file mode 100644 index 3ef9251e4a..0000000000 --- a/shared-lib/flowy-sync/src/client_grid/grid_meta_pad.rs +++ /dev/null @@ -1,432 +0,0 @@ -use crate::entities::revision::{md5, RepeatedRevision, Revision}; -use crate::errors::{internal_error, CollaborateError, CollaborateResult}; -use crate::util::{cal_diff, make_delta_from_revisions}; -use bytes::Bytes; -use flowy_grid_data_model::entities::{ - gen_block_id, gen_grid_id, FieldChangesetParams, FieldMeta, FieldOrder, FieldType, GridBlockInfoChangeset, - GridBlockMetaSnapshot, GridMeta, -}; -use lib_infra::util::move_vec_element; -use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder}; -use std::collections::HashMap; -use std::sync::Arc; - -pub type GridMetaDelta = PlainTextDelta; -pub type GridDeltaBuilder = PlainTextDeltaBuilder; - -pub struct GridMetaPad { - pub(crate) grid_meta: Arc, - pub(crate) delta: GridMetaDelta, -} - -pub trait JsonDeserializer { - fn deserialize(&self, type_option_data: Vec) -> CollaborateResult; -} - -impl GridMetaPad { - pub async fn duplicate_grid_meta(&self) -> (Vec, Vec) { - let fields = self.grid_meta.fields.to_vec(); - - let blocks = self - .grid_meta - .blocks - .iter() - .map(|block| { - let mut duplicated_block = block.clone(); - duplicated_block.block_id = gen_block_id(); - duplicated_block - }) - .collect::>(); - - (fields, blocks) - } - - pub fn from_delta(delta: GridMetaDelta) -> CollaborateResult { - let s = delta.to_str()?; - let grid: GridMeta = serde_json::from_str(&s) - .map_err(|e| CollaborateError::internal().context(format!("Deserialize delta to grid failed: {}", e)))?; - - Ok(Self { - grid_meta: Arc::new(grid), - delta, - }) - } - - pub fn from_revisions(_grid_id: &str, revisions: Vec) -> CollaborateResult { - let grid_delta: GridMetaDelta = make_delta_from_revisions::(revisions)?; - Self::from_delta(grid_delta) - } - - #[tracing::instrument(level = "debug", skip_all, err)] - pub fn create_field_meta( - &mut self, - new_field_meta: FieldMeta, - start_field_id: Option, - ) -> CollaborateResult> { - self.modify_grid(|grid_meta| { - // Check if the field exists or not - if grid_meta - .fields - .iter() - .any(|field_meta| field_meta.id == new_field_meta.id) - { - tracing::error!("Duplicate grid field"); - return Ok(None); - } - - let insert_index = match start_field_id { - None => None, - Some(start_field_id) => grid_meta.fields.iter().position(|field| field.id == start_field_id), - }; - - match insert_index { - None => grid_meta.fields.push(new_field_meta), - Some(index) => grid_meta.fields.insert(index, new_field_meta), - } - Ok(Some(())) - }) - } - - pub fn delete_field_meta(&mut self, field_id: &str) -> CollaborateResult> { - self.modify_grid( - |grid_meta| match grid_meta.fields.iter().position(|field| field.id == field_id) { - None => Ok(None), - Some(index) => { - grid_meta.fields.remove(index); - Ok(Some(())) - } - }, - ) - } - - pub fn duplicate_field_meta( - &mut self, - field_id: &str, - duplicated_field_id: &str, - ) -> CollaborateResult> { - self.modify_grid( - |grid_meta| match grid_meta.fields.iter().position(|field| field.id == field_id) { - None => Ok(None), - Some(index) => { - let mut duplicate_field_meta = grid_meta.fields[index].clone(); - duplicate_field_meta.id = duplicated_field_id.to_string(); - duplicate_field_meta.name = format!("{} (copy)", duplicate_field_meta.name); - grid_meta.fields.insert(index + 1, duplicate_field_meta); - Ok(Some(())) - } - }, - ) - } - - pub fn switch_to_field( - &mut self, - field_id: &str, - field_type: FieldType, - type_option_json_builder: B, - ) -> CollaborateResult> - where - B: FnOnce(&FieldType) -> String, - { - self.modify_grid(|grid_meta| { - // - match grid_meta.fields.iter_mut().find(|field_meta| field_meta.id == field_id) { - None => { - tracing::warn!("Can not find the field with id: {}", field_id); - Ok(None) - } - Some(field_meta) => { - if field_meta.get_type_option_str(&field_type).is_none() { - let type_option_json = type_option_json_builder(&field_type); - field_meta.insert_type_option_str(&field_type, type_option_json); - } - - field_meta.field_type = field_type; - Ok(Some(())) - } - } - }) - } - - pub fn update_field_meta( - &mut self, - changeset: FieldChangesetParams, - deserializer: T, - ) -> CollaborateResult> { - let field_id = changeset.field_id.clone(); - self.modify_field(&field_id, |field| { - let mut is_changed = None; - if let Some(name) = changeset.name { - field.name = name; - is_changed = Some(()) - } - - if let Some(desc) = changeset.desc { - field.desc = desc; - is_changed = Some(()) - } - - if let Some(field_type) = changeset.field_type { - field.field_type = field_type; - is_changed = Some(()) - } - - if let Some(frozen) = changeset.frozen { - field.frozen = frozen; - is_changed = Some(()) - } - - if let Some(visibility) = changeset.visibility { - field.visibility = visibility; - is_changed = Some(()) - } - - if let Some(width) = changeset.width { - field.width = width; - is_changed = Some(()) - } - - if let Some(type_option_data) = changeset.type_option_data { - match deserializer.deserialize(type_option_data) { - Ok(json_str) => { - let field_type = field.field_type.clone(); - field.insert_type_option_str(&field_type, json_str); - is_changed = Some(()) - } - Err(err) => { - tracing::error!("Deserialize data to type option json failed: {}", err); - } - } - } - - Ok(is_changed) - }) - } - - pub fn get_field_meta(&self, field_id: &str) -> Option<(usize, &FieldMeta)> { - self.grid_meta - .fields - .iter() - .enumerate() - .find(|(_, field)| field.id == field_id) - } - - pub fn replace_field_meta(&mut self, field_meta: FieldMeta) -> CollaborateResult> { - self.modify_grid( - |grid_meta| match grid_meta.fields.iter().position(|field| field.id == field_meta.id) { - None => Ok(None), - Some(index) => { - grid_meta.fields.remove(index); - grid_meta.fields.insert(index, field_meta); - Ok(Some(())) - } - }, - ) - } - - pub fn move_field( - &mut self, - field_id: &str, - from_index: usize, - to_index: usize, - ) -> CollaborateResult> { - self.modify_grid(|grid_meta| { - match move_vec_element( - &mut grid_meta.fields, - |field| field.id == field_id, - from_index, - to_index, - ) - .map_err(internal_error)? - { - true => Ok(Some(())), - false => Ok(None), - } - }) - } - - pub fn contain_field(&self, field_id: &str) -> bool { - self.grid_meta.fields.iter().any(|field| field.id == field_id) - } - - pub fn get_field_orders(&self) -> Vec { - self.grid_meta.fields.iter().map(FieldOrder::from).collect() - } - - pub fn get_field_metas(&self, field_orders: Option>) -> CollaborateResult> { - match field_orders { - None => Ok(self.grid_meta.fields.clone()), - Some(field_orders) => { - let field_by_field_id = self - .grid_meta - .fields - .iter() - .map(|field| (&field.id, field)) - .collect::>(); - - let fields = field_orders - .iter() - .flat_map(|field_order| match field_by_field_id.get(&field_order.field_id) { - None => { - tracing::error!("Can't find the field with id: {}", field_order.field_id); - None - } - Some(field) => Some((*field).clone()), - }) - .collect::>(); - Ok(fields) - } - } - } - - pub fn create_block_meta(&mut self, block: GridBlockMetaSnapshot) -> CollaborateResult> { - self.modify_grid(|grid_meta| { - if grid_meta.blocks.iter().any(|b| b.block_id == block.block_id) { - tracing::warn!("Duplicate grid block"); - Ok(None) - } else { - match grid_meta.blocks.last() { - None => grid_meta.blocks.push(block), - Some(last_block) => { - if last_block.start_row_index > block.start_row_index - && last_block.len() > block.start_row_index - { - let msg = "GridBlock's start_row_index should be greater than the last_block's start_row_index and its len".to_string(); - return Err(CollaborateError::internal().context(msg)) - } - grid_meta.blocks.push(block); - } - } - Ok(Some(())) - } - }) - } - - pub fn get_block_metas(&self) -> Vec { - self.grid_meta.blocks.clone() - } - - pub fn update_block_meta(&mut self, changeset: GridBlockInfoChangeset) -> CollaborateResult> { - let block_id = changeset.block_id.clone(); - self.modify_block(&block_id, |block| { - let mut is_changed = None; - - if let Some(row_count) = changeset.row_count { - block.row_count = row_count; - is_changed = Some(()); - } - - if let Some(start_row_index) = changeset.start_row_index { - block.start_row_index = start_row_index; - is_changed = Some(()); - } - - Ok(is_changed) - }) - } - - pub fn md5(&self) -> String { - md5(&self.delta.to_delta_bytes()) - } - - pub fn delta_str(&self) -> String { - self.delta.to_delta_str() - } - - pub fn delta_bytes(&self) -> Bytes { - self.delta.to_delta_bytes() - } - - pub fn fields(&self) -> &[FieldMeta] { - &self.grid_meta.fields - } - - fn modify_grid(&mut self, f: F) -> CollaborateResult> - where - F: FnOnce(&mut GridMeta) -> CollaborateResult>, - { - let cloned_grid = self.grid_meta.clone(); - match f(Arc::make_mut(&mut self.grid_meta))? { - None => Ok(None), - Some(_) => { - let old = json_from_grid(&cloned_grid)?; - let new = json_from_grid(&self.grid_meta)?; - match cal_diff::(old, new) { - None => Ok(None), - Some(delta) => { - self.delta = self.delta.compose(&delta)?; - Ok(Some(GridChangeset { delta, md5: self.md5() })) - } - } - } - } - } - - pub fn modify_block(&mut self, block_id: &str, f: F) -> CollaborateResult> - where - F: FnOnce(&mut GridBlockMetaSnapshot) -> CollaborateResult>, - { - self.modify_grid( - |grid_meta| match grid_meta.blocks.iter().position(|block| block.block_id == block_id) { - None => { - tracing::warn!("[GridMetaPad]: Can't find any block with id: {}", block_id); - Ok(None) - } - Some(index) => f(&mut grid_meta.blocks[index]), - }, - ) - } - - pub fn modify_field(&mut self, field_id: &str, f: F) -> CollaborateResult> - where - F: FnOnce(&mut FieldMeta) -> CollaborateResult>, - { - self.modify_grid( - |grid_meta| match grid_meta.fields.iter().position(|field| field.id == field_id) { - None => { - tracing::warn!("[GridMetaPad]: Can't find any field with id: {}", field_id); - Ok(None) - } - Some(index) => f(&mut grid_meta.fields[index]), - }, - ) - } -} - -fn json_from_grid(grid: &Arc) -> CollaborateResult { - let json = serde_json::to_string(grid) - .map_err(|err| internal_error(format!("Serialize grid to json str failed. {:?}", err)))?; - Ok(json) -} - -pub struct GridChangeset { - pub delta: GridMetaDelta, - /// md5: the md5 of the grid after applying the change. - pub md5: String, -} - -pub fn make_grid_delta(grid_meta: &GridMeta) -> GridMetaDelta { - let json = serde_json::to_string(&grid_meta).unwrap(); - PlainTextDeltaBuilder::new().insert(&json).build() -} - -pub fn make_grid_revisions(user_id: &str, grid_meta: &GridMeta) -> RepeatedRevision { - let delta = make_grid_delta(grid_meta); - let bytes = delta.to_delta_bytes(); - let revision = Revision::initial_revision(user_id, &grid_meta.grid_id, bytes); - revision.into() -} - -impl std::default::Default for GridMetaPad { - fn default() -> Self { - let grid = GridMeta { - grid_id: gen_grid_id(), - fields: vec![], - blocks: vec![], - }; - let delta = make_grid_delta(&grid); - GridMetaPad { - grid_meta: Arc::new(grid), - delta, - } - } -} diff --git a/shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs b/shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs index b0188066ef..ffe1e81e9f 100644 --- a/shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs +++ b/shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs @@ -53,8 +53,11 @@ impl GridRevisionPad { pub fn from_delta(delta: GridRevisionDelta) -> CollaborateResult { let content = delta.content()?; - let grid: GridRevision = serde_json::from_str(&content) - .map_err(|e| CollaborateError::internal().context(format!("Deserialize delta to grid failed: {}", e)))?; + let grid: GridRevision = serde_json::from_str(&content).map_err(|e| { + let msg = format!("Deserialize delta to grid failed: {}", e); + tracing::error!("{}", msg); + CollaborateError::internal().context(msg) + })?; Ok(Self { grid_rev: Arc::new(grid), diff --git a/shared-lib/lib-ot/src/core/operation/builder.rs b/shared-lib/lib-ot/src/core/operation/builder.rs index 9483d4cae7..c4f9cc8277 100644 --- a/shared-lib/lib-ot/src/core/operation/builder.rs +++ b/shared-lib/lib-ot/src/core/operation/builder.rs @@ -4,6 +4,7 @@ use crate::rich_text::RichTextAttributes; pub type RichTextOpBuilder = OperationsBuilder; pub type PlainTextOpBuilder = OperationsBuilder; +#[derive(Default)] pub struct OperationsBuilder { operations: Vec>, } @@ -13,17 +14,17 @@ where T: Attributes, { pub fn new() -> OperationsBuilder { - OperationsBuilder { operations: vec![] } + OperationsBuilder::default() } pub fn retain_with_attributes(mut self, n: usize, attributes: T) -> OperationsBuilder { - let retain = Operation::retain_with_attributes(n.into(), attributes); + let retain = Operation::retain_with_attributes(n, attributes); self.operations.push(retain); self } pub fn retain(mut self, n: usize) -> OperationsBuilder { - let retain = Operation::retain(n.into()); + let retain = Operation::retain(n); self.operations.push(retain); self } @@ -34,13 +35,13 @@ where } pub fn insert_with_attributes(mut self, s: &str, attributes: T) -> OperationsBuilder { - let insert = Operation::insert_with_attributes(s.into(), attributes); + let insert = Operation::insert_with_attributes(s, attributes); self.operations.push(insert); self } pub fn insert(mut self, s: &str) -> OperationsBuilder { - let insert = Operation::insert(s.into()); + let insert = Operation::insert(s); self.operations.push(insert); self }