nathan 2 роки тому
батько
коміт
9488f42749

+ 1 - 1
shared-lib/flowy-ast/src/ast.rs

@@ -4,7 +4,7 @@
 
 
 use crate::event_attrs::EventEnumAttrs;
 use crate::event_attrs::EventEnumAttrs;
 use crate::node_attrs::NodeStructAttrs;
 use crate::node_attrs::NodeStructAttrs;
-use crate::{is_recognizable_field, pb_attrs, ty_ext::*, ASTResult, PBAttrsContainer, PBStructAttrs, NODE_TYPE};
+use crate::{is_recognizable_field, ty_ext::*, ASTResult, PBAttrsContainer, PBStructAttrs, NODE_TYPE};
 use proc_macro2::Ident;
 use proc_macro2::Ident;
 use syn::Meta::NameValue;
 use syn::Meta::NameValue;
 use syn::{self, punctuated::Punctuated};
 use syn::{self, punctuated::Punctuated};

+ 1 - 3
shared-lib/flowy-ast/src/event_attrs.rs

@@ -1,10 +1,8 @@
 use crate::{get_event_meta_items, parse_lit_str, symbol::*, ASTResult};
 use crate::{get_event_meta_items, parse_lit_str, symbol::*, ASTResult};
 
 
-
 use syn::{
 use syn::{
     self,
     self,
-    parse::{self, Parse},
-    Meta::{List, NameValue, Path},
+    Meta::{NameValue, Path},
     NestedMeta::{Lit, Meta},
     NestedMeta::{Lit, Meta},
 };
 };
 
 

+ 0 - 3
shared-lib/flowy-ast/src/lib.rs

@@ -1,9 +1,6 @@
 #[macro_use]
 #[macro_use]
 extern crate syn;
 extern crate syn;
 
 
-#[macro_use]
-extern crate quote;
-
 mod ast;
 mod ast;
 mod ctxt;
 mod ctxt;
 mod pb_attrs;
 mod pb_attrs;

+ 16 - 17
shared-lib/flowy-ast/src/node_attrs.rs

@@ -1,11 +1,8 @@
-use crate::{get_node_meta_items, get_pb_meta_items, parse_lit_into_expr_path, symbol::*, ASTAttr, ASTResult};
-use proc_macro2::{Group, Ident, Span, TokenStream, TokenTree};
+use crate::{get_node_meta_items, parse_lit_into_expr_path, symbol::*, ASTAttr, ASTResult};
 use quote::ToTokens;
 use quote::ToTokens;
 use syn::{
 use syn::{
-    self,
-    parse::{self, Parse},
-    LitStr,
-    Meta::{List, NameValue, Path},
+    self, LitStr,
+    Meta::NameValue,
     NestedMeta::{Lit, Meta},
     NestedMeta::{Lit, Meta},
 };
 };
 
 
@@ -14,18 +11,20 @@ pub struct NodeStructAttrs {
     pub has_child: bool,
     pub has_child: bool,
     pub child_name: Option<LitStr>,
     pub child_name: Option<LitStr>,
     pub child_index: Option<syn::LitInt>,
     pub child_index: Option<syn::LitInt>,
-    get_node_value_with: Option<syn::ExprPath>,
-    set_node_value_with: Option<syn::ExprPath>,
+    pub get_node_value_with: Option<syn::ExprPath>,
+    pub set_node_value_with: Option<syn::ExprPath>,
+    pub with_children: Option<syn::ExprPath>,
 }
 }
 
 
 impl NodeStructAttrs {
 impl NodeStructAttrs {
     /// Extract out the `#[node(...)]` attributes from a struct field.
     /// Extract out the `#[node(...)]` attributes from a struct field.
-    pub fn from_ast(ast_result: &ASTResult, index: usize, field: &syn::Field) -> Self {
+    pub fn from_ast(ast_result: &ASTResult, _index: usize, field: &syn::Field) -> Self {
         let mut rename = ASTAttr::none(ast_result, RENAME_NODE);
         let mut rename = ASTAttr::none(ast_result, RENAME_NODE);
         let mut child_name = ASTAttr::none(ast_result, CHILD_NODE_NAME);
         let mut child_name = ASTAttr::none(ast_result, CHILD_NODE_NAME);
         let mut child_index = ASTAttr::none(ast_result, CHILD_NODE_INDEX);
         let mut child_index = ASTAttr::none(ast_result, CHILD_NODE_INDEX);
         let mut get_node_value_with = ASTAttr::none(ast_result, GET_NODE_VALUE_WITH);
         let mut get_node_value_with = ASTAttr::none(ast_result, GET_NODE_VALUE_WITH);
         let mut set_node_value_with = ASTAttr::none(ast_result, SET_NODE_VALUE_WITH);
         let mut set_node_value_with = ASTAttr::none(ast_result, SET_NODE_VALUE_WITH);
+        let mut with_children = ASTAttr::none(ast_result, WITH_CHILDREN);
 
 
         for meta_item in field
         for meta_item in field
             .attrs
             .attrs
@@ -69,6 +68,13 @@ impl NodeStructAttrs {
                     }
                     }
                 }
                 }
 
 
+                // Parse `#[node(with_children= "...")]`
+                Meta(NameValue(m)) if m.path == WITH_CHILDREN => {
+                    if let Ok(path) = parse_lit_into_expr_path(ast_result, WITH_CHILDREN, &m.lit) {
+                        with_children.set(&m.path, path);
+                    }
+                }
+
                 Meta(meta_item) => {
                 Meta(meta_item) => {
                     let path = meta_item.path().into_token_stream().to_string().replace(' ', "");
                     let path = meta_item.path().into_token_stream().to_string().replace(' ', "");
                     ast_result.error_spanned_by(meta_item.path(), format!("unknown node field attribute `{}`", path));
                     ast_result.error_spanned_by(meta_item.path(), format!("unknown node field attribute `{}`", path));
@@ -87,14 +93,7 @@ impl NodeStructAttrs {
             child_name,
             child_name,
             get_node_value_with: get_node_value_with.get(),
             get_node_value_with: get_node_value_with.get(),
             set_node_value_with: set_node_value_with.get(),
             set_node_value_with: set_node_value_with.get(),
+            with_children: with_children.get(),
         }
         }
     }
     }
-
-    pub fn set_node_value_with(&self) -> Option<&syn::ExprPath> {
-        self.set_node_value_with.as_ref()
-    }
-
-    pub fn get_node_value_with(&self) -> Option<&syn::ExprPath> {
-        self.get_node_value_with.as_ref()
-    }
 }
 }

+ 1 - 0
shared-lib/flowy-ast/src/symbol.rs

@@ -45,6 +45,7 @@ pub const GET_NODE_VALUE_WITH: Symbol = Symbol("get_value_with");
 pub const SET_NODE_VALUE_WITH: Symbol = Symbol("set_value_with");
 pub const SET_NODE_VALUE_WITH: Symbol = Symbol("set_value_with");
 pub const GET_VEC_ELEMENT_WITH: Symbol = Symbol("get_element_with");
 pub const GET_VEC_ELEMENT_WITH: Symbol = Symbol("get_element_with");
 pub const GET_MUT_VEC_ELEMENT_WITH: Symbol = Symbol("get_mut_element_with");
 pub const GET_MUT_VEC_ELEMENT_WITH: Symbol = Symbol("get_mut_element_with");
