mirror of
				https://github.com/AppFlowy-IO/AppFlowy.git
				synced 2025-11-04 03:54:44 +00:00 
			
		
		
		
	chore: add Node macro
This commit is contained in:
		
							parent
							
								
									eb1659fd3e
								
							
						
					
					
						commit
						81dd353d12
					
				@ -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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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(),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -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 {
 | 
			
		||||
 | 
			
		||||
@ -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 {
 | 
			
		||||
 | 
			
		||||
@ -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()
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user