mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: macro node
This commit is contained in:
parent
aca9b81c79
commit
9488f42749
@ -4,7 +4,7 @@
|
||||
|
||||
use crate::event_attrs::EventEnumAttrs;
|
||||
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 syn::Meta::NameValue;
|
||||
use syn::{self, punctuated::Punctuated};
|
||||
|
@ -1,10 +1,8 @@
|
||||
use crate::{get_event_meta_items, parse_lit_str, symbol::*, ASTResult};
|
||||
|
||||
|
||||
use syn::{
|
||||
self,
|
||||
parse::{self, Parse},
|
||||
Meta::{List, NameValue, Path},
|
||||
Meta::{NameValue, Path},
|
||||
NestedMeta::{Lit, Meta},
|
||||
};
|
||||
|
||||
|
@ -1,9 +1,6 @@
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
mod ast;
|
||||
mod ctxt;
|
||||
mod pb_attrs;
|
||||
|
@ -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 syn::{
|
||||
self,
|
||||
parse::{self, Parse},
|
||||
LitStr,
|
||||
Meta::{List, NameValue, Path},
|
||||
self, LitStr,
|
||||
Meta::NameValue,
|
||||
NestedMeta::{Lit, Meta},
|
||||
};
|
||||
|
||||
@ -14,18 +11,20 @@ pub struct NodeStructAttrs {
|
||||
pub has_child: bool,
|
||||
pub child_name: Option<LitStr>,
|
||||
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 {
|
||||
/// 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 child_name = ASTAttr::none(ast_result, CHILD_NODE_NAME);
|
||||
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 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
|
||||
.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) => {
|
||||
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));
|
||||
@ -87,14 +93,7 @@ impl NodeStructAttrs {
|
||||
child_name,
|
||||
get_node_value_with: get_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()
|
||||
}
|
||||
}
|
||||
|
@ -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 GET_VEC_ELEMENT_WITH: Symbol = Symbol("get_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 {
|
||||
fn eq(&self, word: &Symbol) -> bool {
|
||||
|
@ -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),
|
||||
"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 {
|
||||
|
@ -1,4 +1,5 @@
|
||||
use flowy_ast::EventEnumAttrs;
|
||||
use quote::format_ident;
|
||||
|
||||
pub struct EventASTContext {
|
||||
pub event: syn::Ident,
|
||||
|
@ -15,6 +15,3 @@ pub struct ProtoCache {
|
||||
pub structs: Vec<String>,
|
||||
pub enums: Vec<String>,
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
@ -26,8 +26,9 @@ pub fn make_helper_funcs_token_stream(ast: &ASTContainer) -> TokenStream {
|
||||
let struct_ident = &ast.ident;
|
||||
token_streams.extend(quote! {
|
||||
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 field_name = children_field.name().unwrap();
|
||||
eprintln!("😄 {:?} {:?}", struct_ident, field_name);
|
||||
let child_name = children_field.node_attrs.child_name.as_ref().unwrap();
|
||||
let get_func_name = format_ident!("get_{}", 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! {
|
||||
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_path = element.get_path();
|
||||
let element_path = element.get_path().unwrap();
|
||||
|
||||
let mut write_guard = self.tree.write();
|
||||
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);
|
||||
}
|
||||
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 parent_path = self.get_path();
|
||||
let parent_path = self.get_path().unwrap();
|
||||
|
||||
let path = parent_path.clone_with(self.#field_name.len());
|
||||
let node_data = value.to_node_data();
|
||||
transaction.push_operation(NodeOperation::Insert {
|
||||
path,
|
||||
path: path.clone(),
|
||||
nodes: vec![node_data],
|
||||
});
|
||||
|
||||
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);
|
||||
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 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! {
|
||||
impl #struct_ident {
|
||||
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! {
|
||||
impl #struct_ident {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -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!()
|
||||
}
|
||||
}
|
@ -1,21 +1,53 @@
|
||||
use crate::client_folder::trash_node::TrashNode;
|
||||
use crate::client_folder::workspace_node::WorkspaceNode;
|
||||
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 std::string::ToString;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub type AtomicNodeTree = RwLock<NodeTree>;
|
||||
|
||||
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 {
|
||||
@ -23,42 +55,27 @@ impl FolderNodePad {
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
.iter_mut()
|
||||
.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![];
|
||||
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();
|
||||
|
||||
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, 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> {
|
||||
@ -69,6 +86,49 @@ impl FolderNodePad {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for FolderNodePad {
|
||||
fn default() -> Self {
|
||||
let tree = Arc::new(RwLock::new(NodeTree::default()));
|
||||
|
||||
// Workspace
|
||||
let mut workspaces = WorkspaceList {
|
||||
tree: tree.clone(),
|
||||
node_id: None,
|
||||
inner: vec![],
|
||||
};
|
||||
let workspace_node = workspaces.to_node_data();
|
||||
|
||||
// Trash
|
||||
let mut trash = TrashList {
|
||||
tree: tree.clone(),
|
||||
node_id: None,
|
||||
inner: vec![],
|
||||
};
|
||||
let trash_node = trash.to_node_data();
|
||||
|
||||
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 _ = 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 {
|
||||
tree,
|
||||
node_id,
|
||||
workspaces,
|
||||
trash,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn folder_path() -> Path {
|
||||
vec![0].into()
|
||||
}
|
||||
@ -80,75 +140,3 @@ fn workspaces_path() -> Path {
|
||||
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())
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
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")
|
||||
.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![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TrashNode {
|
||||
tree: Arc<AtomicNodeTree>,
|
||||
parent_path: Path,
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
@ -1,12 +1,11 @@
|
||||
mod app_node;
|
||||
mod builder;
|
||||
mod folder_node;
|
||||
mod folder_node_2;
|
||||
mod folder_pad;
|
||||
mod trash_node;
|
||||
mod util;
|
||||
mod view_node;
|
||||
mod workspace_node;
|
||||
mod workspace_node_2;
|
||||
|
||||
pub use folder_node::*;
|
||||
pub use folder_node::*;
|
||||
pub use folder_pad::*;
|
||||
pub use workspace_node::*;
|
||||
|
20
shared-lib/flowy-sync/src/client_folder/trash_node.rs
Normal file
20
shared-lib/flowy-sync/src/client_folder/trash_node.rs
Normal file
@ -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,
|
||||
}
|
@ -5,7 +5,7 @@ use std::sync::Arc;
|
||||
|
||||
pub fn get_attributes_str_value(tree: Arc<AtomicNodeTree>, node_id: &NodeId, key: &str) -> Option<String> {
|
||||
tree.read()
|
||||
.get_node(node_id.clone())
|
||||
.get_node(*node_id)
|
||||
.and_then(|node| node.attributes.get(key).cloned())
|
||||
.and_then(|value| value.str_value())
|
||||
}
|
||||
@ -22,7 +22,7 @@ pub fn set_attributes_str_value(
|
||||
};
|
||||
let mut new_attributes = old_attributes.clone();
|
||||
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 {
|
||||
path,
|
||||
changeset: Changeset::Attributes {
|
||||
@ -34,21 +34,21 @@ pub fn set_attributes_str_value(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_attributes_int_value(tree: Arc<AtomicNodeTree>, node_id: &NodeId, key: &str) -> Option<i64> {
|
||||
tree.read()
|
||||
.get_node(node_id.clone())
|
||||
.get_node(*node_id)
|
||||
.and_then(|node| node.attributes.get(key).cloned())
|
||||
.and_then(|value| value.int_value())
|
||||
}
|
||||
|
||||
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> {
|
||||
tree.read()
|
||||
.get_node(node_id.clone())
|
||||
.get_node(*node_id)
|
||||
.and_then(|node| node.attributes.get(key).cloned())
|
||||
}
|
||||
|
@ -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!()
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone, Node)]
|
||||
#[node_type = "workspace"]
|
||||
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>,
|
||||
|
||||
#[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,
|
||||
|
||||
#[node(child_name = "app")]
|
||||
pub apps: Vec<AppNode>,
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
transaction.push_operation(NodeOperation::Insert {
|
||||
path: path.clone(),
|
||||
nodes: vec![workspace_node],
|
||||
});
|
||||
|
||||
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>>>();
|
||||
|
||||
Ok(Self {
|
||||
id: workspace_id,
|
||||
pub fn new(tree: Arc<AtomicNodeTree>, id: String, name: String) -> Self {
|
||||
Self {
|
||||
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())
|
||||
}
|
||||
|
||||
pub fn get_app(&self, app_id: &str) -> Option<&Arc<AppNode>> {
|
||||
self.apps.iter().find(|app| app.id == app_id)
|
||||
}
|
||||
|
||||
pub fn get_mut_app(&mut self, app_id: &str) -> Option<&mut Arc<AppNode>> {
|
||||
self.apps.iter_mut().find(|app| app.id == app_id)
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
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);
|
||||
node_id: None,
|
||||
id,
|
||||
name,
|
||||
apps: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_all_apps(&self) -> Vec<Arc<AppNode>> {
|
||||
self.apps.clone()
|
||||
#[derive(Clone, Node)]
|
||||
#[node_type = "app"]
|
||||
pub struct AppNode {
|
||||
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,
|
||||
}
|
||||
|
||||
impl AppNode {
|
||||
pub fn new(tree: Arc<AtomicNodeTree>, id: String, name: String) -> Self {
|
||||
Self {
|
||||
tree,
|
||||
node_id: None,
|
||||
id,
|
||||
name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
use flowy_sync::client_folder::FolderNodePad;
|
||||
use folder_rev_model::WorkspaceRevision;
|
||||
use flowy_sync::client_folder::{FolderNodePad, WorkspaceNode};
|
||||
|
||||
#[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();
|
||||
assert_eq!(
|
||||
json,
|
||||
@ -13,16 +12,9 @@ fn client_folder_create_default_folder_test() {
|
||||
|
||||
#[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();
|
||||
assert_eq!(
|
||||
json,
|
||||
@ -37,17 +29,10 @@ fn client_folder_create_default_folder_with_workspace_test() {
|
||||
|
||||
#[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();
|
||||
assert_eq!(
|
||||
json,
|
||||
@ -57,23 +42,17 @@ fn client_folder_delete_workspace_test() {
|
||||
|
||||
#[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
|
||||
.get_workspace("1")
|
||||
.workspaces
|
||||
.get_mut_workspace("1")
|
||||
.unwrap()
|
||||
.set_name("My first workspace")
|
||||
.unwrap();
|
||||
.set_name("my first workspace".to_string());
|
||||
|
||||
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"
|
||||
);
|
||||
}
|
||||
|
@ -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 {
|
||||
CreateWorkspace { id: String, name: String },
|
||||
DeleteWorkspace { id: String },
|
||||
AssertPathOfWorkspace { id: String, expected_path: Path },
|
||||
AssertNumberOfWorkspace { expected: usize },
|
||||
CreateApp { id: String, name: String },
|
||||
DeleteApp { id: String },
|
||||
UpdateApp { id: String, name: String },
|
||||
AssertApp { id: String, expected: Option<AppRevision> },
|
||||
AssertAppContent { id: String, name: String },
|
||||
AssertNumberOfApps { expected: usize },
|
||||
// AssertNumberOfApps { expected: usize },
|
||||
}
|
||||
|
||||
pub struct FolderNodePadTest {
|
||||
@ -18,15 +22,8 @@ pub struct FolderNodePadTest {
|
||||
impl FolderNodePadTest {
|
||||
pub fn new() -> FolderNodePadTest {
|
||||
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 }
|
||||
}
|
||||
|
||||
@ -38,32 +35,34 @@ impl FolderNodePadTest {
|
||||
|
||||
pub fn run_script(&mut self, script: FolderNodePadScript) {
|
||||
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 } => {
|
||||
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 = Arc::make_mut(workspace_node);
|
||||
let _ = workspace_node.add_app(revision).unwrap();
|
||||
let _ = workspace_node.add_app(app_node).unwrap();
|
||||
}
|
||||
FolderNodePadScript::DeleteApp { id } => {
|
||||
let workspace_node = self.folder_pad.get_mut_workspace("1").unwrap();
|
||||
let workspace_node = Arc::make_mut(workspace_node);
|
||||
workspace_node.remove_app(&id);
|
||||
}
|
||||
FolderNodePadScript::UpdateApp { id, name } => {
|
||||
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 } => {
|
||||
let workspace_node = self.folder_pad.get_workspace("1").unwrap();
|
||||
@ -73,7 +72,7 @@ impl FolderNodePadTest {
|
||||
Some(expected_app) => {
|
||||
let app_node = app.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 app = workspace_node.get_app(&id).unwrap();
|
||||
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);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,39 @@
|
||||
use crate::client_folder::script::FolderNodePadScript::*;
|
||||
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]
|
||||
fn client_folder_create_app_test() {
|
||||
|
@ -61,8 +61,8 @@ where
|
||||
predicate: impl Fn(&Arc<T>) -> bool,
|
||||
) -> Option<Arc<T>> {
|
||||
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(
|
||||
@ -72,7 +72,7 @@ where
|
||||
predicate: impl Fn(&Arc<T>) -> bool,
|
||||
) -> Option<&mut Arc<T>> {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -196,7 +196,7 @@ impl NodeTree {
|
||||
path.push(counter);
|
||||
current_node = parent_node;
|
||||
}
|
||||
|
||||
path.reverse();
|
||||
Path(path)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user