From 81dd353d12bd93fbdd307fba1f8df56d951cf4e8 Mon Sep 17 00:00:00 2001 From: nathan Date: Sat, 3 Dec 2022 21:12:45 +0800 Subject: [PATCH] chore: add Node macro --- shared-lib/flowy-ast/src/ast.rs | 35 ++++- shared-lib/flowy-ast/src/node_attrs.rs | 26 +++- shared-lib/flowy-ast/src/pb_attrs.rs | 12 +- shared-lib/flowy-ast/src/symbol.rs | 9 +- shared-lib/flowy-derive/src/lib.rs | 2 +- shared-lib/flowy-derive/src/node/mod.rs | 130 +++++++++++++++++- .../flowy-derive/src/proto_buf/deserialize.rs | 14 +- .../flowy-derive/src/proto_buf/enum_serde.rs | 2 +- .../flowy-derive/src/proto_buf/serialize.rs | 2 +- .../src/client_folder/workspace_node_2.rs | 88 +++++++++++- .../lib-ot/src/core/attributes/attribute.rs | 12 ++ shared-lib/lib-ot/src/core/node_tree/node.rs | 9 ++ 12 files changed, 303 insertions(+), 38 deletions(-) diff --git a/shared-lib/flowy-ast/src/ast.rs b/shared-lib/flowy-ast/src/ast.rs index 140ecedbec..4986e0a663 100644 --- a/shared-lib/flowy-ast/src/ast.rs +++ b/shared-lib/flowy-ast/src/ast.rs @@ -4,14 +4,18 @@ use crate::event_attrs::EventEnumAttrs; use crate::node_attrs::NodeStructAttrs; -use crate::{is_recognizable_field, pb_attrs, ty_ext::*, ASTResult, PBAttrsContainer, PBStructAttrs}; +use crate::{is_recognizable_field, pb_attrs, ty_ext::*, ASTResult, PBAttrsContainer, PBStructAttrs, NODE_TYPE}; +use proc_macro2::Ident; +use syn::Meta::NameValue; use syn::{self, punctuated::Punctuated}; pub struct ASTContainer<'a> { /// The struct or enum name (without generics). pub ident: syn::Ident, + + pub node_type: Option, /// Attributes on the structure. - pub attrs: PBAttrsContainer, + pub pb_attrs: PBAttrsContainer, /// The contents of the struct or enum. pub data: ASTData<'a>, } @@ -40,7 +44,13 @@ impl<'a> ASTContainer<'a> { }; let ident = ast.ident.clone(); - let item = ASTContainer { ident, attrs, data }; + let node_type = get_node_type(ast_result, &ident, &ast.attrs); + let item = ASTContainer { + ident, + pb_attrs: attrs, + node_type, + data, + }; Some(item) } } @@ -182,7 +192,6 @@ impl<'a> ASTField<'a> { } } - #[allow(dead_code)] pub fn name(&self) -> Option { if let syn::Member::Named(ident) = &self.member { Some(ident.clone()) @@ -249,3 +258,21 @@ fn fields_from_ast<'a>(cx: &ASTResult, fields: &'a Punctuated Option { + let mut node_type = None; + attrs + .iter() + .filter(|attr| attr.path.segments.iter().any(|s| s.ident == NODE_TYPE)) + .for_each(|attr| { + if let Ok(NameValue(named_value)) = attr.parse_meta() { + if node_type.is_some() { + ast_result.error_spanned_by(struct_name, "Duplicate node type definition"); + } + if let syn::Lit::Str(s) = named_value.lit { + node_type = Some(s.value()); + } + } + }); + node_type +} diff --git a/shared-lib/flowy-ast/src/node_attrs.rs b/shared-lib/flowy-ast/src/node_attrs.rs index 265f4aecee..26c11cffc6 100644 --- a/shared-lib/flowy-ast/src/node_attrs.rs +++ b/shared-lib/flowy-ast/src/node_attrs.rs @@ -1,14 +1,17 @@ use crate::{get_node_meta_items, get_pb_meta_items, parse_lit_into_expr_path, symbol::*, ASTAttr, ASTResult}; -use proc_macro2::{Group, Span, TokenStream, TokenTree}; +use proc_macro2::{Group, Ident, Span, TokenStream, TokenTree}; use quote::ToTokens; use syn::{ self, parse::{self, Parse}, + LitStr, Meta::{List, NameValue, Path}, NestedMeta::{Lit, Meta}, }; pub struct NodeStructAttrs { + pub rename: Option, + pub is_children: bool, node_index: Option, get_node_value_with: Option, set_node_value_with: Option, @@ -18,14 +21,11 @@ impl NodeStructAttrs { /// Extract out the `#[node(...)]` attributes from a struct field. pub fn from_ast(ast_result: &ASTResult, index: usize, field: &syn::Field) -> Self { let mut node_index = ASTAttr::none(ast_result, NODE_INDEX); + let mut rename = ASTAttr::none(ast_result, NODE_RENAME); + let mut is_children = ASTAttr::none(ast_result, NODE_CHILDREN); 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 ident = match &field.ident { - Some(ident) => ident.to_string(), - None => index.to_string(), - }; - for meta_item in field .attrs .iter() @@ -40,6 +40,18 @@ impl NodeStructAttrs { } } + // Parse '#[node(children)]' + Meta(Path(path)) if path == NODE_CHILDREN => { + eprintln!("😄 {:?}", path); + is_children.set(path, true); + } + + // Parse '#[node(rename = x)]' + Meta(NameValue(m)) if m.path == NODE_RENAME => { + if let syn::Lit::Str(lit) = &m.lit { + rename.set(&m.path, lit.clone()); + } + } // Parse `#[node(get_node_value_with = "...")]` Meta(NameValue(m)) if m.path == GET_NODE_VALUE_WITH => { if let Ok(path) = parse_lit_into_expr_path(ast_result, GET_NODE_VALUE_WITH, &m.lit) { @@ -66,7 +78,9 @@ impl NodeStructAttrs { } NodeStructAttrs { + rename: rename.get(), node_index: node_index.get(), + is_children: is_children.get().unwrap_or(false), get_node_value_with: get_node_value_with.get(), set_node_value_with: set_node_value_with.get(), } diff --git a/shared-lib/flowy-ast/src/pb_attrs.rs b/shared-lib/flowy-ast/src/pb_attrs.rs index 708090ee81..7ad84149fb 100644 --- a/shared-lib/flowy-ast/src/pb_attrs.rs +++ b/shared-lib/flowy-ast/src/pb_attrs.rs @@ -114,7 +114,7 @@ impl<'c, T> ASTAttr<'c, T> { } } - fn set_if_none(&mut self, value: T) { + pub(crate) fn set_if_none(&mut self, value: T) { if self.value.is_none() { self.value = Some(value); } @@ -260,7 +260,7 @@ pub enum Default { } pub fn is_recognizable_attribute(attr: &syn::Attribute) -> bool { - attr.path == PB_ATTRS || attr.path == EVENT || attr.path == NODE_ATTRS + attr.path == PB_ATTRS || attr.path == EVENT || attr.path == NODE_ATTRS || attr.path == NODES_ATTRS } pub fn get_pb_meta_items(cx: &ASTResult, attr: &syn::Attribute) -> Result, ()> { @@ -286,17 +286,14 @@ pub fn get_pb_meta_items(cx: &ASTResult, attr: &syn::Attribute) -> Result Result, ()> { // Only handle the attribute that we have defined - if attr.path != NODE_ATTRS { + if attr.path != NODE_ATTRS && attr.path != NODES_ATTRS { return Ok(vec![]); } // http://strymon.systems.ethz.ch/typename/syn/enum.Meta.html match attr.parse_meta() { Ok(List(meta)) => Ok(meta.nested.into_iter().collect()), - Ok(other) => { - cx.error_spanned_by(other, "expected #[node(...)]"); - Err(()) - } + Ok(_) => Ok(vec![]), Err(err) => { cx.error_spanned_by(attr, "attribute must be str, e.g. #[node(xx = \"xxx\")]"); cx.syn_error(err); @@ -304,6 +301,7 @@ pub fn get_node_meta_items(cx: &ASTResult, attr: &syn::Attribute) -> Result Result, ()> { // Only handle the attribute that we have defined if attr.path != EVENT { diff --git a/shared-lib/flowy-ast/src/symbol.rs b/shared-lib/flowy-ast/src/symbol.rs index 5fc05e3132..51a6e06aca 100644 --- a/shared-lib/flowy-ast/src/symbol.rs +++ b/shared-lib/flowy-ast/src/symbol.rs @@ -34,11 +34,16 @@ pub const EVENT_ERR: Symbol = Symbol("event_err"); // Node pub const NODE_ATTRS: Symbol = Symbol("node"); +pub const NODES_ATTRS: Symbol = Symbol("nodes"); +pub const NODE_TYPE: Symbol = Symbol("node_type"); +pub const NODE_INDEX: Symbol = Symbol("index"); +pub const NODE_RENAME: Symbol = Symbol("rename"); +pub const NODE_CHILDREN: Symbol = Symbol("children"); pub const SKIP_NODE_ATTRS: Symbol = Symbol("skip_node_attribute"); pub const GET_NODE_VALUE_WITH: Symbol = Symbol("get_value_with"); pub const SET_NODE_VALUE_WITH: Symbol = Symbol("set_value_with"); -//#[node(index = "1")] -pub const NODE_INDEX: Symbol = Symbol("index"); +pub const GET_VEC_ELEMENT_WITH: Symbol = Symbol("get_element_with"); +pub const GET_MUT_VEC_ELEMENT_WITH: Symbol = Symbol("get_mut_element_with"); impl PartialEq for Ident { fn eq(&self, word: &Symbol) -> bool { diff --git a/shared-lib/flowy-derive/src/lib.rs b/shared-lib/flowy-derive/src/lib.rs index 91baf7280f..06cbd2b8b2 100644 --- a/shared-lib/flowy-derive/src/lib.rs +++ b/shared-lib/flowy-derive/src/lib.rs @@ -37,7 +37,7 @@ pub fn derive_dart_event(input: TokenStream) -> TokenStream { .into() } -#[proc_macro_derive(Node, attributes(node))] +#[proc_macro_derive(Node, attributes(node, nodes, node_type))] pub fn derive_node(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); node::expand_derive(&input).unwrap_or_else(to_compile_errors).into() diff --git a/shared-lib/flowy-derive/src/node/mod.rs b/shared-lib/flowy-derive/src/node/mod.rs index b201c5e8e0..10738b255c 100644 --- a/shared-lib/flowy-derive/src/node/mod.rs +++ b/shared-lib/flowy-derive/src/node/mod.rs @@ -1,14 +1,134 @@ -use flowy_ast::{ASTContainer, ASTResult}; +use flowy_ast::{ASTContainer, ASTField, ASTResult}; use proc_macro2::TokenStream; pub fn expand_derive(input: &syn::DeriveInput) -> Result> { let ast_result = ASTResult::new(); - // let cont = match ASTContainer::from_ast(&ast_result, input) { - // Some(cont) => cont, - // None => return Err(ast_result.check().unwrap_err()), - // }; + let cont = match ASTContainer::from_ast(&ast_result, input) { + Some(cont) => cont, + None => return Err(ast_result.check().unwrap_err()), + }; let mut token_stream: TokenStream = TokenStream::default(); + token_stream.extend(make_to_node_data_token_stream(&cont)); + + if let Some(get_value_token_stream) = make_get_set_value_token_steam(&cont) { + token_stream.extend(get_value_token_stream); + } + ast_result.check()?; Ok(token_stream) } + +pub fn make_alter_children_token_stream(ast: &ASTContainer) -> TokenStream { + let mut token_streams = TokenStream::default(); + + token_streams +} + +pub fn make_to_node_data_token_stream(ast: &ASTContainer) -> TokenStream { + let struct_ident = &ast.ident; + let mut token_streams = TokenStream::default(); + let node_type = ast + .node_type + .as_ref() + .expect("Define the type of the node by using #[node_type = \"xx\" in the struct"); + let set_key_values = ast + .data + .all_fields() + .filter(|field| !field.node_attrs.is_children) + .flat_map(|field| { + let mut field_name = field.name().expect("the name of the field should not be empty"); + let original_field_name = field.name().expect("the name of the field should not be empty"); + if let Some(rename) = &field.node_attrs.rename { + field_name = format_ident!("{}", rename.value()); + } + let field_name_str = field_name.to_string(); + quote! { + .insert_attribute(#field_name_str, self.#original_field_name.clone()) + } + }); + + let children_fields = ast + .data + .all_fields() + .filter(|field| field.node_attrs.is_children) + .collect::>(); + + let childrens_token_streams = match children_fields.is_empty() { + true => { + quote! { + let children = vec![]; + } + } + false => { + let children_field = children_fields.first().unwrap(); + let original_field_name = children_field + .name() + .expect("the name of the field should not be empty"); + quote! { + let children = self.#original_field_name.iter().map(|value| value.to_node_data()).collect::>(); + } + } + }; + + token_streams.extend(quote! { + impl #struct_ident { + pub fn to_node_data(&self) -> NodeData { + #childrens_token_streams + + let builder = NodeDataBuilder::new(#node_type) + #(#set_key_values)* + .extend_node_data(children); + + builder.build() + } + } + }); + + token_streams +} + +pub fn make_get_set_value_token_steam(ast: &ASTContainer) -> Option { + let struct_ident = &ast.ident; + let mut token_streams = TokenStream::default(); + + let tree = format_ident!("tree"); + for field in ast.data.all_fields() { + if field.node_attrs.is_children { + continue; + } + + let mut field_name = field.name().expect("the name of the field should not be empty"); + if let Some(rename) = &field.node_attrs.rename { + field_name = format_ident!("{}", rename.value()); + } + + let field_name_str = field_name.to_string(); + let get_func_name = format_ident!("get_{}", field_name); + let set_func_name = format_ident!("set_{}", field_name); + 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() { + 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.path, #field_name_str) + } + } + }); + } + + 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.path, #field_name_str, value); + } + } + }); + } + } + ast.data.all_fields().for_each(|field| {}); + Some(token_streams) +} diff --git a/shared-lib/flowy-derive/src/proto_buf/deserialize.rs b/shared-lib/flowy-derive/src/proto_buf/deserialize.rs index f7b7a408cf..a781fae30e 100644 --- a/shared-lib/flowy-derive/src/proto_buf/deserialize.rs +++ b/shared-lib/flowy-derive/src/proto_buf/deserialize.rs @@ -2,8 +2,8 @@ use crate::proto_buf::util::*; use flowy_ast::*; use proc_macro2::{Span, TokenStream}; -pub fn make_de_token_steam(ctxt: &ASTResult, ast: &ASTContainer) -> Option { - let pb_ty = ast.attrs.pb_struct_type()?; +pub fn make_de_token_steam(ast_result: &ASTResult, ast: &ASTContainer) -> Option { + let pb_ty = ast.pb_attrs.pb_struct_type()?; let struct_ident = &ast.ident; let build_take_fields = ast @@ -15,9 +15,9 @@ pub fn make_de_token_steam(ctxt: &ASTResult, ast: &ASTContainer) -> Option Option Option { +fn token_stream_for_one_of(ast_result: &ASTResult, field: &ASTField) -> Option { let member = &field.member; - let ident = get_member_ident(ctxt, member)?; - let ty_info = match parse_ty(ctxt, field.ty) { + let ident = get_member_ident(ast_result, member)?; + let ty_info = match parse_ty(ast_result, field.ty) { Ok(ty_info) => ty_info, Err(e) => { eprintln!("token_stream_for_one_of failed: {:?} with error: {}", member, e); diff --git a/shared-lib/flowy-derive/src/proto_buf/enum_serde.rs b/shared-lib/flowy-derive/src/proto_buf/enum_serde.rs index adb1cf8852..63fa0e4883 100644 --- a/shared-lib/flowy-derive/src/proto_buf/enum_serde.rs +++ b/shared-lib/flowy-derive/src/proto_buf/enum_serde.rs @@ -4,7 +4,7 @@ use proc_macro2::TokenStream; #[allow(dead_code)] pub fn make_enum_token_stream(_ast_result: &ASTResult, cont: &ASTContainer) -> Option { let enum_ident = &cont.ident; - let pb_enum = cont.attrs.pb_enum_type()?; + let pb_enum = cont.pb_attrs.pb_enum_type()?; let build_to_pb_enum = cont.data.all_idents().map(|i| { let token_stream: TokenStream = quote! { #enum_ident::#i => crate::protobuf::#pb_enum::#i, diff --git a/shared-lib/flowy-derive/src/proto_buf/serialize.rs b/shared-lib/flowy-derive/src/proto_buf/serialize.rs index e5c2c6297f..97481acc88 100644 --- a/shared-lib/flowy-derive/src/proto_buf/serialize.rs +++ b/shared-lib/flowy-derive/src/proto_buf/serialize.rs @@ -4,7 +4,7 @@ use flowy_ast::*; use proc_macro2::TokenStream; pub fn make_se_token_stream(ast_result: &ASTResult, ast: &ASTContainer) -> Option { - let pb_ty = ast.attrs.pb_struct_type()?; + let pb_ty = ast.pb_attrs.pb_struct_type()?; let struct_ident = &ast.ident; let build_set_pb_fields = ast diff --git a/shared-lib/flowy-sync/src/client_folder/workspace_node_2.rs b/shared-lib/flowy-sync/src/client_folder/workspace_node_2.rs index 143352f575..47901daee3 100644 --- a/shared-lib/flowy-sync/src/client_folder/workspace_node_2.rs +++ b/shared-lib/flowy-sync/src/client_folder/workspace_node_2.rs @@ -1,12 +1,92 @@ use crate::client_folder::AtomicNodeTree; +use crate::errors::CollaborateResult; use flowy_derive::Node; +use lib_ot::core::*; use std::sync::Arc; -#[derive(Debug, Clone, Node)] +#[derive(Clone, Node)] +#[node_type = "workspace"] pub struct WorkspaceNode2 { tree: Arc, - #[node] + path: Path, + + #[node(get_value_with = "get_attributes_str_value")] + #[node(set_value_with = "set_attributes_str_value")] pub id: String, - // pub name: String, - // pub path: Path, + + #[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(children)] + pub apps: Vec, +} + +#[derive(Clone, Node)] +#[node_type = "app"] +pub struct AppNode2 { + tree: Arc, + path: Path, + + #[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, +} + +pub fn get_attributes_str_value(tree: Arc, path: &Path, key: &str) -> Option { + 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, + 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(()) +} + +pub fn get_attributes_int_value(tree: Arc, path: &Path, key: &str) -> Option { + tree.read() + .get_node_at_path(&path) + .and_then(|node| node.attributes.get(key).cloned()) + .and_then(|value| value.int_value()) +} + +pub fn get_attributes(tree: Arc, path: &Path) -> Option { + tree.read() + .get_node_at_path(&path) + .and_then(|node| Some(node.attributes.clone())) +} + +pub fn get_attributes_value(tree: Arc, path: &Path, key: &str) -> Option { + tree.read() + .get_node_at_path(&path) + .and_then(|node| node.attributes.get(key).cloned()) } diff --git a/shared-lib/lib-ot/src/core/attributes/attribute.rs b/shared-lib/lib-ot/src/core/attributes/attribute.rs index bed2baade8..90c2398440 100644 --- a/shared-lib/lib-ot/src/core/attributes/attribute.rs +++ b/shared-lib/lib-ot/src/core/attributes/attribute.rs @@ -293,6 +293,18 @@ impl std::convert::From for AttributeValue { } } +impl std::convert::From for AttributeValue { + fn from(value: i64) -> Self { + AttributeValue::from_int(value) + } +} + +impl std::convert::From for AttributeValue { + fn from(value: i32) -> Self { + AttributeValue::from_int(value as i64) + } +} + #[derive(Default)] pub struct AttributeBuilder { attributes: AttributeHashMap, diff --git a/shared-lib/lib-ot/src/core/node_tree/node.rs b/shared-lib/lib-ot/src/core/node_tree/node.rs index 2608912645..67fdbd3110 100644 --- a/shared-lib/lib-ot/src/core/node_tree/node.rs +++ b/shared-lib/lib-ot/src/core/node_tree/node.rs @@ -6,6 +6,10 @@ use crate::errors::OTError; use crate::text_delta::DeltaTextOperations; use serde::{Deserialize, Serialize}; +pub trait ToNodeData: Send + Sync { + fn to_node_data(&self) -> NodeData; +} + #[derive(Default, Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub struct NodeData { #[serde(rename = "type")] @@ -66,6 +70,11 @@ impl NodeDataBuilder { self } + pub fn extend_node_data(mut self, node: Vec) -> Self { + self.node.children.extend(node); + self + } + /// Inserts attributes to the builder's node. /// /// The attributes will be replace if they shared the same key