+pub const WITH_CHILDREN: Symbol = Symbol("with_children");
 
 
 impl PartialEq<Symbol> for Ident {
 impl PartialEq<Symbol> for Ident {
     fn eq(&self, word: &Symbol) -> bool {
     fn eq(&self, word: &Symbol) -> bool {

+ 3 - 1
shared-lib/flowy-ast/src/ty_ext.rs

@@ -62,7 +62,9 @@ pub fn parse_ty<'a>(ast_result: &ASTResult, ty: &'a syn::Type) -> Result<Option<
                 "Vec" => generate_vec_ty_info(ast_result, seg, bracketed),
                 "Vec" => generate_vec_ty_info(ast_result, seg, bracketed),
                 "Option" => generate_option_ty_info(ast_result, ty, seg, bracketed),
                 "Option" => generate_option_ty_info(ast_result, ty, seg, bracketed),
                 _ => {
                 _ => {
-                    return Err(format!("Unsupported ty {}", seg.ident));
+                    let msg = format!("Unsupported type: {}", seg.ident);
+                    ast_result.error_spanned_by(&seg.ident, &msg);
+                    return Err(msg);
                 }
                 }
             }
             }
         } else {
         } else {

+ 1 - 0
shared-lib/flowy-codegen/src/dart_event/ast.rs

@@ -1,4 +1,5 @@
 use flowy_ast::EventEnumAttrs;
 use flowy_ast::EventEnumAttrs;
+use quote::format_ident;
 
 
 pub struct EventASTContext {
 pub struct EventASTContext {
     pub event: syn::Ident,
     pub event: syn::Ident,

+ 0 - 3
shared-lib/flowy-codegen/src/lib.rs

@@ -15,6 +15,3 @@ pub struct ProtoCache {
     pub structs: Vec<String>,
     pub structs: Vec<String>,
     pub enums: Vec<String>,
     pub enums: Vec<String>,
 }
 }
-
-#[macro_use]
-extern crate quote;

+ 33 - 18
shared-lib/flowy-derive/src/node/mod.rs

@@ -26,8 +26,9 @@ pub fn make_helper_funcs_token_stream(ast: &ASTContainer) -> TokenStream {
     let struct_ident = &ast.ident;
     let struct_ident = &ast.ident;
     token_streams.extend(quote! {
     token_streams.extend(quote! {
       impl #struct_ident {
       impl #struct_ident {
-            pub fn get_path(&self) -> Path {
-                self.tree.read().path_from_node_id(self.node_id.clone())
+            pub fn get_path(&self) -> Option<Path> {
+                let node_id = &self.node_id?;
+               Some(self.tree.read().path_from_node_id(node_id.clone()))
             }
             }
         }
         }
     });
     });
@@ -50,7 +51,6 @@ pub fn make_alter_children_token_stream(ast_result: &ASTResult, ast: &ASTContain
         }
         }
         let children_field = children_fields.first().unwrap();
         let children_field = children_fields.first().unwrap();
         let field_name = children_field.name().unwrap();
         let field_name = children_field.name().unwrap();
-        eprintln!("😄 {:?} {:?}", struct_ident, field_name);
         let child_name = children_field.node_attrs.child_name.as_ref().unwrap();
         let child_name = children_field.node_attrs.child_name.as_ref().unwrap();
         let get_func_name = format_ident!("get_{}", child_name.value());
         let get_func_name = format_ident!("get_{}", child_name.value());
         let get_mut_func_name = format_ident!("get_mut_{}", child_name.value());
         let get_mut_func_name = format_ident!("get_mut_{}", child_name.value());
@@ -60,22 +60,26 @@ pub fn make_alter_children_token_stream(ast_result: &ASTResult, ast: &ASTContain
 
 
         token_streams.extend(quote! {
         token_streams.extend(quote! {
              impl #struct_ident {
              impl #struct_ident {
-                pub fn #get_func_name(&self, id: &str) -> Option<&#ty> {
-                     self.#field_name.iter().find(|element| element.id == id)
+                pub fn #get_func_name<T: AsRef<str>>(&self, id: T) -> Option<&#ty> {
+                    let id = id.as_ref();
+                    self.#field_name.iter().find(|element| element.id == id)
                 }
                 }
 
 
-                pub fn #get_mut_func_name(&mut self, id: &str) -> Option<&mut #ty> {
-                     self.#field_name.iter_mut().find(|element| element.id == id)
+                pub fn #get_mut_func_name<T: AsRef<str>>(&mut self, id: T) -> Option<&mut #ty> {
+                    let id = id.as_ref();
+                    self.#field_name.iter_mut().find(|element| element.id == id)
                 }
                 }
 
 
-                pub fn #remove_func_name(&mut self, id: &str) {
-                     if let Some(index) = self.#field_name.iter().position(|element| element.id == id) {
+                pub fn #remove_func_name<T: AsRef<str>>(&mut self, id: T) {
+                    let id = id.as_ref();
+                     if let Some(index) = self.#field_name.iter().position(|element| element.id == id && element.node_id.is_some()) {
                         let element = self.#field_name.remove(index);
                         let element = self.#field_name.remove(index);
-                        let element_path = element.get_path();
+                        let element_path = element.get_path().unwrap();
 
 
                         let mut write_guard = self.tree.write();
                         let mut write_guard = self.tree.write();
                         let mut nodes = vec![];
                         let mut nodes = vec![];
-                        if let Some(node_data) = write_guard.get_node_data(element.node_id.clone()) {
+
+                        if let Some(node_data) = element.node_id.and_then(|node_id| write_guard.get_node_data(node_id.clone())) {
                             nodes.push(node_data);
                             nodes.push(node_data);
                         }
                         }
                         let _ = write_guard.apply_op(NodeOperation::Delete {
                         let _ = write_guard.apply_op(NodeOperation::Delete {
@@ -85,18 +89,26 @@ pub fn make_alter_children_token_stream(ast_result: &ASTResult, ast: &ASTContain
                     }
                     }
                 }
                 }
 
 
-                pub fn #add_func_name(&mut self, value: #ty) {
+                pub fn #add_func_name(&mut self, mut value: #ty) -> Result<(), String> {
+                    if self.node_id.is_none() {
+                        return Err("The node id is empty".to_owned());
+                    }
+
                     let mut transaction = Transaction::new();
                     let mut transaction = Transaction::new();
-                    let parent_path = self.get_path();
+                    let parent_path = self.get_path().unwrap();
+
                     let path = parent_path.clone_with(self.#field_name.len());
                     let path = parent_path.clone_with(self.#field_name.len());
                     let node_data = value.to_node_data();
                     let node_data = value.to_node_data();
                     transaction.push_operation(NodeOperation::Insert {
                     transaction.push_operation(NodeOperation::Insert {
-                        path,
+                        path: path.clone(),
                         nodes: vec![node_data],
                         nodes: vec![node_data],
                      });
                      });
 
 
                     let _ = self.tree.write().apply_transaction(transaction);
                     let _ = self.tree.write().apply_transaction(transaction);
+                    let child_node_id = self.tree.read().node_id_at_path(path).unwrap();
+                    value.node_id = Some(child_node_id);
                     self.#field_name.push(value);
                     self.#field_name.push(value);
+                    Ok(())
                 }
                 }
              }
              }
         });
         });
@@ -189,21 +201,24 @@ pub fn make_get_set_value_token_steam(ast: &ASTContainer) -> Option<TokenStream>
         let get_value_return_ty = field.ty;
         let get_value_return_ty = field.ty;
         let set_value_input_ty = field.ty;
         let set_value_input_ty = field.ty;
 
 
