Forráskód Böngészése

chore: add Node macro

nathan 2 éve
szülő
commit
81dd353d12

+ 31 - 4
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<String>,
     /// 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<syn::Ident> {
         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<syn::Field, Token!
         })
         .collect()
 }
+
+fn get_node_type(ast_result: &ASTResult, struct_name: &Ident, attrs: &[syn::Attribute]) -> Option<String> {
+    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
+}

+ 20 - 6
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<LitStr>,
+    pub is_children: bool,
     node_index: Option<syn::LitInt>,
     get_node_value_with: Option<syn::ExprPath>,
     set_node_value_with: Option<syn::ExprPath>,
@@ -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(),
         }

+ 5 - 7
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<Vec<syn::NestedMeta>, ()> {
@@ -286,17 +286,14 @@ pub fn get_pb_meta_items(cx: &ASTResult, attr: &syn::Attribute) -> Result<Vec<sy
 
 pub fn get_node_meta_items(cx: &ASTResult, attr: &syn::Attribute) -> Result<Vec<syn::NestedMeta>, ()> {
     // 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<Vec<
         }
     }
 }
+
 pub fn get_event_meta_items(cx: &ASTResult, attr: &syn::Attribute) -> Result<Vec<syn::NestedMeta>, ()> {
     // Only handle the attribute that we have defined
     if attr.path != EVENT {

+ 7 - 2
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<Symbol> for Ident {
     fn eq(&self, word: &Symbol) -> bool {

+ 1 - 1
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()

+ 125 - 5
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<TokenStream, Vec<syn::Error>> {
     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::<Vec<&ASTField>>();
+
+    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::<Vec<NodeData>>();
+            }
+        }
+    };
+
+    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<TokenStream> {
+    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)
+}

+ 7 - 7
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<TokenStream> {
-    let pb_ty = ast.attrs.pb_struct_type()?;
+pub fn make_de_token_steam(ast_result: &ASTResult, ast: &ASTContainer) -> Option<TokenStream> {
+    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<Token
                 let member = &field.member;
                 Some(quote! { o.#member=#struct_ident::#func(pb); })
             } else if field.pb_attrs.is_one_of() {
-                token_stream_for_one_of(ctxt, field)
+                token_stream_for_one_of(ast_result, field)
             } else {
-                token_stream_for_field(ctxt, &field.member, field.ty, false)
+                token_stream_for_field(ast_result, &field.member, field.ty, false)
             }
         });
 
@@ -58,10 +58,10 @@ pub fn make_de_token_steam(ctxt: &ASTResult, ast: &ASTContainer) -> Option<Token
     // None
 }
 
-fn token_stream_for_one_of(ctxt: &ASTResult, field: &ASTField) -> Option<TokenStream> {
+fn token_stream_for_one_of(ast_result: &ASTResult, field: &ASTField) -> Option<TokenStream> {
     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);

+ 1 - 1
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<TokenStream> {
     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,

+ 1 - 1
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<TokenStream> {
-    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

+ 84 - 4
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<AtomicNodeTree>,
-    #[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<AppNode2>,
+}
+
+#[derive(Clone, Node)]
+#[node_type = "app"]
+pub struct AppNode2 {
+    tree: Arc<AtomicNodeTree>,
+    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<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(())
+}
+
+pub fn get_attributes_int_value(tree: Arc<AtomicNodeTree>, path: &Path, key: &str) -> Option<i64> {
+    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<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())
 }

+ 12 - 0
shared-lib/lib-ot/src/core/attributes/attribute.rs

@@ -293,6 +293,18 @@ impl std::convert::From<f64> for AttributeValue {
     }
 }
 
+impl std::convert::From<i64> for AttributeValue {
+    fn from(value: i64) -> Self {
+        AttributeValue::from_int(value)
+    }
+}
+
+impl std::convert::From<i32> for AttributeValue {
+    fn from(value: i32) -> Self {
+        AttributeValue::from_int(value as i64)
+    }
+}
+
 #[derive(Default)]
 pub struct AttributeBuilder {
     attributes: AttributeHashMap,

+ 9 - 0
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<NodeData>) -> 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