AppFlowy/shared-lib/flowy-sync/src/client_folder/folder_pad.rs

797 lines
27 KiB
Rust
Raw Normal View History

2022-03-04 10:11:12 +00:00
use crate::util::cal_diff;
2022-01-16 05:44:14 +00:00
use crate::{
client_folder::builder::FolderPadBuilder,
2022-01-21 13:41:24 +00:00
entities::{
folder_info::FolderDelta,
revision::{md5, Revision},
},
2022-01-16 05:44:14 +00:00
errors::{CollaborateError, CollaborateResult},
2022-01-15 15:58:36 +00:00
};
use flowy_folder_data_model::entities::{app::App, trash::Trash, view::View, workspace::Workspace};
2022-03-04 10:11:12 +00:00
use lib_ot::core::*;
2022-03-06 01:03:02 +00:00
2022-01-15 15:58:36 +00:00
use serde::{Deserialize, Serialize};
use std::sync::Arc;
2022-01-16 05:44:14 +00:00
#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
2022-01-17 03:55:36 +00:00
pub struct FolderPad {
2022-01-20 15:51:11 +00:00
pub(crate) workspaces: Vec<Arc<Workspace>>,
pub(crate) trash: Vec<Arc<Trash>>,
2022-01-17 03:55:36 +00:00
#[serde(skip)]
2022-03-04 10:11:12 +00:00
pub(crate) delta: FolderDelta,
2022-01-18 14:56:57 +00:00
}
2022-01-17 03:55:36 +00:00
impl FolderPad {
pub fn new(workspaces: Vec<Workspace>, trash: Vec<Trash>) -> CollaborateResult<Self> {
2022-01-20 15:51:11 +00:00
FolderPadBuilder::new()
.with_workspace(workspaces)
.with_trash(trash)
.build()
}
2022-01-16 05:44:14 +00:00
pub fn from_revisions(revisions: Vec<Revision>) -> CollaborateResult<Self> {
2022-01-20 15:51:11 +00:00
FolderPadBuilder::new().build_with_revisions(revisions)
2022-01-16 05:44:14 +00:00
}
2022-01-24 09:35:58 +00:00
pub fn from_delta(delta: FolderDelta) -> CollaborateResult<Self> {
FolderPadBuilder::new().build_with_delta(delta)
}
2022-01-16 05:44:14 +00:00
2022-01-24 09:35:58 +00:00
pub fn delta(&self) -> &FolderDelta {
2022-03-04 10:11:12 +00:00
&self.delta
2022-01-24 09:35:58 +00:00
}
2022-01-21 13:41:24 +00:00
pub fn reset_folder(&mut self, delta: FolderDelta) -> CollaborateResult<String> {
let folder = FolderPad::from_delta(delta)?;
self.workspaces = folder.workspaces;
self.trash = folder.trash;
2022-03-04 10:11:12 +00:00
self.delta = folder.delta;
2022-01-21 13:41:24 +00:00
Ok(self.md5())
}
pub fn compose_remote_delta(&mut self, delta: FolderDelta) -> CollaborateResult<String> {
2022-03-04 10:11:12 +00:00
let composed_delta = self.delta.compose(&delta)?;
2022-01-21 13:41:24 +00:00
self.reset_folder(composed_delta)
}
2022-01-24 09:35:58 +00:00
pub fn is_empty(&self) -> bool {
self.workspaces.is_empty() && self.trash.is_empty()
}
2022-01-28 02:56:55 +00:00
#[tracing::instrument(level = "trace", skip(self, workspace), fields(workspace_name=%workspace.name), err)]
2022-01-18 14:56:57 +00:00
pub fn create_workspace(&mut self, workspace: Workspace) -> CollaborateResult<Option<FolderChange>> {
2022-01-15 15:58:36 +00:00
let workspace = Arc::new(workspace);
if self.workspaces.contains(&workspace) {
2022-01-16 05:44:14 +00:00
tracing::warn!("[RootFolder]: Duplicate workspace");
return Ok(None);
2022-01-15 15:58:36 +00:00
}
2022-01-17 03:55:36 +00:00
self.modify_workspaces(move |workspaces| {
2022-01-16 05:44:14 +00:00
workspaces.push(workspace);
Ok(Some(()))
})
2022-01-15 15:58:36 +00:00
}
2022-01-16 05:44:14 +00:00
pub fn update_workspace(
&mut self,
workspace_id: &str,
name: Option<String>,
desc: Option<String>,
2022-01-18 14:56:57 +00:00
) -> CollaborateResult<Option<FolderChange>> {
self.with_workspace(workspace_id, |workspace| {
2022-01-16 05:44:14 +00:00
if let Some(name) = name {
workspace.name = name;
}
if let Some(desc) = desc {
workspace.desc = desc;
}
Ok(Some(()))
})
}
2022-01-17 03:55:36 +00:00
pub fn read_workspaces(&self, workspace_id: Option<String>) -> CollaborateResult<Vec<Workspace>> {
match workspace_id {
None => {
let workspaces = self
.workspaces
.iter()
.map(|workspace| workspace.as_ref().clone())
.collect::<Vec<Workspace>>();
Ok(workspaces)
2022-01-24 09:35:58 +00:00
}
2022-01-17 03:55:36 +00:00
Some(workspace_id) => {
if let Some(workspace) = self.workspaces.iter().find(|workspace| workspace.id == workspace_id) {
Ok(vec![workspace.as_ref().clone()])
} else {
Err(CollaborateError::record_not_found()
.context(format!("Can't find workspace with id {}", workspace_id)))
}
2022-01-24 09:35:58 +00:00
}
2022-01-17 03:55:36 +00:00
}
}
2022-01-28 02:56:55 +00:00
#[tracing::instrument(level = "trace", skip(self), err)]
2022-01-18 14:56:57 +00:00
pub fn delete_workspace(&mut self, workspace_id: &str) -> CollaborateResult<Option<FolderChange>> {
2022-01-17 03:55:36 +00:00
self.modify_workspaces(|workspaces| {
2022-01-16 05:44:14 +00:00
workspaces.retain(|w| w.id != workspace_id);
Ok(Some(()))
})
}
2022-01-28 02:56:55 +00:00
#[tracing::instrument(level = "trace", skip(self), fields(app_name=%app.name), err)]
2022-01-18 14:56:57 +00:00
pub fn create_app(&mut self, app: App) -> CollaborateResult<Option<FolderChange>> {
2022-01-16 05:44:14 +00:00
let workspace_id = app.workspace_id.clone();
2022-01-18 14:56:57 +00:00
self.with_workspace(&workspace_id, move |workspace| {
2022-01-16 05:44:14 +00:00
if workspace.apps.contains(&app) {
tracing::warn!("[RootFolder]: Duplicate app");
return Ok(None);
}
workspace.apps.push(app);
Ok(Some(()))
})
}
2022-01-17 03:55:36 +00:00
pub fn read_app(&self, app_id: &str) -> CollaborateResult<App> {
for workspace in &self.workspaces {
if let Some(app) = workspace.apps.iter().find(|app| app.id == app_id) {
return Ok(app.clone());
}
}
Err(CollaborateError::record_not_found().context(format!("Can't find app with id {}", app_id)))
}
2022-01-16 05:44:14 +00:00
pub fn update_app(
&mut self,
app_id: &str,
name: Option<String>,
desc: Option<String>,
2022-01-18 14:56:57 +00:00
) -> CollaborateResult<Option<FolderChange>> {
self.with_app(app_id, move |app| {
2022-01-16 05:44:14 +00:00
if let Some(name) = name {
app.name = name;
}
if let Some(desc) = desc {
app.desc = desc;
}
Ok(Some(()))
})
}
2022-01-28 02:56:55 +00:00
#[tracing::instrument(level = "trace", skip(self), err)]
2022-01-18 14:56:57 +00:00
pub fn delete_app(&mut self, app_id: &str) -> CollaborateResult<Option<FolderChange>> {
2022-01-17 03:55:36 +00:00
let app = self.read_app(app_id)?;
2022-01-18 14:56:57 +00:00
self.with_workspace(&app.workspace_id, |workspace| {
2022-01-17 03:55:36 +00:00
workspace.apps.retain(|app| app.id != app_id);
2022-01-16 05:44:14 +00:00
Ok(Some(()))
})
}
2022-01-28 02:56:55 +00:00
#[tracing::instrument(level = "trace", skip(self), fields(view_name=%view.name), err)]
2022-01-18 14:56:57 +00:00
pub fn create_view(&mut self, view: View) -> CollaborateResult<Option<FolderChange>> {
2022-01-16 05:44:14 +00:00
let app_id = view.belong_to_id.clone();
2022-01-18 14:56:57 +00:00
self.with_app(&app_id, move |app| {
2022-01-16 05:44:14 +00:00
if app.belongings.contains(&view) {
tracing::warn!("[RootFolder]: Duplicate view");
return Ok(None);
}
app.belongings.push(view);
Ok(Some(()))
})
}
2022-01-17 03:55:36 +00:00
pub fn read_view(&self, view_id: &str) -> CollaborateResult<View> {
for workspace in &self.workspaces {
for app in &(*workspace.apps) {
if let Some(view) = app.belongings.iter().find(|b| b.id == view_id) {
return Ok(view.clone());
}
}
}
Err(CollaborateError::record_not_found().context(format!("Can't find view with id {}", view_id)))
}
pub fn read_views(&self, belong_to_id: &str) -> CollaborateResult<Vec<View>> {
for workspace in &self.workspaces {
for app in &(*workspace.apps) {
if app.id == belong_to_id {
2022-01-18 14:56:57 +00:00
return Ok(app.belongings.clone().take_items());
2022-01-17 03:55:36 +00:00
}
}
}
2022-01-18 14:56:57 +00:00
Ok(vec![])
2022-01-17 03:55:36 +00:00
}
2022-01-16 05:44:14 +00:00
pub fn update_view(
&mut self,
view_id: &str,
name: Option<String>,
desc: Option<String>,
modified_time: i64,
2022-01-18 14:56:57 +00:00
) -> CollaborateResult<Option<FolderChange>> {
2022-01-17 03:55:36 +00:00
let view = self.read_view(view_id)?;
2022-01-18 14:56:57 +00:00
self.with_view(&view.belong_to_id, view_id, |view| {
2022-01-15 15:58:36 +00:00
if let Some(name) = name {
2022-01-16 05:44:14 +00:00
view.name = name;
2022-01-15 15:58:36 +00:00
}
if let Some(desc) = desc {
2022-01-16 05:44:14 +00:00
view.desc = desc;
}
view.modified_time = modified_time;
Ok(Some(()))
})
}
2022-01-28 02:56:55 +00:00
#[tracing::instrument(level = "trace", skip(self), err)]
2022-01-18 14:56:57 +00:00
pub fn delete_view(&mut self, view_id: &str) -> CollaborateResult<Option<FolderChange>> {
2022-01-17 03:55:36 +00:00
let view = self.read_view(view_id)?;
2022-01-18 14:56:57 +00:00
self.with_app(&view.belong_to_id, |app| {
2022-01-17 03:55:36 +00:00
app.belongings.retain(|view| view.id != view_id);
2022-01-16 05:44:14 +00:00
Ok(Some(()))
})
}
2022-01-18 14:56:57 +00:00
pub fn create_trash(&mut self, trash: Vec<Trash>) -> CollaborateResult<Option<FolderChange>> {
self.with_trash(|t| {
2022-01-17 03:55:36 +00:00
let mut new_trash = trash.into_iter().map(Arc::new).collect::<Vec<Arc<Trash>>>();
t.append(&mut new_trash);
2022-01-16 05:44:14 +00:00
Ok(Some(()))
})
}
2022-01-17 03:55:36 +00:00
pub fn read_trash(&self, trash_id: Option<String>) -> CollaborateResult<Vec<Trash>> {
match trash_id {
None => Ok(self.trash.iter().map(|t| t.as_ref().clone()).collect::<Vec<Trash>>()),
Some(trash_id) => match self.trash.iter().find(|t| t.id == trash_id) {
Some(trash) => Ok(vec![trash.as_ref().clone()]),
None => Ok(vec![]),
},
}
}
2022-01-18 14:56:57 +00:00
pub fn delete_trash(&mut self, trash_ids: Option<Vec<String>>) -> CollaborateResult<Option<FolderChange>> {
2022-01-17 03:55:36 +00:00
match trash_ids {
2022-01-18 14:56:57 +00:00
None => self.with_trash(|trash| {
2022-01-17 03:55:36 +00:00
trash.clear();
Ok(Some(()))
}),
2022-01-18 14:56:57 +00:00
Some(trash_ids) => self.with_trash(|trash| {
2022-01-17 03:55:36 +00:00
trash.retain(|t| !trash_ids.contains(&t.id));
Ok(Some(()))
}),
}
2022-01-16 05:44:14 +00:00
}
2022-01-17 03:55:36 +00:00
2022-01-24 09:35:58 +00:00
pub fn md5(&self) -> String {
2022-03-15 11:00:28 +00:00
md5(&self.delta.to_delta_bytes())
2022-01-24 09:35:58 +00:00
}
2022-01-26 15:29:18 +00:00
pub fn to_json(&self) -> CollaborateResult<String> {
serde_json::to_string(self)
.map_err(|e| CollaborateError::internal().context(format!("serial trash to json failed: {}", e)))
}
2022-01-16 05:44:14 +00:00
}
2022-01-17 03:55:36 +00:00
impl FolderPad {
2022-01-18 14:56:57 +00:00
fn modify_workspaces<F>(&mut self, f: F) -> CollaborateResult<Option<FolderChange>>
2022-01-16 05:44:14 +00:00
where
2022-01-17 03:55:36 +00:00
F: FnOnce(&mut Vec<Arc<Workspace>>) -> CollaborateResult<Option<()>>,
2022-01-16 05:44:14 +00:00
{
let cloned_self = self.clone();
2022-01-17 03:55:36 +00:00
match f(&mut self.workspaces)? {
2022-01-16 05:44:14 +00:00
None => Ok(None),
Some(_) => {
let old = cloned_self.to_json()?;
let new = self.to_json()?;
2022-03-04 10:11:12 +00:00
match cal_diff::<PlainTextAttributes>(old, new) {
None => Ok(None),
Some(delta) => {
self.delta = self.delta.compose(&delta)?;
Ok(Some(FolderChange { delta, md5: self.md5() }))
}
}
2022-01-24 09:35:58 +00:00
}
2022-01-16 05:44:14 +00:00
}
}
2022-01-18 14:56:57 +00:00
fn with_workspace<F>(&mut self, workspace_id: &str, f: F) -> CollaborateResult<Option<FolderChange>>
2022-01-16 05:44:14 +00:00
where
2022-01-17 03:55:36 +00:00
F: FnOnce(&mut Workspace) -> CollaborateResult<Option<()>>,
2022-01-16 05:44:14 +00:00
{
2022-01-17 03:55:36 +00:00
self.modify_workspaces(|workspaces| {
2022-01-16 05:44:14 +00:00
if let Some(workspace) = workspaces.iter_mut().find(|workspace| workspace_id == workspace.id) {
2022-01-17 03:55:36 +00:00
f(Arc::make_mut(workspace))
2022-01-16 05:44:14 +00:00
} else {
2022-03-10 13:43:23 +00:00
tracing::warn!("[FolderPad]: Can't find any workspace with id: {}", workspace_id);
2022-01-16 05:44:14 +00:00
Ok(None)
2022-01-15 15:58:36 +00:00
}
2022-01-16 05:44:14 +00:00
})
}
2022-01-18 14:56:57 +00:00
fn with_trash<F>(&mut self, f: F) -> CollaborateResult<Option<FolderChange>>
2022-01-16 05:44:14 +00:00
where
F: FnOnce(&mut Vec<Arc<Trash>>) -> CollaborateResult<Option<()>>,
{
let cloned_self = self.clone();
match f(&mut self.trash)? {
None => Ok(None),
Some(_) => {
let old = cloned_self.to_json()?;
let new = self.to_json()?;
2022-03-04 10:11:12 +00:00
match cal_diff::<PlainTextAttributes>(old, new) {
None => Ok(None),
Some(delta) => {
self.delta = self.delta.compose(&delta)?;
Ok(Some(FolderChange { delta, md5: self.md5() }))
}
}
2022-01-24 09:35:58 +00:00
}
2022-01-15 15:58:36 +00:00
}
}
2022-01-18 14:56:57 +00:00
fn with_app<F>(&mut self, app_id: &str, f: F) -> CollaborateResult<Option<FolderChange>>
2022-01-16 05:44:14 +00:00
where
2022-01-17 03:55:36 +00:00
F: FnOnce(&mut App) -> CollaborateResult<Option<()>>,
2022-01-16 05:44:14 +00:00
{
let workspace_id = match self
.workspaces
.iter()
.find(|workspace| workspace.apps.iter().any(|app| app.id == app_id))
{
None => {
2022-03-10 13:43:23 +00:00
tracing::warn!("[FolderPad]: Can't find any app with id: {}", app_id);
2022-01-16 05:44:14 +00:00
return Ok(None);
2022-01-24 09:35:58 +00:00
}
2022-01-16 05:44:14 +00:00
Some(workspace) => workspace.id.clone(),
};
2022-01-18 14:56:57 +00:00
self.with_workspace(&workspace_id, |workspace| {
// It's ok to unwrap because we get the workspace from the app_id.
2022-01-17 03:55:36 +00:00
f(workspace.apps.iter_mut().find(|app| app_id == app.id).unwrap())
2022-01-16 05:44:14 +00:00
})
}
2022-01-18 14:56:57 +00:00
fn with_view<F>(&mut self, belong_to_id: &str, view_id: &str, f: F) -> CollaborateResult<Option<FolderChange>>
2022-01-16 05:44:14 +00:00
where
2022-01-17 03:55:36 +00:00
F: FnOnce(&mut View) -> CollaborateResult<Option<()>>,
2022-01-16 05:44:14 +00:00
{
2022-01-18 14:56:57 +00:00
self.with_app(belong_to_id, |app| {
2022-01-16 05:44:14 +00:00
match app.belongings.iter_mut().find(|view| view_id == view.id) {
None => {
2022-03-10 13:43:23 +00:00
tracing::warn!("[FolderPad]: Can't find any view with id: {}", view_id);
2022-01-16 05:44:14 +00:00
Ok(None)
2022-01-24 09:35:58 +00:00
}
2022-01-17 03:55:36 +00:00
Some(view) => f(view),
2022-01-16 05:44:14 +00:00
}
})
}
2022-01-15 15:58:36 +00:00
}
2022-03-04 10:11:12 +00:00
pub fn default_folder_delta() -> FolderDelta {
PlainTextDeltaBuilder::new()
.insert(r#"{"workspaces":[],"trash":[]}"#)
.build()
}
pub fn initial_folder_delta(folder_pad: &FolderPad) -> CollaborateResult<FolderDelta> {
let json = folder_pad.to_json()?;
let delta = PlainTextDeltaBuilder::new().insert(&json).build();
Ok(delta)
}
impl std::default::Default for FolderPad {
fn default() -> Self {
FolderPad {
workspaces: vec![],
trash: vec![],
delta: default_folder_delta(),
2022-01-15 15:58:36 +00:00
}
}
2022-03-04 10:11:12 +00:00
}
pub struct FolderChange {
pub delta: FolderDelta,
/// md5: the md5 of the FolderPad's delta after applying the change.
pub md5: String,
2022-01-15 15:58:36 +00:00
}
#[cfg(test)]
mod tests {
2022-01-16 05:44:14 +00:00
#![allow(clippy::all)]
use crate::{client_folder::folder_pad::FolderPad, entities::folder_info::FolderDelta};
2022-01-15 15:58:36 +00:00
use chrono::Utc;
use flowy_folder_data_model::entities::{app::App, trash::Trash, view::View, workspace::Workspace};
2022-02-28 14:38:53 +00:00
use lib_ot::core::{OperationTransformable, PlainTextDelta, PlainTextDeltaBuilder};
2022-01-15 15:58:36 +00:00
#[test]
2022-01-16 05:44:14 +00:00
fn folder_add_workspace() {
let (mut folder, initial_delta, _) = test_folder();
let _time = Utc::now();
let mut workspace_1 = Workspace::default();
workspace_1.name = "My first workspace".to_owned();
2022-01-18 14:56:57 +00:00
let delta_1 = folder.create_workspace(workspace_1).unwrap().unwrap().delta;
2022-01-15 15:58:36 +00:00
2022-01-16 05:44:14 +00:00
let mut workspace_2 = Workspace::default();
workspace_2.name = "My second workspace".to_owned();
2022-01-18 14:56:57 +00:00
let delta_2 = folder.create_workspace(workspace_2).unwrap().unwrap().delta;
2022-01-15 15:58:36 +00:00
2022-01-16 05:44:14 +00:00
let folder_from_delta = make_folder_from_delta(initial_delta, vec![delta_1, delta_2]);
assert_eq!(folder, folder_from_delta);
2022-01-15 15:58:36 +00:00
}
#[test]
2022-01-16 05:44:14 +00:00
fn folder_update_workspace() {
let (mut folder, initial_delta, workspace) = test_folder();
2022-01-18 14:56:57 +00:00
assert_folder_equal(
&folder,
&make_folder_from_delta(initial_delta.clone(), vec![]),
r#"{"workspaces":[{"id":"1","name":"😁 my first workspace","desc":"","apps":[],"modified_time":0,"create_time":0}],"trash":[]}"#,
);
2022-01-16 05:44:14 +00:00
let delta = folder
2022-01-18 14:56:57 +00:00
.update_workspace(&workspace.id, Some("☺️ rename workspace".to_string()), None)
2022-01-16 05:44:14 +00:00
.unwrap()
2022-01-18 14:56:57 +00:00
.unwrap()
.delta;
2022-01-16 05:44:14 +00:00
let folder_from_delta = make_folder_from_delta(initial_delta, vec![delta]);
2022-01-18 14:56:57 +00:00
assert_folder_equal(
&folder,
&folder_from_delta,
r#"{"workspaces":[{"id":"1","name":"☺️ rename workspace","desc":"","apps":[],"modified_time":0,"create_time":0}],"trash":[]}"#,
);
2022-01-16 05:44:14 +00:00
}
#[test]
fn folder_add_app() {
let (folder, initial_delta, _app) = test_app_folder();
let folder_from_delta = make_folder_from_delta(initial_delta, vec![]);
assert_eq!(folder, folder_from_delta);
2022-01-18 14:56:57 +00:00
assert_folder_equal(
&folder,
&folder_from_delta,
r#"{
"workspaces": [
{
"id": "1",
"name": "😁 my first workspace",
"desc": "",
"apps": [
{
"id": "",
"workspace_id": "1",
"name": "😁 my first app",
"desc": "",
"belongings": [],
"version": 0,
"modified_time": 0,
"create_time": 0
}
],
"modified_time": 0,
"create_time": 0
}
],
"trash": []
}"#,
);
2022-01-16 05:44:14 +00:00
}
#[test]
fn folder_update_app() {
let (mut folder, initial_delta, app) = test_app_folder();
let delta = folder
2022-01-18 14:56:57 +00:00
.update_app(&app.id, Some("🤪 rename app".to_owned()), None)
2022-01-16 05:44:14 +00:00
.unwrap()
2022-01-18 14:56:57 +00:00
.unwrap()
.delta;
let new_folder = make_folder_from_delta(initial_delta, vec![delta]);
assert_folder_equal(
&folder,
&new_folder,
r#"{
"workspaces": [
{
"id": "1",
"name": "😁 my first workspace",
"desc": "",
"apps": [
{
"id": "",
"workspace_id": "1",
"name": "🤪 rename app",
"desc": "",
"belongings": [],
"version": 0,
"modified_time": 0,
"create_time": 0
}
],
"modified_time": 0,
"create_time": 0
}
],
"trash": []
}"#,
);
2022-01-16 05:44:14 +00:00
}
#[test]
fn folder_delete_app() {
let (mut folder, initial_delta, app) = test_app_folder();
2022-01-18 14:56:57 +00:00
let delta = folder.delete_app(&app.id).unwrap().unwrap().delta;
let new_folder = make_folder_from_delta(initial_delta, vec![delta]);
assert_folder_equal(
&folder,
&new_folder,
r#"{
"workspaces": [
{
"id": "1",
"name": "😁 my first workspace",
"desc": "",
"apps": [],
"modified_time": 0,
"create_time": 0
}
],
"trash": []
}"#,
);
2022-01-16 05:44:14 +00:00
}
#[test]
fn folder_add_view() {
let (folder, initial_delta, _view) = test_view_folder();
2022-01-18 14:56:57 +00:00
assert_folder_equal(
&folder,
&make_folder_from_delta(initial_delta, vec![]),
r#"
{
"workspaces": [
{
"id": "1",
"name": "😁 my first workspace",
"desc": "",
"apps": [
{
"id": "",
"workspace_id": "1",
"name": "😁 my first app",
"desc": "",
"belongings": [
{
"id": "",
"belong_to_id": "",
"name": "🎃 my first view",
"desc": "",
"view_type": "Blank",
"version": 0,
"belongings": [],
"modified_time": 0,
"create_time": 0
}
],
"version": 0,
"modified_time": 0,
"create_time": 0
}
],
"modified_time": 0,
"create_time": 0
}
],
"trash": []
}"#,
);
2022-01-16 05:44:14 +00:00
}
#[test]
fn folder_update_view() {
let (mut folder, initial_delta, view) = test_view_folder();
let delta = folder
2022-01-18 14:56:57 +00:00
.update_view(&view.id, Some("😦 rename view".to_owned()), None, 123)
2022-01-16 05:44:14 +00:00
.unwrap()
2022-01-18 14:56:57 +00:00
.unwrap()
.delta;
let new_folder = make_folder_from_delta(initial_delta, vec![delta]);
assert_folder_equal(
&folder,
&new_folder,
r#"{
"workspaces": [
{
"id": "1",
"name": "😁 my first workspace",
"desc": "",
"apps": [
{
"id": "",
"workspace_id": "1",
"name": "😁 my first app",
"desc": "",
"belongings": [
{
"id": "",
"belong_to_id": "",
"name": "😦 rename view",
"desc": "",
"view_type": "Blank",
"version": 0,
"belongings": [],
"modified_time": 123,
"create_time": 0
}
],
"version": 0,
"modified_time": 0,
"create_time": 0
}
],
"modified_time": 0,
"create_time": 0
}
],
"trash": []
}"#,
);
2022-01-16 05:44:14 +00:00
}
#[test]
fn folder_delete_view() {
let (mut folder, initial_delta, view) = test_view_folder();
2022-01-18 14:56:57 +00:00
let delta = folder.delete_view(&view.id).unwrap().unwrap().delta;
let new_folder = make_folder_from_delta(initial_delta, vec![delta]);
assert_folder_equal(
&folder,
&new_folder,
r#"{
"workspaces": [
{
"id": "1",
"name": "😁 my first workspace",
"desc": "",
"apps": [
{
"id": "",
"workspace_id": "1",
"name": "😁 my first app",
"desc": "",
"belongings": [],
"version": 0,
"modified_time": 0,
"create_time": 0
}
],
"modified_time": 0,
"create_time": 0
}
],
"trash": []
}"#,
);
}
2022-01-16 05:44:14 +00:00
2022-01-18 14:56:57 +00:00
#[test]
fn folder_add_trash() {
let (folder, initial_delta, _trash) = test_trash();
assert_folder_equal(
&folder,
&make_folder_from_delta(initial_delta, vec![]),
r#"{
"workspaces": [],
"trash": [
{
"id": "1",
"name": "🚽 my first trash",
"modified_time": 0,
"create_time": 0,
"ty": "Unknown"
}
]
}
"#,
);
}
#[test]
fn folder_delete_trash() {
let (mut folder, initial_delta, trash) = test_trash();
let delta = folder.delete_trash(Some(vec![trash.id])).unwrap().unwrap().delta;
assert_folder_equal(
&folder,
&make_folder_from_delta(initial_delta, vec![delta]),
r#"{
"workspaces": [],
"trash": []
}
"#,
);
2022-01-16 05:44:14 +00:00
}
2022-01-21 13:41:24 +00:00
fn test_folder() -> (FolderPad, FolderDelta, Workspace) {
2022-01-17 03:55:36 +00:00
let mut folder = FolderPad::default();
2022-01-16 05:44:14 +00:00
let folder_json = serde_json::to_string(&folder).unwrap();
2022-02-28 14:38:53 +00:00
let mut delta = PlainTextDeltaBuilder::new().insert(&folder_json).build();
2022-01-16 05:44:14 +00:00
let mut workspace = Workspace::default();
2022-01-18 14:56:57 +00:00
workspace.name = "😁 my first workspace".to_owned();
2022-01-16 05:44:14 +00:00
workspace.id = "1".to_owned();
delta = delta
2022-01-18 14:56:57 +00:00
.compose(&folder.create_workspace(workspace.clone()).unwrap().unwrap().delta)
2022-01-16 05:44:14 +00:00
.unwrap();
(folder, delta, workspace)
}
2022-01-21 13:41:24 +00:00
fn test_app_folder() -> (FolderPad, FolderDelta, App) {
2022-01-16 05:44:14 +00:00
let (mut folder, mut initial_delta, workspace) = test_folder();
let mut app = App::default();
app.workspace_id = workspace.id;
2022-01-18 14:56:57 +00:00
app.name = "😁 my first app".to_owned();
2022-01-15 15:58:36 +00:00
2022-01-16 05:44:14 +00:00
initial_delta = initial_delta
2022-01-18 14:56:57 +00:00
.compose(&folder.create_app(app.clone()).unwrap().unwrap().delta)
2022-01-16 05:44:14 +00:00
.unwrap();
2022-01-15 15:58:36 +00:00
2022-01-16 05:44:14 +00:00
(folder, initial_delta, app)
}
2022-01-21 13:41:24 +00:00
fn test_view_folder() -> (FolderPad, FolderDelta, View) {
2022-01-16 05:44:14 +00:00
let (mut folder, mut initial_delta, app) = test_app_folder();
let mut view = View::default();
view.belong_to_id = app.id.clone();
2022-01-18 14:56:57 +00:00
view.name = "🎃 my first view".to_owned();
2022-01-16 05:44:14 +00:00
initial_delta = initial_delta
2022-01-18 14:56:57 +00:00
.compose(&folder.create_view(view.clone()).unwrap().unwrap().delta)
2022-01-16 05:44:14 +00:00
.unwrap();
(folder, initial_delta, view)
}
2022-01-21 13:41:24 +00:00
fn test_trash() -> (FolderPad, FolderDelta, Trash) {
2022-01-18 14:56:57 +00:00
let mut folder = FolderPad::default();
let folder_json = serde_json::to_string(&folder).unwrap();
2022-02-28 14:38:53 +00:00
let mut delta = PlainTextDeltaBuilder::new().insert(&folder_json).build();
2022-01-18 14:56:57 +00:00
let mut trash = Trash::default();
trash.name = "🚽 my first trash".to_owned();
trash.id = "1".to_owned();
delta = delta
.compose(&folder.create_trash(vec![trash.clone()]).unwrap().unwrap().delta)
.unwrap();
(folder, delta, trash)
}
2022-02-28 14:38:53 +00:00
fn make_folder_from_delta(mut initial_delta: FolderDelta, deltas: Vec<PlainTextDelta>) -> FolderPad {
2022-01-16 05:44:14 +00:00
for delta in deltas {
initial_delta = initial_delta.compose(&delta).unwrap();
}
2022-01-17 03:55:36 +00:00
FolderPad::from_delta(initial_delta).unwrap()
2022-01-15 15:58:36 +00:00
}
2022-01-18 14:56:57 +00:00
fn assert_folder_equal(old: &FolderPad, new: &FolderPad, expected: &str) {
assert_eq!(old, new);
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());
assert_eq!(json1, json2);
}
2022-01-15 15:58:36 +00:00
}