-        if let Some(get_value_with_fn) = field.node_attrs.get_node_value_with() {
+        if let Some(get_value_with_fn) = &field.node_attrs.get_node_value_with {
             token_streams.extend(quote! {
             token_streams.extend(quote! {
               impl #struct_ident {
               impl #struct_ident {
                     pub fn #get_func_name(&self) -> Option<#get_value_return_ty> {
                     pub fn #get_func_name(&self) -> Option<#get_value_return_ty> {
-                        #get_value_with_fn(self.#tree.clone(), &self.node_id, #field_name_str)
+                        let node_id = self.node_id.as_ref()?;
+                        #get_value_with_fn(self.#tree.clone(), node_id, #field_name_str)
                     }
                     }
                 }
                 }
             });
             });
         }
         }
 
 
-        if let Some(set_value_with_fn) = field.node_attrs.set_node_value_with() {
+        if let Some(set_value_with_fn) = &field.node_attrs.set_node_value_with {
             token_streams.extend(quote! {
             token_streams.extend(quote! {
               impl #struct_ident {
               impl #struct_ident {
                     pub fn #set_func_name(&self, value: #set_value_input_ty) {
                     pub fn #set_func_name(&self, value: #set_value_input_ty) {
-                        let _ = #set_value_with_fn(self.#tree.clone(), &self.node_id, #field_name_str, value);
+                        if let Some(node_id) = self.node_id.as_ref() {
+                            let _ = #set_value_with_fn(self.#tree.clone(), node_id, #field_name_str, value);
+                        }
                     }
                     }
                 }
                 }
             });
             });

+ 0 - 95
shared-lib/flowy-sync/src/client_folder/app_node.rs

@@ -1,95 +0,0 @@
-use crate::client_folder::view_node::ViewNode;
-use crate::client_folder::{get_attributes_str_value, set_attributes_str_value, AtomicNodeTree};
-use crate::errors::CollaborateResult;
-use folder_rev_model::{AppRevision, ViewRevision};
-use lib_ot::core::{NodeData, NodeDataBuilder, NodeOperation, Path, Transaction};
-use std::sync::Arc;
-
-#[derive(Debug, Clone)]
-pub struct AppNode {
-    pub id: String,
-    tree: Arc<AtomicNodeTree>,
-    pub(crate) path: Path,
-    views: Vec<Arc<ViewNode>>,
-}
-
-impl AppNode {
-    pub(crate) fn from_app_revision(
-        transaction: &mut Transaction,
-        revision: AppRevision,
-        tree: Arc<AtomicNodeTree>,
-        path: Path,
-    ) -> CollaborateResult<Self> {
-        let app_id = revision.id.clone();
-        let app_node = NodeDataBuilder::new("app")
-            .insert_attribute("id", revision.id)
-            .insert_attribute("name", revision.name)
-            .insert_attribute("workspace_id", revision.workspace_id)
-            .build();
-
-        transaction.push_operation(NodeOperation::Insert {
-            path: path.clone(),
-            nodes: vec![app_node],
-        });
-
-        let views = revision
-            .belongings
-            .into_iter()
-            .enumerate()
-            .map(|(index, app)| (path.clone_with(index), app))
-            .flat_map(
-                |(path, app)| match ViewNode::from_view_revision(transaction, app, tree.clone(), path) {
-                    Ok(view_node) => Some(Arc::new(view_node)),
-                    Err(err) => {
-                        tracing::error!("create view node failed: {:?}", err);
-                        None
-                    }
-                },
-            )
-            .collect::<Vec<Arc<ViewNode>>>();
-
-        Ok(Self {
-            id: app_id,
-            tree,
-            path,
-            views,
-        })
-    }
-
-    pub fn get_name(&self) -> Option<String> {
-        get_attributes_str_value(self.tree.clone(), &self.path, "name")
-    }
-
-    pub fn set_name(&self, name: String) -> CollaborateResult<()> {
-        set_attributes_str_value(self.tree.clone(), &self.path, "name", name)
-    }
-
-    fn get_workspace_id(&self) -> Option<String> {
-        get_attributes_str_value(self.tree.clone(), &self.path, "workspace_id")
-    }
-
-    fn set_workspace_id(&self, workspace_id: String) -> CollaborateResult<()> {
-        set_attributes_str_value(self.tree.clone(), &self.path, "workspace_id", workspace_id)
-    }
-
-    fn get_view(&self, view_id: &str) -> Option<&Arc<ViewNode>> {
-        todo!()
-    }
-
-    fn get_mut_view(&mut self, view_id: &str) -> Option<&mut Arc<ViewNode>> {
-        todo!()
-    }
-
-    fn add_view(&mut self, revision: ViewRevision) -> CollaborateResult<()> {
-        let mut transaction = Transaction::new();
-        let path = self.path.clone_with(self.views.len());
-        let view_node = ViewNode::from_view_revision(&mut transaction, revision, self.tree.clone(), path)?;
-        let _ = self.tree.write().apply_transaction(transaction)?;
-        self.views.push(Arc::new(view_node));
-        todo!()
-    }
-
-    fn remove_view(&mut self, view_id: &str) {
-        todo!()
-    }
-}

+ 89 - 101
shared-lib/flowy-sync/src/client_folder/folder_node.rs

@@ -1,21 +1,53 @@
+use crate::client_folder::trash_node::TrashNode;
 use crate::client_folder::workspace_node::WorkspaceNode;
 use crate::client_folder::workspace_node::WorkspaceNode;
 use crate::errors::{CollaborateError, CollaborateResult};
 use crate::errors::{CollaborateError, CollaborateResult};
-use folder_rev_model::{AppRevision, ViewRevision, WorkspaceRevision};
-use lib_ot::core::{
-    AttributeEntry, AttributeHashMap, AttributeValue, Changeset, Node, NodeDataBuilder, NodeOperation, NodeTree, Path,
-    Transaction,
-};
+use flowy_derive::Node;
+use lib_ot::core::NodeTree;
+use lib_ot::core::*;
 use parking_lot::RwLock;
 use parking_lot::RwLock;
-use std::string::ToString;
 use std::sync::Arc;
 use std::sync::Arc;
 
 
 pub type AtomicNodeTree = RwLock<NodeTree>;
 pub type AtomicNodeTree = RwLock<NodeTree>;
 
 
 pub struct FolderNodePad {
 pub struct FolderNodePad {
-    tree: Arc<AtomicNodeTree>,
-    // name: workspaces, index of the node,
-    workspaces: Vec<Arc<WorkspaceNode>>,
-    trash: Vec<Arc<TrashNode>>,
+    pub tree: Arc<AtomicNodeTree>,
+    pub node_id: NodeId,
+    pub workspaces: WorkspaceList,
+    pub trash: TrashList,
+}
+
+#[derive(Clone, Node)]
+#[node_type = "workspaces"]
+pub struct WorkspaceList {
+    pub tree: Arc<AtomicNodeTree>,
+    pub node_id: Option<NodeId>,
+
+    #[node(child_name = "workspace")]
+    inner: Vec<WorkspaceNode>,
+}
+
+impl std::ops::Deref for WorkspaceList {
+    type Target = Vec<WorkspaceNode>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl std::ops::DerefMut for WorkspaceList {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.inner
+    }
+}
+
+#[derive(Clone, Node)]
+#[node_type = "trash"]
+pub struct TrashList {
+    pub tree: Arc<AtomicNodeTree>,
+    pub node_id: Option<NodeId>,
+
+    #[node(child_name = "trash")]
+    inner: Vec<TrashNode>,
 }
 }
 
 
 impl FolderNodePad {
 impl FolderNodePad {
@@ -23,42 +55,27 @@ impl FolderNodePad {
         Self::default()
         Self::default()
     }
     }
 
 
-    pub fn get_workspace(&self, workspace_id: &str) -> Option<&Arc<WorkspaceNode>> {
+    pub fn get_workspace(&self, workspace_id: &str) -> Option<&WorkspaceNode> {
         self.workspaces.iter().find(|workspace| workspace.id == workspace_id)
         self.workspaces.iter().find(|workspace| workspace.id == workspace_id)
     }
     }
 
 
-    pub fn get_mut_workspace(&mut self, workspace_id: &str) -> Option<&mut Arc<WorkspaceNode>> {
+    pub fn get_mut_workspace(&mut self, workspace_id: &str) -> Option<&mut WorkspaceNode> {
         self.workspaces
         self.workspaces
             .iter_mut()
             .iter_mut()
             .find(|workspace| workspace.id == workspace_id)
             .find(|workspace| workspace.id == workspace_id)
     }
     }
 
 
-    pub fn remove_workspace(&mut self, workspace_id: &str) {
-        if let Some(workspace) = self.workspaces.iter().find(|workspace| workspace.id == workspace_id) {
-            let mut write_guard = self.tree.write();
-            let mut nodes = vec![];
-
-            if let Some(node_data) = write_guard.get_node_data_at_path(&workspace.path) {
-                nodes.push(node_data);
-            }
-            let _ = write_guard.apply_op(NodeOperation::Delete {
-                path: workspace.path.clone(),
-                nodes,
-            });
-        }
-    }
+    pub fn add_workspace(&mut self, mut workspace: WorkspaceNode) {
+        let path = workspaces_path().clone_with(self.workspaces.len());
+        let op = NodeOperation::Insert {
+            path: path.clone(),
+            nodes: vec![workspace.to_node_data()],
+        };
+        self.tree.write().apply_op(op).unwrap();
 
 
-    pub fn add_workspace(&mut self, revision: WorkspaceRevision) -> CollaborateResult<()> {
-        let mut transaction = Transaction::new();
-        let node = WorkspaceNode::from_workspace_revision(
-            &mut transaction,
-            revision,
-            self.tree.clone(),
-            workspaces_path().clone_with(self.workspaces.len()),
-        )?;
-        let _ = self.tree.write().apply_transaction(transaction)?;
-        self.workspaces.push(Arc::new(node));
-        Ok(())
+        let node_id = self.tree.read().node_id_at_path(path).unwrap();
+        workspace.node_id = Some(node_id);
+        self.workspaces.push(workspace);
     }
     }
 
 
     pub fn to_json(&self, pretty: bool) -> CollaborateResult<String> {
     pub fn to_json(&self, pretty: bool) -> CollaborateResult<String> {
@@ -69,65 +86,26 @@ impl FolderNodePad {
     }
     }
 }
 }
 
 
-fn folder_path() -> Path {
-    vec![0].into()
-}
-
-fn workspaces_path() -> Path {
-    folder_path().clone_with(0)
-}
-
-fn trash_path() -> Path {
-    folder_path().clone_with(1)
-}
-
-pub fn get_attributes(tree: Arc<AtomicNodeTree>, path: &Path) -> Option<AttributeHashMap> {
-    tree.read()
-        .get_node_at_path(&path)
-        .and_then(|node| Some(node.attributes.clone()))
-}
-
-pub fn get_attributes_value(tree: Arc<AtomicNodeTree>, path: &Path, key: &str) -> Option<AttributeValue> {
-    tree.read()
-        .get_node_at_path(&path)
-        .and_then(|node| node.attributes.get(key).cloned())
-}
+impl std::default::Default for FolderNodePad {
+    fn default() -> Self {
+        let tree = Arc::new(RwLock::new(NodeTree::default()));
 
 
-pub fn get_attributes_str_value(tree: Arc<AtomicNodeTree>, path: &Path, key: &str) -> Option<String> {
-    tree.read()
-        .get_node_at_path(&path)
-        .and_then(|node| node.attributes.get(key).cloned())
-        .and_then(|value| value.str_value())
-}
+        // Workspace
+        let mut workspaces = WorkspaceList {
+            tree: tree.clone(),
+            node_id: None,
+            inner: vec![],
+        };
+        let workspace_node = workspaces.to_node_data();
 
 
-pub fn set_attributes_str_value(
-    tree: Arc<AtomicNodeTree>,
-    path: &Path,
-    key: &str,
-    value: String,
-) -> CollaborateResult<()> {
-    let old_attributes = match get_attributes(tree.clone(), path) {
-        None => AttributeHashMap::new(),
-        Some(attributes) => attributes,
-    };
-    let mut new_attributes = old_attributes.clone();
-    new_attributes.insert(key, value);
-
-    let update_operation = NodeOperation::Update {
-        path: path.clone(),
-        changeset: Changeset::Attributes {
-            new: new_attributes,
-            old: old_attributes,
-        },
-    };
-    let _ = tree.write().apply_op(update_operation)?;
-    Ok(())
-}
+        // Trash
+        let mut trash = TrashList {
+            tree: tree.clone(),
+            node_id: None,
+            inner: vec![],
+        };
+        let trash_node = trash.to_node_data();
 
 
-impl std::default::Default for FolderNodePad {
-    fn default() -> Self {
-        let workspace_node = NodeDataBuilder::new("workspaces").build();
-        let trash_node = NodeDataBuilder::new("trash").build();
         let folder_node = NodeDataBuilder::new("folder")
         let folder_node = NodeDataBuilder::new("folder")
             .add_node_data(workspace_node)
             .add_node_data(workspace_node)
             .add_node_data(trash_node)
             .add_node_data(trash_node)
@@ -137,18 +115,28 @@ impl std::default::Default for FolderNodePad {
             path: folder_path(),
             path: folder_path(),
             nodes: vec![folder_node],
             nodes: vec![folder_node],
         };
         };
-        let mut tree = NodeTree::default();
-        let _ = tree.apply_op(operation).unwrap();
+        let _ = tree.write().apply_op(operation).unwrap();
+        let node_id = tree.read().node_id_at_path(folder_path()).unwrap();
+        workspaces.node_id = Some(tree.read().node_id_at_path(workspaces_path()).unwrap());
+        trash.node_id = Some(tree.read().node_id_at_path(trash_path()).unwrap());
 
 
         Self {
         Self {
-            tree: Arc::new(RwLock::new(tree)),
-            workspaces: vec![],
-            trash: vec![],
+            tree,
+            node_id,
+            workspaces,
+            trash,
         }
         }
     }
     }
 }
 }
 
 
-pub struct TrashNode {
-    tree: Arc<AtomicNodeTree>,
-    parent_path: Path,
+fn folder_path() -> Path {
+    vec![0].into()
+}
+
+fn workspaces_path() -> Path {
+    folder_path().clone_with(0)
+}
+
+fn trash_path() -> Path {
+    folder_path().clone_with(1)
 }
 }

+ 0 - 51
shared-lib/flowy-sync/src/client_folder/folder_node_2.rs

@@ -1,51 +0,0 @@
-use crate::client_folder::workspace_node_2::WorkspaceNode2;
-use crate::errors::{CollaborateError, CollaborateResult};
-use flowy_derive::Node;
-use lib_ot::core::NodeTree;
-use lib_ot::core::*;
-use parking_lot::RwLock;
-use std::sync::Arc;
-
-pub type AtomicNodeTree = RwLock<NodeTree>;
-
-#[derive(Node)]
-#[node_type = "folder"]
-pub struct FolderNodePad2 {
-    tree: Arc<AtomicNodeTree>,
-    node_id: NodeId,
-    // name: workspaces, index of the node,
-    #[node(child_name = "child")]
-    children: Vec<Box<dyn ToNodeData>>,
-}
-
-impl FolderNodePad2 {
-    pub fn new() -> Self {
-        // let workspace_node = NodeDataBuilder::new("workspaces").build();
-        // let trash_node = NodeDataBuilder::new("trash").build();
-        // let folder_node = NodeDataBuilder::new("folder")
-        //     .add_node_data(workspace_node)
-        //     .add_node_data(trash_node)
-        //     .build();
-        //
-        // let operation = NodeOperation::Insert {
-        //     path: folder_path(),
-        //     nodes: vec![folder_node],
-        // };
-        // let mut tree = NodeTree::default();
-        // let _ = tree.apply_op(operation).unwrap();
-        //
-        // Self {
-        //     tree: Arc::new(RwLock::new(tree)),
-        //     workspaces: vec![],
-        //     trash: vec![],
-        // }
-        todo!()
-    }
-
-    pub fn to_json(&self, pretty: bool) -> CollaborateResult<String> {
-        self.tree
-            .read()
-            .to_json(pretty)
-            .map_err(|e| CollaborateError::serde().context(e))
-    }
-}

+ 3 - 4
shared-lib/flowy-sync/src/client_folder/mod.rs

@@ -1,12 +1,11 @@
-mod app_node;
 mod builder;
 mod builder;
 mod folder_node;
 mod folder_node;
-mod folder_node_2;
 mod folder_pad;
 mod folder_pad;
+mod trash_node;
 mod util;
 mod util;
-mod view_node;
 mod workspace_node;
 mod workspace_node;
-mod workspace_node_2;
 
 
+pub use folder_node::*;
 pub use folder_node::*;
 pub use folder_node::*;
 pub use folder_pad::*;
 pub use folder_pad::*;
+pub use workspace_node::*;

+ 20 - 0
shared-lib/flowy-sync/src/client_folder/trash_node.rs

@@ -0,0 +1,20 @@
+use crate::client_folder::util::*;
+use crate::client_folder::AtomicNodeTree;
+use flowy_derive::Node;
+use lib_ot::core::*;
+use std::sync::Arc;
+
+#[derive(Clone, Node)]
+#[node_type = "trash"]
+pub struct TrashNode {
+    pub tree: Arc<AtomicNodeTree>,
+    pub node_id: Option<NodeId>,
+
+    #[node(get_value_with = "get_attributes_str_value")]
+    #[node(set_value_with = "set_attributes_str_value")]
+    pub id: String,
+
+    #[node(get_value_with = "get_attributes_str_value")]
+    #[node(set_value_with = "set_attributes_str_value")]
+    pub name: String,
+}

+ 7 - 7
shared-lib/flowy-sync/src/client_folder/util.rs

@@ -5,7 +5,7 @@ use std::sync::Arc;
 
 
 pub fn get_attributes_str_value(tree: Arc<AtomicNodeTree>, node_id: &NodeId, key: &str) -> Option<String> {
 pub fn get_attributes_str_value(tree: Arc<AtomicNodeTree>, node_id: &NodeId, key: &str) -> Option<String> {
     tree.read()
     tree.read()
-        .get_node(node_id.clone())
+        .get_node(*node_id)
         .and_then(|node| node.attributes.get(key).cloned())
         .and_then(|node| node.attributes.get(key).cloned())
         .and_then(|value| value.str_value())
         .and_then(|value| value.str_value())
 }
 }
@@ -22,7 +22,7 @@ pub fn set_attributes_str_value(
     };
     };
     let mut new_attributes = old_attributes.clone();
     let mut new_attributes = old_attributes.clone();
     new_attributes.insert(key, value);
     new_attributes.insert(key, value);
-    let path = tree.read().path_from_node_id(node_id.clone());
+    let path = tree.read().path_from_node_id(*node_id);
     let update_operation = NodeOperation::Update {
     let update_operation = NodeOperation::Update {
         path,
         path,
         changeset: Changeset::Attributes {
         changeset: Changeset::Attributes {
@@ -34,21 +34,21 @@ pub fn set_attributes_str_value(
     Ok(())
     Ok(())
 }
 }
 
 
+#[allow(dead_code)]
 pub fn get_attributes_int_value(tree: Arc<AtomicNodeTree>, node_id: &NodeId, key: &str) -> Option<i64> {
 pub fn get_attributes_int_value(tree: Arc<AtomicNodeTree>, node_id: &NodeId, key: &str) -> Option<i64> {
     tree.read()
     tree.read()
-        .get_node(node_id.clone())
+        .get_node(*node_id)
         .and_then(|node| node.attributes.get(key).cloned())
         .and_then(|node| node.attributes.get(key).cloned())
         .and_then(|value| value.int_value())
         .and_then(|value| value.int_value())
 }
 }
 
 
 pub fn get_attributes(tree: Arc<AtomicNodeTree>, node_id: &NodeId) -> Option<AttributeHashMap> {
 pub fn get_attributes(tree: Arc<AtomicNodeTree>, node_id: &NodeId) -> Option<AttributeHashMap> {
-    tree.read()
-        .get_node(node_id.clone())
-        .and_then(|node| Some(node.attributes.clone()))
+    tree.read().get_node(*node_id).map(|node| node.attributes.clone())
 }
 }
 
 
+#[allow(dead_code)]
 pub fn get_attributes_value(tree: Arc<AtomicNodeTree>, node_id: &NodeId, key: &str) -> Option<AttributeValue> {
 pub fn get_attributes_value(tree: Arc<AtomicNodeTree>, node_id: &NodeId, key: &str) -> Option<AttributeValue> {
     tree.read()
     tree.read()
-        .get_node(node_id.clone())
+        .get_node(*node_id)
         .and_then(|node| node.attributes.get(key).cloned())
         .and_then(|node| node.attributes.get(key).cloned())
 }
 }

+ 0 - 44
shared-lib/flowy-sync/src/client_folder/view_node.rs

@@ -1,44 +0,0 @@
-use crate::client_folder::AtomicNodeTree;
-use crate::errors::CollaborateResult;
-use folder_rev_model::ViewRevision;
-use lib_ot::core::{NodeDataBuilder, NodeOperation, Path, Transaction};
-use std::sync::Arc;
-
-#[derive(Debug, Clone)]
-pub struct ViewNode {
-    tree: Arc<AtomicNodeTree>,
-    path: Path,
-}
-
-impl ViewNode {
-    pub(crate) fn from_view_revision(
-        transaction: &mut Transaction,
-        revision: ViewRevision,
-        tree: Arc<AtomicNodeTree>,
-        path: Path,
-    ) -> CollaborateResult<Self> {
-        let view_node = NodeDataBuilder::new("view")
-            .insert_attribute("id", revision.id)
-            .insert_attribute("name", revision.name)
-            .build();
-
-        transaction.push_operation(NodeOperation::Insert {
-            path: path.clone(),
-            nodes: vec![view_node],
-        });
-
-        Ok(Self { tree, path })
-    }
-
-    fn get_id(&self) -> &str {
-        todo!()
-    }
-
-    fn get_app_id(&self) -> &str {
-        todo!()
-    }
-
-    fn set_app_id(&self, workspace_id: String) {
-        todo!()
-    }
-}

+ 46 - 88
shared-lib/flowy-sync/src/client_folder/workspace_node.rs

@@ -1,104 +1,62 @@
-use crate::client_folder::app_node::AppNode;
-use crate::client_folder::view_node::ViewNode;
-use crate::client_folder::{get_attributes_str_value, get_attributes_value, set_attributes_str_value, AtomicNodeTree};
-use crate::errors::CollaborateResult;
-use folder_rev_model::{AppRevision, WorkspaceRevision};
-use lib_ot::core::{AttributeValue, NodeDataBuilder, NodeOperation, Path, Transaction};
+use crate::client_folder::util::*;
+use crate::client_folder::AtomicNodeTree;
+
+use flowy_derive::Node;
+use lib_ot::core::*;
 use std::sync::Arc;
 use std::sync::Arc;
 
 
-#[derive(Debug, Clone)]
+#[derive(Clone, Node)]
+#[node_type = "workspace"]
 pub struct WorkspaceNode {
 pub struct WorkspaceNode {
-    pub(crate) id: String,
-    tree: Arc<AtomicNodeTree>,
-    pub(crate) path: Path,
-    apps: Vec<Arc<AppNode>>,
-}
+    pub tree: Arc<AtomicNodeTree>,
+    pub node_id: Option<NodeId>,
 
 
-impl WorkspaceNode {
-    pub(crate) fn from_workspace_revision(
-        transaction: &mut Transaction,
-        revision: WorkspaceRevision,
-        tree: Arc<AtomicNodeTree>,
-        path: Path,
-    ) -> CollaborateResult<Self> {
-        let workspace_id = revision.id.clone();
-        let workspace_node = NodeDataBuilder::new("workspace")
-            .insert_attribute("id", revision.id)
-            .insert_attribute("name", revision.name)
-            .build();
+    #[node(get_value_with = "get_attributes_str_value")]
+    #[node(set_value_with = "set_attributes_str_value")]
+    pub id: String,
 
 
-        transaction.push_operation(NodeOperation::Insert {
-            path: path.clone(),
-            nodes: vec![workspace_node],
-        });
+    #[node(get_value_with = "get_attributes_str_value")]
+    #[node(set_value_with = "set_attributes_str_value")]
+    pub name: String,
 
 
-        let apps = revision
-            .apps
-            .into_iter()
-            .enumerate()
-            .map(|(index, app)| (path.clone_with(index), app))
-            .flat_map(
-                |(path, app)| match AppNode::from_app_revision(transaction, app, tree.clone(), path) {
-                    Ok(app_node) => Some(Arc::new(app_node)),
-                    Err(err) => {
-                        tracing::warn!("Create app node failed: {:?}", err);
-                        None
-                    }
-                },
-            )
-            .collect::<Vec<Arc<AppNode>>>();
+    #[node(child_name = "app")]
+    pub apps: Vec<AppNode>,
+}
 
 
-        Ok(Self {
-            id: workspace_id,
+impl WorkspaceNode {
+    pub fn new(tree: Arc<AtomicNodeTree>, id: String, name: String) -> Self {
+        Self {
             tree,
             tree,
-            path,
-            apps,
-        })
-    }
-
-    pub fn get_name(&self) -> Option<String> {
-        get_attributes_str_value(self.tree.clone(), &self.path, "name")
-    }
-
-    pub fn set_name(&self, name: &str) -> CollaborateResult<()> {
-        set_attributes_str_value(self.tree.clone(), &self.path, "name", name.to_string())
+            node_id: None,
+            id,
+            name,
+            apps: vec![],
+        }
     }
     }
+}
 
 
-    pub fn get_app(&self, app_id: &str) -> Option<&Arc<AppNode>> {
-        self.apps.iter().find(|app| app.id == app_id)
-    }
+#[derive(Clone, Node)]
+#[node_type = "app"]
+pub struct AppNode {
+    pub tree: Arc<AtomicNodeTree>,
+    pub node_id: Option<NodeId>,
 
 
-    pub fn get_mut_app(&mut self, app_id: &str) -> Option<&mut Arc<AppNode>> {
-        self.apps.iter_mut().find(|app| app.id == app_id)
-    }
+    #[node(get_value_with = "get_attributes_str_value")]
+    #[node(set_value_with = "set_attributes_str_value")]
+    pub id: String,
 
 
-    pub fn add_app(&mut self, app: AppRevision) -> CollaborateResult<()> {
-        let mut transaction = Transaction::new();
-        let path = self.path.clone_with(self.apps.len());
-        let app_node = AppNode::from_app_revision(&mut transaction, app, self.tree.clone(), path.clone())?;
-        let _ = self.tree.write().apply_transaction(transaction);
-        self.apps.push(Arc::new(app_node));
-        Ok(())
-    }
+    #[node(get_value_with = "get_attributes_str_value")]
+    #[node(set_value_with = "set_attributes_str_value")]
+    pub name: String,
+}
 
 
-    pub fn remove_app(&mut self, app_id: &str) {
-        if let Some(index) = self.apps.iter().position(|app| app.id == app_id) {
-            let app = self.apps.remove(index);
-            let mut nodes = vec![];
-            let app_node = self.tree.read().get_node_data_at_path(&app.path);
-            debug_assert!(app_node.is_some());
-            if let Some(node_data) = app_node {
-                nodes.push(node_data);
-            }
-            let delete_operation = NodeOperation::Delete {
-                path: app.path.clone(),
-                nodes,
-            };
-            let _ = self.tree.write().apply_op(delete_operation);
+impl AppNode {
+    pub fn new(tree: Arc<AtomicNodeTree>, id: String, name: String) -> Self {
+        Self {
+            tree,
+            node_id: None,
+            id,
+            name,
         }
         }
     }
     }
-
-    pub fn get_all_apps(&self) -> Vec<Arc<AppNode>> {
-        self.apps.clone()
-    }
 }
 }

+ 0 - 43
shared-lib/flowy-sync/src/client_folder/workspace_node_2.rs

@@ -1,43 +0,0 @@
-use crate::client_folder::util::*;
-use crate::client_folder::AtomicNodeTree;
-use crate::errors::CollaborateResult;
-use flowy_derive::Node;
-use lib_ot::core::*;
-use std::sync::Arc;
-
-#[derive(Clone, Node)]
-#[node_type = "workspace"]
-pub struct WorkspaceNode2 {
-    tree: Arc<AtomicNodeTree>,
-    node_id: NodeId,
-
-    #[node(get_value_with = "get_attributes_str_value")]
-    #[node(set_value_with = "set_attributes_str_value")]
-    pub id: String,
-
-    #[node(get_value_with = "get_attributes_str_value")]
-    #[node(set_value_with = "set_attributes_str_value")]
-    #[node(rename = "name123")]
-    pub name: String,
-
-    #[node(get_value_with = "get_attributes_int_value")]
-    pub time: i64,
-
-    #[node(child_name = "app")]
-    pub apps: Vec<AppNode2>,
-}
-
-#[derive(Clone, Node)]
-#[node_type = "app"]
-pub struct AppNode2 {
-    tree: Arc<AtomicNodeTree>,
-    node_id: NodeId,
-
-    #[node(get_value_with = "get_attributes_str_value")]
-    #[node(set_value_with = "set_attributes_str_value")]
-    pub id: String,
-
-    #[node(get_value_with = "get_attributes_str_value")]
-    #[node(set_value_with = "set_attributes_str_value")]
-    pub name: String,
-}

+ 18 - 39
shared-lib/flowy-sync/tests/client_folder/folder_test.rs

@@ -1,9 +1,8 @@
-use flowy_sync::client_folder::FolderNodePad;
-use folder_rev_model::WorkspaceRevision;
+use flowy_sync::client_folder::{FolderNodePad, WorkspaceNode};
 
 
 #[test]
 #[test]
 fn client_folder_create_default_folder_test() {
 fn client_folder_create_default_folder_test() {
-    let folder_pad = FolderNodePad::default();
+    let folder_pad = FolderNodePad::new();
     let json = folder_pad.to_json(false).unwrap();
     let json = folder_pad.to_json(false).unwrap();
     assert_eq!(
     assert_eq!(
         json,
         json,
@@ -13,16 +12,9 @@ fn client_folder_create_default_folder_test() {
 
 
 #[test]
 #[test]
 fn client_folder_create_default_folder_with_workspace_test() {
 fn client_folder_create_default_folder_with_workspace_test() {
-    let mut folder_pad = FolderNodePad::default();
-    let workspace = WorkspaceRevision {
-        id: "1".to_string(),
-        name: "workspace name".to_string(),
-        desc: "".to_string(),
-        apps: vec![],
-        modified_time: 0,
-        create_time: 0,
-    };
-    folder_pad.add_workspace(workspace).unwrap();
+    let mut folder_pad = FolderNodePad::new();
+    let workspace = WorkspaceNode::new(folder_pad.tree.clone(), "1".to_string(), "workspace name".to_string());
+    folder_pad.workspaces.add_workspace(workspace).unwrap();
     let json = folder_pad.to_json(false).unwrap();
     let json = folder_pad.to_json(false).unwrap();
     assert_eq!(
     assert_eq!(
         json,
         json,
@@ -37,17 +29,10 @@ fn client_folder_create_default_folder_with_workspace_test() {
 
 
 #[test]
 #[test]
 fn client_folder_delete_workspace_test() {
 fn client_folder_delete_workspace_test() {
-    let mut folder_pad = FolderNodePad::default();
-    let workspace = WorkspaceRevision {
-        id: "1".to_string(),
-        name: "workspace name".to_string(),
-        desc: "".to_string(),
-        apps: vec![],
-        modified_time: 0,
-        create_time: 0,
-    };
-    folder_pad.add_workspace(workspace).unwrap();
-    folder_pad.remove_workspace("1");
+    let mut folder_pad = FolderNodePad::new();
+    let workspace = WorkspaceNode::new(folder_pad.tree.clone(), "1".to_string(), "workspace name".to_string());
+    folder_pad.workspaces.add_workspace(workspace).unwrap();
+    folder_pad.workspaces.remove_workspace("1");
     let json = folder_pad.to_json(false).unwrap();
     let json = folder_pad.to_json(false).unwrap();
     assert_eq!(
     assert_eq!(
         json,
         json,
@@ -57,23 +42,17 @@ fn client_folder_delete_workspace_test() {
 
 
 #[test]
 #[test]
 fn client_folder_update_workspace_name_test() {
 fn client_folder_update_workspace_name_test() {
-    let mut folder_pad = FolderNodePad::default();
-    let workspace = WorkspaceRevision {
-        id: "1".to_string(),
-        name: "workspace name".to_string(),
-        desc: "".to_string(),
-        apps: vec![],
-        modified_time: 0,
-        create_time: 0,
-    };
-    folder_pad.add_workspace(workspace).unwrap();
+    let mut folder_pad = FolderNodePad::new();
+    let workspace = WorkspaceNode::new(folder_pad.tree.clone(), "1".to_string(), "workspace name".to_string());
+    folder_pad.workspaces.add_workspace(workspace).unwrap();
     folder_pad
     folder_pad
-        .get_workspace("1")
+        .workspaces
+        .get_mut_workspace("1")
         .unwrap()
         .unwrap()
-        .set_name("My first workspace")
-        .unwrap();
+        .set_name("my first workspace".to_string());
+
     assert_eq!(
     assert_eq!(
-        folder_pad.get_workspace("1").unwrap().get_name().unwrap(),
-        "My first workspace"
+        folder_pad.workspaces.get_workspace("1").unwrap().get_name().unwrap(),
+        "my first workspace"
     );
     );
 }
 }

+ 34 - 36
shared-lib/flowy-sync/tests/client_folder/script.rs

@@ -1,14 +1,18 @@
-use flowy_sync::client_folder::FolderNodePad;
-use folder_rev_model::{AppRevision, WorkspaceRevision};
-use std::sync::Arc;
+use flowy_sync::client_folder::{AppNode, FolderNodePad, WorkspaceNode};
+use folder_rev_model::AppRevision;
+use lib_ot::core::Path;
 
 
 pub enum FolderNodePadScript {
 pub enum FolderNodePadScript {
+    CreateWorkspace { id: String, name: String },
+    DeleteWorkspace { id: String },
+    AssertPathOfWorkspace { id: String, expected_path: Path },
+    AssertNumberOfWorkspace { expected: usize },
     CreateApp { id: String, name: String },
     CreateApp { id: String, name: String },
     DeleteApp { id: String },
     DeleteApp { id: String },
     UpdateApp { id: String, name: String },
     UpdateApp { id: String, name: String },
     AssertApp { id: String, expected: Option<AppRevision> },
     AssertApp { id: String, expected: Option<AppRevision> },
     AssertAppContent { id: String, name: String },
     AssertAppContent { id: String, name: String },
-    AssertNumberOfApps { expected: usize },
+    // AssertNumberOfApps { expected: usize },
 }
 }
 
 
 pub struct FolderNodePadTest {
 pub struct FolderNodePadTest {
@@ -18,15 +22,8 @@ pub struct FolderNodePadTest {
 impl FolderNodePadTest {
 impl FolderNodePadTest {
     pub fn new() -> FolderNodePadTest {
     pub fn new() -> FolderNodePadTest {
         let mut folder_pad = FolderNodePad::default();
         let mut folder_pad = FolderNodePad::default();
-        let workspace = WorkspaceRevision {
-            id: "1".to_string(),
-            name: "workspace name".to_string(),
-            desc: "".to_string(),
-            apps: vec![],
-            modified_time: 0,
-            create_time: 0,
-        };
-        let _ = folder_pad.add_workspace(workspace).unwrap();
+        let workspace = WorkspaceNode::new(folder_pad.tree.clone(), "1".to_string(), "workspace name".to_string());
+        let _ = folder_pad.workspaces.add_workspace(workspace).unwrap();
         Self { folder_pad }
         Self { folder_pad }
     }
     }
 
 
@@ -38,32 +35,34 @@ impl FolderNodePadTest {
 
 
     pub fn run_script(&mut self, script: FolderNodePadScript) {
     pub fn run_script(&mut self, script: FolderNodePadScript) {
         match script {
         match script {
+            FolderNodePadScript::CreateWorkspace { id, name } => {
+                let workspace = WorkspaceNode::new(self.folder_pad.tree.clone(), id, name);
+                self.folder_pad.workspaces.add_workspace(workspace).unwrap();
+            }
+            FolderNodePadScript::DeleteWorkspace { id } => {
+                self.folder_pad.workspaces.remove_workspace(id);
+            }
+            FolderNodePadScript::AssertPathOfWorkspace { id, expected_path } => {
+                let workspace_node: &WorkspaceNode = self.folder_pad.workspaces.get_workspace(id).unwrap();
+                let node_id = workspace_node.node_id.unwrap();
+                let path = self.folder_pad.tree.read().path_from_node_id(node_id);
+                assert_eq!(path, expected_path);
+            }
+            FolderNodePadScript::AssertNumberOfWorkspace { expected } => {
+                assert_eq!(self.folder_pad.workspaces.len(), expected);
+            }
             FolderNodePadScript::CreateApp { id, name } => {
             FolderNodePadScript::CreateApp { id, name } => {
-                let revision = AppRevision {
-                    id,
-                    workspace_id: "1".to_string(),
-                    name,
-                    desc: "".to_string(),
-                    belongings: vec![],
-                    version: 0,
-                    modified_time: 0,
-                    create_time: 0,
-                };
-
+                let app_node = AppNode::new(self.folder_pad.tree.clone(), id, name);
                 let workspace_node = self.folder_pad.get_mut_workspace("1").unwrap();
                 let workspace_node = self.folder_pad.get_mut_workspace("1").unwrap();
-                let workspace_node = Arc::make_mut(workspace_node);
-                let _ = workspace_node.add_app(revision).unwrap();
+                let _ = workspace_node.add_app(app_node).unwrap();
             }
             }
             FolderNodePadScript::DeleteApp { id } => {
             FolderNodePadScript::DeleteApp { id } => {
                 let workspace_node = self.folder_pad.get_mut_workspace("1").unwrap();
                 let workspace_node = self.folder_pad.get_mut_workspace("1").unwrap();
-                let workspace_node = Arc::make_mut(workspace_node);
                 workspace_node.remove_app(&id);
                 workspace_node.remove_app(&id);
             }
             }
             FolderNodePadScript::UpdateApp { id, name } => {
             FolderNodePadScript::UpdateApp { id, name } => {
                 let workspace_node = self.folder_pad.get_mut_workspace("1").unwrap();
                 let workspace_node = self.folder_pad.get_mut_workspace("1").unwrap();
-                let workspace_node = Arc::make_mut(workspace_node);
-                let app_node = Arc::make_mut(workspace_node.get_mut_app(&id).unwrap());
-                app_node.set_name(name).unwrap();
+                workspace_node.get_mut_app(&id).unwrap().set_name(name);
             }
             }
             FolderNodePadScript::AssertApp { id, expected } => {
             FolderNodePadScript::AssertApp { id, expected } => {
                 let workspace_node = self.folder_pad.get_workspace("1").unwrap();
                 let workspace_node = self.folder_pad.get_workspace("1").unwrap();
@@ -73,7 +72,7 @@ impl FolderNodePadTest {
                     Some(expected_app) => {
                     Some(expected_app) => {
                         let app_node = app.unwrap();
                         let app_node = app.unwrap();
                         assert_eq!(expected_app.name, app_node.get_name().unwrap());
                         assert_eq!(expected_app.name, app_node.get_name().unwrap());
-                        assert_eq!(expected_app.id, app_node.id);
+                        assert_eq!(expected_app.id, app_node.get_id().unwrap());
                     }
                     }
                 }
                 }
             }
             }
@@ -81,11 +80,10 @@ impl FolderNodePadTest {
                 let workspace_node = self.folder_pad.get_workspace("1").unwrap();
                 let workspace_node = self.folder_pad.get_workspace("1").unwrap();
                 let app = workspace_node.get_app(&id).unwrap();
                 let app = workspace_node.get_app(&id).unwrap();
                 assert_eq!(app.get_name().unwrap(), name)
                 assert_eq!(app.get_name().unwrap(), name)
-            }
-            FolderNodePadScript::AssertNumberOfApps { expected } => {
-                let workspace_node = self.folder_pad.get_workspace("1").unwrap();
-                assert_eq!(workspace_node.get_all_apps().len(), expected);
-            }
+            } // FolderNodePadScript::AssertNumberOfApps { expected } => {
+              //     let workspace_node = self.folder_pad.get_workspace("1").unwrap();
+              //     assert_eq!(workspace_node.apps.len(), expected);
+              // }
         }
         }
     }
     }
 }
 }

+ 34 - 1
shared-lib/flowy-sync/tests/client_folder/workspace_test.rs

@@ -1,6 +1,39 @@
 use crate::client_folder::script::FolderNodePadScript::*;
 use crate::client_folder::script::FolderNodePadScript::*;
 use crate::client_folder::script::FolderNodePadTest;
 use crate::client_folder::script::FolderNodePadTest;
-use flowy_sync::client_folder::FolderNodePad;
+
+#[test]
+fn client_folder_create_multi_workspaces_test() {
+    let mut test = FolderNodePadTest::new();
+    test.run_scripts(vec![
+        AssertPathOfWorkspace {
+            id: "1".to_string(),
+            expected_path: vec![0, 0, 0].into(),
+        },
+        CreateWorkspace {
+            id: "a".to_string(),
+            name: "workspace a".to_string(),
+        },
+        AssertPathOfWorkspace {
+            id: "a".to_string(),
+            expected_path: vec![0, 0, 1].into(),
+        },
+        CreateWorkspace {
+            id: "b".to_string(),
+            name: "workspace b".to_string(),
+        },
+        AssertPathOfWorkspace {
+            id: "b".to_string(),
+            expected_path: vec![0, 0, 2].into(),
+        },
+        AssertNumberOfWorkspace { expected: 3 },
+        // The path of the workspace 'b' will be changed after deleting the 'a' workspace.
+        DeleteWorkspace { id: "a".to_string() },
+        AssertPathOfWorkspace {
+            id: "b".to_string(),
+            expected_path: vec![0, 0, 1].into(),
+        },
+    ]);
+}
 
 
 #[test]
 #[test]
 fn client_folder_create_app_test() {
 fn client_folder_create_app_test() {

+ 3 - 3
shared-lib/grid-rev-model/src/grid_setting_rev.rs

@@ -61,8 +61,8 @@ where
         predicate: impl Fn(&Arc<T>) -> bool,
         predicate: impl Fn(&Arc<T>) -> bool,
     ) -> Option<Arc<T>> {
     ) -> Option<Arc<T>> {
         let objects = self.get_objects(field_id, field_type)?;
         let objects = self.get_objects(field_id, field_type)?;
-        let index = objects.iter().position(|object| predicate(object))?;
-        objects.get(index).map(|object| object.clone())
+        let index = objects.iter().position(predicate)?;
+        objects.get(index).cloned()
     }
     }
 
 
     pub fn get_mut_object(
     pub fn get_mut_object(
@@ -72,7 +72,7 @@ where
         predicate: impl Fn(&Arc<T>) -> bool,
         predicate: impl Fn(&Arc<T>) -> bool,
     ) -> Option<&mut Arc<T>> {
     ) -> Option<&mut Arc<T>> {
         let objects = self.get_mut_objects(field_id, field_type)?;
         let objects = self.get_mut_objects(field_id, field_type)?;
-        let index = objects.iter().position(|object| predicate(object))?;
+        let index = objects.iter().position(predicate)?;
         objects.get_mut(index)
         objects.get_mut(index)
     }
     }
 
 

+ 1 - 1
shared-lib/lib-ot/src/core/node_tree/tree.rs

@@ -196,7 +196,7 @@ impl NodeTree {
             path.push(counter);
             path.push(counter);
             current_node = parent_node;
             current_node = parent_node;
         }
         }
-
+        path.reverse();
         Path(path)
         Path(path)
     }
     }