mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: add node ast parser
This commit is contained in:
parent
c89277d507
commit
eb1659fd3e
@ -1,21 +1,24 @@
|
||||
#![allow(clippy::all)]
|
||||
#![allow(unused_attributes)]
|
||||
#![allow(unused_assignments)]
|
||||
use crate::{attr, ty_ext::*, ASTResult, AttrsContainer};
|
||||
|
||||
use crate::event_attrs::EventEnumAttrs;
|
||||
use crate::node_attrs::NodeStructAttrs;
|
||||
use crate::{is_recognizable_field, pb_attrs, ty_ext::*, ASTResult, PBAttrsContainer, PBStructAttrs};
|
||||
use syn::{self, punctuated::Punctuated};
|
||||
|
||||
pub struct ASTContainer<'a> {
|
||||
/// The struct or enum name (without generics).
|
||||
pub ident: syn::Ident,
|
||||
/// Attributes on the structure.
|
||||
pub attrs: AttrsContainer,
|
||||
pub attrs: PBAttrsContainer,
|
||||
/// The contents of the struct or enum.
|
||||
pub data: ASTData<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ASTContainer<'a> {
|
||||
pub fn from_ast(ast_result: &ASTResult, ast: &'a syn::DeriveInput) -> Option<ASTContainer<'a>> {
|
||||
let attrs = AttrsContainer::from_ast(ast_result, ast);
|
||||
let attrs = PBAttrsContainer::from_ast(ast_result, ast);
|
||||
// syn::DeriveInput
|
||||
// 1. syn::DataUnion
|
||||
// 2. syn::DataStruct
|
||||
@ -55,7 +58,7 @@ impl<'a> ASTData<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_variants(&'a self) -> Box<dyn Iterator<Item = &'a attr::ASTEnumAttrVariant> + 'a> {
|
||||
pub fn all_variants(&'a self) -> Box<dyn Iterator<Item = &'a EventEnumAttrs> + 'a> {
|
||||
match self {
|
||||
ASTData::Enum(variants) => {
|
||||
let iter = variants.iter().map(|variant| &variant.attrs);
|
||||
@ -85,7 +88,7 @@ impl<'a> ASTData<'a> {
|
||||
/// A variant of an enum.
|
||||
pub struct ASTEnumVariant<'a> {
|
||||
pub ident: syn::Ident,
|
||||
pub attrs: attr::ASTEnumAttrVariant,
|
||||
pub attrs: EventEnumAttrs,
|
||||
pub style: ASTStyle,
|
||||
pub fields: Vec<ASTField<'a>>,
|
||||
pub original: &'a syn::Variant,
|
||||
@ -106,7 +109,8 @@ pub enum BracketCategory {
|
||||
|
||||
pub struct ASTField<'a> {
|
||||
pub member: syn::Member,
|
||||
pub attrs: attr::ASTAttrField,
|
||||
pub pb_attrs: PBStructAttrs,
|
||||
pub node_attrs: NodeStructAttrs,
|
||||
pub ty: &'a syn::Type,
|
||||
pub original: &'a syn::Field,
|
||||
pub bracket_ty: Option<syn::Ident>,
|
||||
@ -161,7 +165,8 @@ impl<'a> ASTField<'a> {
|
||||
Some(ident) => syn::Member::Named(ident.clone()),
|
||||
None => syn::Member::Unnamed(index.into()),
|
||||
},
|
||||
attrs: attr::ASTAttrField::from_ast(cx, index, field),
|
||||
pb_attrs: PBStructAttrs::from_ast(cx, index, field),
|
||||
node_attrs: NodeStructAttrs::from_ast(cx, index, field),
|
||||
ty: &field.ty,
|
||||
original: field,
|
||||
bracket_ty,
|
||||
@ -185,10 +190,6 @@ impl<'a> ASTField<'a> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_option(&self) -> bool {
|
||||
attr::is_option(self.ty)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@ -222,7 +223,7 @@ pub fn enum_from_ast<'a>(
|
||||
variants
|
||||
.iter()
|
||||
.flat_map(|variant| {
|
||||
let attrs = attr::ASTEnumAttrVariant::from_ast(cx, ident, variant, enum_attrs);
|
||||
let attrs = EventEnumAttrs::from_ast(cx, ident, variant, enum_attrs);
|
||||
let (style, fields) = struct_from_ast(cx, &variant.fields);
|
||||
Some(ASTEnumVariant {
|
||||
ident: variant.ident.clone(),
|
||||
@ -239,6 +240,12 @@ fn fields_from_ast<'a>(cx: &ASTResult, fields: &'a Punctuated<syn::Field, Token!
|
||||
fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(index, field)| ASTField::new(cx, field, index).ok())
|
||||
.flat_map(|(index, field)| {
|
||||
if is_recognizable_field(field) {
|
||||
ASTField::new(cx, field, index).ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
147
shared-lib/flowy-ast/src/event_attrs.rs
Normal file
147
shared-lib/flowy-ast/src/event_attrs.rs
Normal file
@ -0,0 +1,147 @@
|
||||
use crate::{get_event_meta_items, parse_lit_str, symbol::*, ASTResult};
|
||||
|
||||
|
||||
use syn::{
|
||||
self,
|
||||
parse::{self, Parse},
|
||||
Meta::{List, NameValue, Path},
|
||||
NestedMeta::{Lit, Meta},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EventAttrs {
|
||||
input: Option<syn::Path>,
|
||||
output: Option<syn::Path>,
|
||||
error_ty: Option<String>,
|
||||
pub ignore: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EventEnumAttrs {
|
||||
pub enum_name: String,
|
||||
pub enum_item_name: String,
|
||||
pub value: String,
|
||||
pub event_attrs: EventAttrs,
|
||||
}
|
||||
|
||||
impl EventEnumAttrs {
|
||||
pub fn from_ast(
|
||||
ast_result: &ASTResult,
|
||||
ident: &syn::Ident,
|
||||
variant: &syn::Variant,
|
||||
enum_attrs: &[syn::Attribute],
|
||||
) -> Self {
|
||||
let enum_item_name = variant.ident.to_string();
|
||||
let enum_name = ident.to_string();
|
||||
let mut value = String::new();
|
||||
if variant.discriminant.is_some() {
|
||||
if let syn::Expr::Lit(ref expr_list) = variant.discriminant.as_ref().unwrap().1 {
|
||||
let lit_int = if let syn::Lit::Int(ref int_value) = expr_list.lit {
|
||||
int_value
|
||||
} else {
|
||||
unimplemented!()
|
||||
};
|
||||
value = lit_int.base10_digits().to_string();
|
||||
}
|
||||
}
|
||||
let event_attrs = get_event_attrs_from(ast_result, &variant.attrs, enum_attrs);
|
||||
EventEnumAttrs {
|
||||
enum_name,
|
||||
enum_item_name,
|
||||
value,
|
||||
event_attrs,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event_input(&self) -> Option<syn::Path> {
|
||||
self.event_attrs.input.clone()
|
||||
}
|
||||
|
||||
pub fn event_output(&self) -> Option<syn::Path> {
|
||||
self.event_attrs.output.clone()
|
||||
}
|
||||
|
||||
pub fn event_error(&self) -> String {
|
||||
self.event_attrs.error_ty.as_ref().unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_event_attrs_from(
|
||||
ast_result: &ASTResult,
|
||||
variant_attrs: &[syn::Attribute],
|
||||
enum_attrs: &[syn::Attribute],
|
||||
) -> EventAttrs {
|
||||
let mut event_attrs = EventAttrs {
|
||||
input: None,
|
||||
output: None,
|
||||
error_ty: None,
|
||||
ignore: false,
|
||||
};
|
||||
|
||||
enum_attrs
|
||||
.iter()
|
||||
.filter(|attr| attr.path.segments.iter().any(|s| s.ident == EVENT_ERR))
|
||||
.for_each(|attr| {
|
||||
if let Ok(NameValue(named_value)) = attr.parse_meta() {
|
||||
if let syn::Lit::Str(s) = named_value.lit {
|
||||
event_attrs.error_ty = Some(s.value());
|
||||
} else {
|
||||
eprintln!("❌ {} should not be empty", EVENT_ERR);
|
||||
}
|
||||
} else {
|
||||
eprintln!("❌ Can not find any {} on attr: {:#?}", EVENT_ERR, attr);
|
||||
}
|
||||
});
|
||||
|
||||
let mut extract_event_attr = |attr: &syn::Attribute, meta_item: &syn::NestedMeta| match &meta_item {
|
||||
Meta(NameValue(name_value)) => {
|
||||
if name_value.path == EVENT_INPUT {
|
||||
if let syn::Lit::Str(s) = &name_value.lit {
|
||||
let input_type = parse_lit_str(s)
|
||||
.map_err(|_| {
|
||||
ast_result
|
||||
.error_spanned_by(s, format!("failed to parse request deserializer {:?}", s.value()))
|
||||
})
|
||||
.unwrap();
|
||||
event_attrs.input = Some(input_type);
|
||||
}
|
||||
}
|
||||
|
||||
if name_value.path == EVENT_OUTPUT {
|
||||
if let syn::Lit::Str(s) = &name_value.lit {
|
||||
let output_type = parse_lit_str(s)
|
||||
.map_err(|_| {
|
||||
ast_result
|
||||
.error_spanned_by(s, format!("failed to parse response deserializer {:?}", s.value()))
|
||||
})
|
||||
.unwrap();
|
||||
event_attrs.output = Some(output_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
Meta(Path(word)) => {
|
||||
if word == EVENT_IGNORE && attr.path == EVENT {
|
||||
event_attrs.ignore = true;
|
||||
}
|
||||
}
|
||||
Lit(s) => ast_result.error_spanned_by(s, "unexpected attribute"),
|
||||
_ => ast_result.error_spanned_by(meta_item, "unexpected attribute"),
|
||||
};
|
||||
|
||||
let attr_meta_items_info = variant_attrs
|
||||
.iter()
|
||||
.flat_map(|attr| match get_event_meta_items(ast_result, attr) {
|
||||
Ok(items) => Some((attr, items)),
|
||||
Err(_) => None,
|
||||
})
|
||||
.collect::<Vec<(&syn::Attribute, Vec<syn::NestedMeta>)>>();
|
||||
|
||||
for (attr, nested_metas) in attr_meta_items_info {
|
||||
nested_metas
|
||||
.iter()
|
||||
.for_each(|meta_item| extract_event_attr(attr, meta_item))
|
||||
}
|
||||
|
||||
// eprintln!("😁{:#?}", event_attrs);
|
||||
event_attrs
|
||||
}
|
@ -5,12 +5,16 @@ extern crate syn;
|
||||
extern crate quote;
|
||||
|
||||
mod ast;
|
||||
mod attr;
|
||||
mod ctxt;
|
||||
mod pb_attrs;
|
||||
|
||||
mod event_attrs;
|
||||
mod node_attrs;
|
||||
pub mod symbol;
|
||||
pub mod ty_ext;
|
||||
|
||||
pub use self::{symbol::*, ty_ext::*};
|
||||
pub use ast::*;
|
||||
pub use attr::*;
|
||||
pub use ctxt::ASTResult;
|
||||
pub use event_attrs::*;
|
||||
pub use pb_attrs::*;
|
||||
|
82
shared-lib/flowy-ast/src/node_attrs.rs
Normal file
82
shared-lib/flowy-ast/src/node_attrs.rs
Normal file
@ -0,0 +1,82 @@
|
||||
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 quote::ToTokens;
|
||||
use syn::{
|
||||
self,
|
||||
parse::{self, Parse},
|
||||
Meta::{List, NameValue, Path},
|
||||
NestedMeta::{Lit, Meta},
|
||||
};
|
||||
|
||||
pub struct NodeStructAttrs {
|
||||
node_index: Option<syn::LitInt>,
|
||||
get_node_value_with: Option<syn::ExprPath>,
|
||||
set_node_value_with: 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 {
|
||||
let mut node_index = ASTAttr::none(ast_result, 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 ident = match &field.ident {
|
||||
Some(ident) => ident.to_string(),
|
||||
None => index.to_string(),
|
||||
};
|
||||
|
||||
for meta_item in field
|
||||
.attrs
|
||||
.iter()
|
||||
.flat_map(|attr| get_node_meta_items(ast_result, attr))
|
||||
.flatten()
|
||||
{
|
||||
match &meta_item {
|
||||
// Parse '#[node(index = x)]'
|
||||
Meta(NameValue(m)) if m.path == NODE_INDEX => {
|
||||
if let syn::Lit::Int(lit) = &m.lit {
|
||||
node_index.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) {
|
||||
get_node_value_with.set(&m.path, path);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[node(set_node_value_with= "...")]`
|
||||
Meta(NameValue(m)) if m.path == SET_NODE_VALUE_WITH => {
|
||||
if let Ok(path) = parse_lit_into_expr_path(ast_result, SET_NODE_VALUE_WITH, &m.lit) {
|
||||
set_node_value_with.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));
|
||||
}
|
||||
|
||||
Lit(lit) => {
|
||||
ast_result.error_spanned_by(lit, "unexpected literal in field attribute");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NodeStructAttrs {
|
||||
node_index: node_index.get(),
|
||||
get_node_value_with: get_node_value_with.get(),
|
||||
set_node_value_with: set_node_value_with.get(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_node_value_with(&self) -> Option<&syn::ExprPath> {
|
||||
self.set_node_value_with.as_ref()
|
||||
}
|
||||
|
||||
pub fn get_node_value_with(&self) -> Option<&syn::ExprPath> {
|
||||
self.get_node_value_with.as_ref()
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
#![allow(clippy::all)]
|
||||
|
||||
use crate::{symbol::*, ASTResult};
|
||||
use proc_macro2::{Group, Span, TokenStream, TokenTree};
|
||||
use quote::ToTokens;
|
||||
use syn::{
|
||||
self,
|
||||
@ -8,16 +10,14 @@ use syn::{
|
||||
NestedMeta::{Lit, Meta},
|
||||
};
|
||||
|
||||
use proc_macro2::{Group, Span, TokenStream, TokenTree};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct AttrsContainer {
|
||||
pub struct PBAttrsContainer {
|
||||
name: String,
|
||||
pb_struct_type: Option<syn::Type>,
|
||||
pb_enum_type: Option<syn::Type>,
|
||||
}
|
||||
|
||||
impl AttrsContainer {
|
||||
impl PBAttrsContainer {
|
||||
/// Extract out the `#[pb(...)]` attributes from an item.
|
||||
pub fn from_ast(ast_result: &ASTResult, item: &syn::DeriveInput) -> Self {
|
||||
let mut pb_struct_type = ASTAttr::none(ast_result, PB_STRUCT);
|
||||
@ -25,7 +25,7 @@ impl AttrsContainer {
|
||||
for meta_item in item
|
||||
.attrs
|
||||
.iter()
|
||||
.flat_map(|attr| get_meta_items(ast_result, attr))
|
||||
.flat_map(|attr| get_pb_meta_items(ast_result, attr))
|
||||
.flatten()
|
||||
{
|
||||
match &meta_item {
|
||||
@ -45,11 +45,11 @@ impl AttrsContainer {
|
||||
|
||||
Meta(meta_item) => {
|
||||
let path = meta_item.path().into_token_stream().to_string().replace(' ', "");
|
||||
ast_result.error_spanned_by(meta_item.path(), format!("unknown pb container attribute `{}`", path));
|
||||
ast_result.error_spanned_by(meta_item.path(), format!("unknown container attribute `{}`", path));
|
||||
}
|
||||
|
||||
Lit(lit) => {
|
||||
ast_result.error_spanned_by(lit, "unexpected literal in pb container attribute");
|
||||
ast_result.error_spanned_by(lit, "unexpected literal in container attribute");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -63,7 +63,7 @@ impl AttrsContainer {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
AttrsContainer {
|
||||
PBAttrsContainer {
|
||||
name: item.ident.to_string(),
|
||||
pb_struct_type: pb_struct_type.get(),
|
||||
pb_enum_type: pb_enum_type.get(),
|
||||
@ -79,7 +79,7 @@ impl AttrsContainer {
|
||||
}
|
||||
}
|
||||
|
||||
struct ASTAttr<'c, T> {
|
||||
pub struct ASTAttr<'c, T> {
|
||||
ast_result: &'c ASTResult,
|
||||
name: Symbol,
|
||||
tokens: TokenStream,
|
||||
@ -87,7 +87,7 @@ struct ASTAttr<'c, T> {
|
||||
}
|
||||
|
||||
impl<'c, T> ASTAttr<'c, T> {
|
||||
fn none(ast_result: &'c ASTResult, name: Symbol) -> Self {
|
||||
pub(crate) fn none(ast_result: &'c ASTResult, name: Symbol) -> Self {
|
||||
ASTAttr {
|
||||
ast_result,
|
||||
name,
|
||||
@ -96,7 +96,7 @@ impl<'c, T> ASTAttr<'c, T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn set<A: ToTokens>(&mut self, obj: A, value: T) {
|
||||
pub(crate) fn set<A: ToTokens>(&mut self, obj: A, value: T) {
|
||||
let tokens = obj.into_token_stream();
|
||||
|
||||
if self.value.is_some() {
|
||||
@ -120,7 +120,7 @@ impl<'c, T> ASTAttr<'c, T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get(self) -> Option<T> {
|
||||
pub(crate) fn get(self) -> Option<T> {
|
||||
self.value
|
||||
}
|
||||
|
||||
@ -133,26 +133,30 @@ impl<'c, T> ASTAttr<'c, T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ASTAttrField {
|
||||
pub struct PBStructAttrs {
|
||||
#[allow(dead_code)]
|
||||
name: String,
|
||||
pb_index: Option<syn::LitInt>,
|
||||
pb_one_of: bool,
|
||||
skip_serializing: bool,
|
||||
skip_deserializing: bool,
|
||||
serialize_with: Option<syn::ExprPath>,
|
||||
deserialize_with: Option<syn::ExprPath>,
|
||||
skip_pb_serializing: bool,
|
||||
skip_pb_deserializing: bool,
|
||||
serialize_pb_with: Option<syn::ExprPath>,
|
||||
deserialize_pb_with: Option<syn::ExprPath>,
|
||||
}
|
||||
|
||||
impl ASTAttrField {
|
||||
pub fn is_recognizable_field(field: &syn::Field) -> bool {
|
||||
field.attrs.iter().any(|attr| is_recognizable_attribute(attr))
|
||||
}
|
||||
|
||||
impl PBStructAttrs {
|
||||
/// Extract out the `#[pb(...)]` attributes from a struct field.
|
||||
pub fn from_ast(ast_result: &ASTResult, index: usize, field: &syn::Field) -> Self {
|
||||
let mut pb_index = ASTAttr::none(ast_result, PB_INDEX);
|
||||
let mut pb_one_of = BoolAttr::none(ast_result, PB_ONE_OF);
|
||||
let mut serialize_with = ASTAttr::none(ast_result, SERIALIZE_WITH);
|
||||
let mut skip_serializing = BoolAttr::none(ast_result, SKIP_SERIALIZING);
|
||||
let mut deserialize_with = ASTAttr::none(ast_result, DESERIALIZE_WITH);
|
||||
let mut skip_deserializing = BoolAttr::none(ast_result, SKIP_DESERIALIZING);
|
||||
let mut serialize_pb_with = ASTAttr::none(ast_result, SERIALIZE_PB_WITH);
|
||||
let mut skip_pb_serializing = BoolAttr::none(ast_result, SKIP_PB_SERIALIZING);
|
||||
let mut deserialize_pb_with = ASTAttr::none(ast_result, DESERIALIZE_PB_WITH);
|
||||
let mut skip_pb_deserializing = BoolAttr::none(ast_result, SKIP_PB_DESERIALIZING);
|
||||
|
||||
let ident = match &field.ident {
|
||||
Some(ident) => ident.to_string(),
|
||||
@ -162,14 +166,14 @@ impl ASTAttrField {
|
||||
for meta_item in field
|
||||
.attrs
|
||||
.iter()
|
||||
.flat_map(|attr| get_meta_items(ast_result, attr))
|
||||
.flat_map(|attr| get_pb_meta_items(ast_result, attr))
|
||||
.flatten()
|
||||
{
|
||||
match &meta_item {
|
||||
// Parse `#[pb(skip)]`
|
||||
Meta(Path(word)) if word == SKIP => {
|
||||
skip_serializing.set_true(word);
|
||||
skip_deserializing.set_true(word);
|
||||
skip_pb_serializing.set_true(word);
|
||||
skip_pb_deserializing.set_true(word);
|
||||
}
|
||||
|
||||
// Parse '#[pb(index = x)]'
|
||||
@ -184,39 +188,39 @@ impl ASTAttrField {
|
||||
pb_one_of.set_true(path);
|
||||
}
|
||||
|
||||
// Parse `#[pb(serialize_with = "...")]`
|
||||
Meta(NameValue(m)) if m.path == SERIALIZE_WITH => {
|
||||
if let Ok(path) = parse_lit_into_expr_path(ast_result, SERIALIZE_WITH, &m.lit) {
|
||||
serialize_with.set(&m.path, path);
|
||||
// Parse `#[pb(serialize_pb_with = "...")]`
|
||||
Meta(NameValue(m)) if m.path == SERIALIZE_PB_WITH => {
|
||||
if let Ok(path) = parse_lit_into_expr_path(ast_result, SERIALIZE_PB_WITH, &m.lit) {
|
||||
serialize_pb_with.set(&m.path, path);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[pb(deserialize_with = "...")]`
|
||||
Meta(NameValue(m)) if m.path == DESERIALIZE_WITH => {
|
||||
if let Ok(path) = parse_lit_into_expr_path(ast_result, DESERIALIZE_WITH, &m.lit) {
|
||||
deserialize_with.set(&m.path, path);
|
||||
// Parse `#[pb(deserialize_pb_with = "...")]`
|
||||
Meta(NameValue(m)) if m.path == DESERIALIZE_PB_WITH => {
|
||||
if let Ok(path) = parse_lit_into_expr_path(ast_result, DESERIALIZE_PB_WITH, &m.lit) {
|
||||
deserialize_pb_with.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 field attribute `{}`", path));
|
||||
ast_result.error_spanned_by(meta_item.path(), format!("unknown pb field attribute `{}`", path));
|
||||
}
|
||||
|
||||
Lit(lit) => {
|
||||
ast_result.error_spanned_by(lit, "unexpected literal in pb field attribute");
|
||||
ast_result.error_spanned_by(lit, "unexpected literal in field attribute");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ASTAttrField {
|
||||
PBStructAttrs {
|
||||
name: ident,
|
||||
pb_index: pb_index.get(),
|
||||
pb_one_of: pb_one_of.get(),
|
||||
skip_serializing: skip_serializing.get(),
|
||||
skip_deserializing: skip_deserializing.get(),
|
||||
serialize_with: serialize_with.get(),
|
||||
deserialize_with: deserialize_with.get(),
|
||||
skip_pb_serializing: skip_pb_serializing.get(),
|
||||
skip_pb_deserializing: skip_pb_deserializing.get(),
|
||||
serialize_pb_with: serialize_pb_with.get(),
|
||||
deserialize_pb_with: deserialize_pb_with.get(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -229,20 +233,20 @@ impl ASTAttrField {
|
||||
self.pb_one_of
|
||||
}
|
||||
|
||||
pub fn serialize_with(&self) -> Option<&syn::ExprPath> {
|
||||
self.serialize_with.as_ref()
|
||||
pub fn serialize_pb_with(&self) -> Option<&syn::ExprPath> {
|
||||
self.serialize_pb_with.as_ref()
|
||||
}
|
||||
|
||||
pub fn deserialize_with(&self) -> Option<&syn::ExprPath> {
|
||||
self.deserialize_with.as_ref()
|
||||
pub fn deserialize_pb_with(&self) -> Option<&syn::ExprPath> {
|
||||
self.deserialize_pb_with.as_ref()
|
||||
}
|
||||
|
||||
pub fn skip_serializing(&self) -> bool {
|
||||
self.skip_serializing
|
||||
pub fn skip_pb_serializing(&self) -> bool {
|
||||
self.skip_pb_serializing
|
||||
}
|
||||
|
||||
pub fn skip_deserializing(&self) -> bool {
|
||||
self.skip_deserializing
|
||||
pub fn skip_pb_deserializing(&self) -> bool {
|
||||
self.skip_pb_deserializing
|
||||
}
|
||||
}
|
||||
|
||||
@ -255,146 +259,13 @@ pub enum Default {
|
||||
Path(syn::ExprPath),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EventAttrs {
|
||||
input: Option<syn::Path>,
|
||||
output: Option<syn::Path>,
|
||||
error_ty: Option<String>,
|
||||
pub ignore: bool,
|
||||
pub fn is_recognizable_attribute(attr: &syn::Attribute) -> bool {
|
||||
attr.path == PB_ATTRS || attr.path == EVENT || attr.path == NODE_ATTRS
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ASTEnumAttrVariant {
|
||||
pub enum_name: String,
|
||||
pub enum_item_name: String,
|
||||
pub value: String,
|
||||
pub event_attrs: EventAttrs,
|
||||
}
|
||||
|
||||
impl ASTEnumAttrVariant {
|
||||
pub fn from_ast(
|
||||
ast_result: &ASTResult,
|
||||
ident: &syn::Ident,
|
||||
variant: &syn::Variant,
|
||||
enum_attrs: &[syn::Attribute],
|
||||
) -> Self {
|
||||
let enum_item_name = variant.ident.to_string();
|
||||
let enum_name = ident.to_string();
|
||||
let mut value = String::new();
|
||||
if variant.discriminant.is_some() {
|
||||
if let syn::Expr::Lit(ref expr_list) = variant.discriminant.as_ref().unwrap().1 {
|
||||
let lit_int = if let syn::Lit::Int(ref int_value) = expr_list.lit {
|
||||
int_value
|
||||
} else {
|
||||
unimplemented!()
|
||||
};
|
||||
value = lit_int.base10_digits().to_string();
|
||||
}
|
||||
}
|
||||
let event_attrs = get_event_attrs_from(ast_result, &variant.attrs, enum_attrs);
|
||||
ASTEnumAttrVariant {
|
||||
enum_name,
|
||||
enum_item_name,
|
||||
value,
|
||||
event_attrs,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event_input(&self) -> Option<syn::Path> {
|
||||
self.event_attrs.input.clone()
|
||||
}
|
||||
|
||||
pub fn event_output(&self) -> Option<syn::Path> {
|
||||
self.event_attrs.output.clone()
|
||||
}
|
||||
|
||||
pub fn event_error(&self) -> String {
|
||||
self.event_attrs.error_ty.as_ref().unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_event_attrs_from(
|
||||
ast_result: &ASTResult,
|
||||
variant_attrs: &[syn::Attribute],
|
||||
enum_attrs: &[syn::Attribute],
|
||||
) -> EventAttrs {
|
||||
let mut event_attrs = EventAttrs {
|
||||
input: None,
|
||||
output: None,
|
||||
error_ty: None,
|
||||
ignore: false,
|
||||
};
|
||||
|
||||
enum_attrs
|
||||
.iter()
|
||||
.filter(|attr| attr.path.segments.iter().any(|s| s.ident == EVENT_ERR))
|
||||
.for_each(|attr| {
|
||||
if let Ok(NameValue(named_value)) = attr.parse_meta() {
|
||||
if let syn::Lit::Str(s) = named_value.lit {
|
||||
event_attrs.error_ty = Some(s.value());
|
||||
} else {
|
||||
eprintln!("❌ {} should not be empty", EVENT_ERR);
|
||||
}
|
||||
} else {
|
||||
eprintln!("❌ Can not find any {} on attr: {:#?}", EVENT_ERR, attr);
|
||||
}
|
||||
});
|
||||
|
||||
let mut extract_event_attr = |attr: &syn::Attribute, meta_item: &syn::NestedMeta| match &meta_item {
|
||||
Meta(NameValue(name_value)) => {
|
||||
if name_value.path == EVENT_INPUT {
|
||||
if let syn::Lit::Str(s) = &name_value.lit {
|
||||
let input_type = parse_lit_str(s)
|
||||
.map_err(|_| {
|
||||
ast_result
|
||||
.error_spanned_by(s, format!("failed to parse request deserializer {:?}", s.value()))
|
||||
})
|
||||
.unwrap();
|
||||
event_attrs.input = Some(input_type);
|
||||
}
|
||||
}
|
||||
|
||||
if name_value.path == EVENT_OUTPUT {
|
||||
if let syn::Lit::Str(s) = &name_value.lit {
|
||||
let output_type = parse_lit_str(s)
|
||||
.map_err(|_| {
|
||||
ast_result
|
||||
.error_spanned_by(s, format!("failed to parse response deserializer {:?}", s.value()))
|
||||
})
|
||||
.unwrap();
|
||||
event_attrs.output = Some(output_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
Meta(Path(word)) => {
|
||||
if word == EVENT_IGNORE && attr.path == EVENT {
|
||||
event_attrs.ignore = true;
|
||||
}
|
||||
}
|
||||
Lit(s) => ast_result.error_spanned_by(s, "unexpected attribute"),
|
||||
_ => ast_result.error_spanned_by(meta_item, "unexpected attribute"),
|
||||
};
|
||||
|
||||
let attr_meta_items_info = variant_attrs
|
||||
.iter()
|
||||
.flat_map(|attr| match get_meta_items(ast_result, attr) {
|
||||
Ok(items) => Some((attr, items)),
|
||||
Err(_) => None,
|
||||
})
|
||||
.collect::<Vec<(&syn::Attribute, Vec<syn::NestedMeta>)>>();
|
||||
|
||||
for (attr, nested_metas) in attr_meta_items_info {
|
||||
nested_metas
|
||||
.iter()
|
||||
.for_each(|meta_item| extract_event_attr(attr, meta_item))
|
||||
}
|
||||
|
||||
// eprintln!("😁{:#?}", event_attrs);
|
||||
event_attrs
|
||||
}
|
||||
|
||||
pub fn get_meta_items(cx: &ASTResult, attr: &syn::Attribute) -> Result<Vec<syn::NestedMeta>, ()> {
|
||||
if attr.path != PB_ATTRS && attr.path != EVENT {
|
||||
pub fn get_pb_meta_items(cx: &ASTResult, attr: &syn::Attribute) -> Result<Vec<syn::NestedMeta>, ()> {
|
||||
// Only handle the attribute that we have defined
|
||||
if attr.path != PB_ATTRS {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
@ -402,7 +273,7 @@ pub fn get_meta_items(cx: &ASTResult, attr: &syn::Attribute) -> Result<Vec<syn::
|
||||
match attr.parse_meta() {
|
||||
Ok(List(meta)) => Ok(meta.nested.into_iter().collect()),
|
||||
Ok(other) => {
|
||||
cx.error_spanned_by(other, "expected #[pb(...)] or or #[event(...)]");
|
||||
cx.error_spanned_by(other, "expected #[pb(...)]");
|
||||
Err(())
|
||||
}
|
||||
Err(err) => {
|
||||
@ -413,7 +284,52 @@ pub fn get_meta_items(cx: &ASTResult, attr: &syn::Attribute) -> Result<Vec<syn::
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_lit_into_expr_path(ast_result: &ASTResult, attr_name: Symbol, lit: &syn::Lit) -> Result<syn::ExprPath, ()> {
|
||||
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 {
|
||||
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(())
|
||||
}
|
||||
Err(err) => {
|
||||
cx.error_spanned_by(attr, "attribute must be str, e.g. #[node(xx = \"xxx\")]");
|
||||
cx.syn_error(err);
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
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 {
|
||||
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 #[event(...)]");
|
||||
Err(())
|
||||
}
|
||||
Err(err) => {
|
||||
cx.error_spanned_by(attr, "attribute must be str, e.g. #[event(xx = \"xxx\")]");
|
||||
cx.syn_error(err);
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_lit_into_expr_path(
|
||||
ast_result: &ASTResult,
|
||||
attr_name: Symbol,
|
||||
lit: &syn::Lit,
|
||||
) -> Result<syn::ExprPath, ()> {
|
||||
let string = get_lit_str(ast_result, attr_name, lit)?;
|
||||
parse_lit_str(string)
|
||||
.map_err(|_| ast_result.error_spanned_by(lit, format!("failed to parse path: {:?}", string.value())))
|
@ -3,23 +3,43 @@ use syn::{Ident, Path};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Symbol(&'static str);
|
||||
pub const PB_ATTRS: Symbol = Symbol("pb");
|
||||
pub const SKIP: Symbol = Symbol("skip"); //#[pb(skip)]
|
||||
pub const PB_INDEX: Symbol = Symbol("index"); //#[pb(index = "1")]
|
||||
pub const PB_ONE_OF: Symbol = Symbol("one_of"); //#[pb(one_of)]
|
||||
pub const DESERIALIZE_WITH: Symbol = Symbol("deserialize_with");
|
||||
pub const SKIP_DESERIALIZING: Symbol = Symbol("skip_deserializing");
|
||||
pub const SERIALIZE_WITH: Symbol = Symbol("serialize_with"); //#[pb(serialize_with = "...")]
|
||||
pub const SKIP_SERIALIZING: Symbol = Symbol("skip_serializing"); //#[pb(skip_serializing)]
|
||||
pub const PB_STRUCT: Symbol = Symbol("struct"); //#[pb(struct="some struct")]
|
||||
pub const PB_ENUM: Symbol = Symbol("enum"); //#[pb(enum="some enum")]
|
||||
|
||||
// Protobuf
|
||||
pub const PB_ATTRS: Symbol = Symbol("pb");
|
||||
//#[pb(skip)]
|
||||
pub const SKIP: Symbol = Symbol("skip");
|
||||
//#[pb(index = "1")]
|
||||
pub const PB_INDEX: Symbol = Symbol("index");
|
||||
//#[pb(one_of)]
|
||||
pub const PB_ONE_OF: Symbol = Symbol("one_of");
|
||||
//#[pb(skip_pb_deserializing = "...")]
|
||||
pub const SKIP_PB_DESERIALIZING: Symbol = Symbol("skip_pb_deserializing");
|
||||
//#[pb(skip_pb_serializing)]
|
||||
pub const SKIP_PB_SERIALIZING: Symbol = Symbol("skip_pb_serializing");
|
||||
//#[pb(serialize_pb_with = "...")]
|
||||
pub const SERIALIZE_PB_WITH: Symbol = Symbol("serialize_pb_with");
|
||||
//#[pb(deserialize_pb_with = "...")]
|
||||
pub const DESERIALIZE_PB_WITH: Symbol = Symbol("deserialize_pb_with");
|
||||
//#[pb(struct="some struct")]
|
||||
pub const PB_STRUCT: Symbol = Symbol("struct");
|
||||
//#[pb(enum="some enum")]
|
||||
pub const PB_ENUM: Symbol = Symbol("enum");
|
||||
|
||||
// Event
|
||||
pub const EVENT_INPUT: Symbol = Symbol("input");
|
||||
pub const EVENT_OUTPUT: Symbol = Symbol("output");
|
||||
pub const EVENT_IGNORE: Symbol = Symbol("ignore");
|
||||
pub const EVENT: Symbol = Symbol("event");
|
||||
pub const EVENT_ERR: Symbol = Symbol("event_err");
|
||||
|
||||
// Node
|
||||
pub const NODE_ATTRS: Symbol = Symbol("node");
|
||||
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");
|
||||
|
||||
impl PartialEq<Symbol> for Ident {
|
||||
fn eq(&self, word: &Symbol) -> bool {
|
||||
self == word.0
|
||||
|
@ -1,4 +1,4 @@
|
||||
use flowy_ast::ASTEnumAttrVariant;
|
||||
use flowy_ast::EventEnumAttrs;
|
||||
|
||||
pub struct EventASTContext {
|
||||
pub event: syn::Ident,
|
||||
@ -10,21 +10,21 @@ pub struct EventASTContext {
|
||||
}
|
||||
|
||||
impl EventASTContext {
|
||||
pub fn from(variant: &ASTEnumAttrVariant) -> EventASTContext {
|
||||
let command_name = variant.enum_item_name.clone();
|
||||
pub fn from(enum_attrs: &EventEnumAttrs) -> EventASTContext {
|
||||
let command_name = enum_attrs.enum_item_name.clone();
|
||||
if command_name.is_empty() {
|
||||
panic!("Invalid command name: {}", variant.enum_item_name);
|
||||
panic!("Invalid command name: {}", enum_attrs.enum_item_name);
|
||||
}
|
||||
|
||||
let event = format_ident!("{}", &command_name);
|
||||
let splits = command_name.split('_').collect::<Vec<&str>>();
|
||||
|
||||
let event_ty = format_ident!("{}", variant.enum_name);
|
||||
let event_ty = format_ident!("{}", enum_attrs.enum_name);
|
||||
let event_request_struct = format_ident!("{}Event", &splits.join(""));
|
||||
|
||||
let event_input = variant.event_input();
|
||||
let event_output = variant.event_output();
|
||||
let event_error = variant.event_error();
|
||||
let event_input = enum_attrs.event_input();
|
||||
let event_output = enum_attrs.event_output();
|
||||
let event_error = enum_attrs.event_error();
|
||||
|
||||
EventASTContext {
|
||||
event,
|
||||
|
@ -130,7 +130,7 @@ pub fn parse_event_crate(event_crate: &DartEventCrate) -> Vec<EventASTContext> {
|
||||
.iter()
|
||||
.filter(|attr| !attr.attrs.event_attrs.ignore)
|
||||
.enumerate()
|
||||
.map(|(_index, attr)| EventASTContext::from(&attr.attrs))
|
||||
.map(|(_index, variant)| EventASTContext::from(&variant.attrs))
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
_ => vec![],
|
||||
|
@ -66,7 +66,7 @@ fn parse_files_protobuf(proto_crate_path: &Path, proto_output_path: &Path) -> Ve
|
||||
|
||||
s.fields
|
||||
.iter()
|
||||
.filter(|field| field.attrs.pb_index().is_some())
|
||||
.filter(|field| field.pb_attrs.pb_index().is_some())
|
||||
.for_each(|field| {
|
||||
ref_types.push(field.ty_as_str());
|
||||
struct_template.set_field(field);
|
||||
@ -121,7 +121,7 @@ pub fn get_ast_structs(ast: &syn::File) -> Vec<Struct> {
|
||||
if let Item::Struct(item_struct) = item {
|
||||
let (_, fields) = struct_from_ast(&ast_result, &item_struct.fields);
|
||||
|
||||
if fields.iter().filter(|f| f.attrs.pb_index().is_some()).count() > 0 {
|
||||
if fields.iter().filter(|f| f.pb_attrs.pb_index().is_some()).count() > 0 {
|
||||
proto_structs.push(Struct {
|
||||
name: item_struct.ident.to_string(),
|
||||
fields,
|
||||
|
@ -36,7 +36,7 @@ impl StructTemplate {
|
||||
pub fn set_field(&mut self, field: &ASTField) {
|
||||
// {{ field_type }} {{ field_name }} = {{index}};
|
||||
let name = field.name().unwrap().to_string();
|
||||
let index = field.attrs.pb_index().unwrap();
|
||||
let index = field.pb_attrs.pb_index().unwrap();
|
||||
|
||||
let ty: &str = &field.ty_as_str();
|
||||
let mut mapped_ty: &str = ty;
|
||||
|
@ -9,6 +9,7 @@ use syn::{parse_macro_input, DeriveInput};
|
||||
extern crate quote;
|
||||
|
||||
mod dart_event;
|
||||
mod node;
|
||||
mod proto_buf;
|
||||
|
||||
// Inspired by https://serde.rs/attributes.html
|
||||
@ -36,6 +37,12 @@ pub fn derive_dart_event(input: TokenStream) -> TokenStream {
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Node, attributes(node))]
|
||||
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()
|
||||
}
|
||||
|
||||
fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
|
||||
let compile_errors = errors.iter().map(syn::Error::to_compile_error);
|
||||
quote!(#(#compile_errors)*)
|
||||
|
14
shared-lib/flowy-derive/src/node/mod.rs
Normal file
14
shared-lib/flowy-derive/src/node/mod.rs
Normal file
@ -0,0 +1,14 @@
|
||||
use flowy_ast::{ASTContainer, 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 mut token_stream: TokenStream = TokenStream::default();
|
||||
ast_result.check()?;
|
||||
Ok(token_stream)
|
||||
}
|
@ -9,12 +9,12 @@ pub fn make_de_token_steam(ctxt: &ASTResult, ast: &ASTContainer) -> Option<Token
|
||||
let build_take_fields = ast
|
||||
.data
|
||||
.all_fields()
|
||||
.filter(|f| !f.attrs.skip_deserializing())
|
||||
.filter(|f| !f.pb_attrs.skip_pb_deserializing())
|
||||
.flat_map(|field| {
|
||||
if let Some(func) = field.attrs.deserialize_with() {
|
||||
if let Some(func) = field.pb_attrs.deserialize_pb_with() {
|
||||
let member = &field.member;
|
||||
Some(quote! { o.#member=#struct_ident::#func(pb); })
|
||||
} else if field.attrs.is_one_of() {
|
||||
} else if field.pb_attrs.is_one_of() {
|
||||
token_stream_for_one_of(ctxt, field)
|
||||
} else {
|
||||
token_stream_for_field(ctxt, &field.member, field.ty, false)
|
||||
|
@ -10,7 +10,7 @@ pub fn make_se_token_stream(ast_result: &ASTResult, ast: &ASTContainer) -> Optio
|
||||
let build_set_pb_fields = ast
|
||||
.data
|
||||
.all_fields()
|
||||
.filter(|f| !f.attrs.skip_serializing())
|
||||
.filter(|f| !f.pb_attrs.skip_pb_serializing())
|
||||
.flat_map(|field| se_token_stream_for_field(ast_result, field, false));
|
||||
|
||||
let se_token_stream: TokenStream = quote! {
|
||||
@ -38,10 +38,10 @@ pub fn make_se_token_stream(ast_result: &ASTResult, ast: &ASTContainer) -> Optio
|
||||
}
|
||||
|
||||
fn se_token_stream_for_field(ast_result: &ASTResult, field: &ASTField, _take: bool) -> Option<TokenStream> {
|
||||
if let Some(func) = &field.attrs.serialize_with() {
|
||||
if let Some(func) = &field.pb_attrs.serialize_pb_with() {
|
||||
let member = &field.member;
|
||||
Some(quote! { pb.#member=o.#func(); })
|
||||
} else if field.attrs.is_one_of() {
|
||||
} else if field.pb_attrs.is_one_of() {
|
||||
token_stream_for_one_of(ast_result, field)
|
||||
} else {
|
||||
gen_token_stream(ast_result, &field.member, field.ty, false)
|
||||
|
@ -60,8 +60,8 @@ impl AppNode {
|
||||
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 set_name(&self, name: String) -> CollaborateResult<()> {
|
||||
set_attributes_str_value(self.tree.clone(), &self.path, "name", name)
|
||||
}
|
||||
|
||||
fn get_workspace_id(&self) -> Option<String> {
|
||||
|
@ -11,8 +11,45 @@ use std::sync::Arc;
|
||||
|
||||
pub type AtomicNodeTree = RwLock<NodeTree>;
|
||||
|
||||
// pub struct FolderNodePad2 {
|
||||
// tree: Arc<AtomicNodeTree>,
|
||||
//
|
||||
// #[node(rename = "workspaces", revision = "WorkspaceRevision")]
|
||||
// workspaces: Vec<Arc<WorkspaceNode>>,
|
||||
// }
|
||||
//
|
||||
// impl FolderNodePad2 {
|
||||
// pub fn get_workspace() {}
|
||||
// pub fn get_mut_workspace() {}
|
||||
// pub fn add_workspace() {}
|
||||
// pub fn remove_workspace() {}
|
||||
// pub fn to_json() {}
|
||||
// }
|
||||
//
|
||||
// #[derive(Debug, Clone)]
|
||||
// pub struct WorkspaceNode2 {
|
||||
// tree: Arc<AtomicNodeTree>,
|
||||
// pub id: String,
|
||||
// pub name: String,
|
||||
// pub path: Path,
|
||||
// }
|
||||
//
|
||||
// impl WorkspaceNode2 {
|
||||
// pub fn get_id() {}
|
||||
// pub fn set_id() {}
|
||||
// pub fn get_name() {}
|
||||
// pub fn set_name() {}
|
||||
// pub fn get_apps() {}
|
||||
//
|
||||
// pub fn get_app() {}
|
||||
// pub fn get_mut_app() {}
|
||||
// pub fn add_app() {}
|
||||
// pub fn remove_app() {}
|
||||
// }
|
||||
|
||||
pub struct FolderNodePad {
|
||||
tree: Arc<AtomicNodeTree>,
|
||||
// name: workspaces, index of the node,
|
||||
workspaces: Vec<Arc<WorkspaceNode>>,
|
||||
trash: Vec<Arc<TrashNode>>,
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ mod folder_node;
|
||||
mod folder_pad;
|
||||
mod view_node;
|
||||
mod workspace_node;
|
||||
mod workspace_node_2;
|
||||
|
||||
pub use folder_node::*;
|
||||
pub use folder_pad::*;
|
||||
|
12
shared-lib/flowy-sync/src/client_folder/workspace_node_2.rs
Normal file
12
shared-lib/flowy-sync/src/client_folder/workspace_node_2.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use crate::client_folder::AtomicNodeTree;
|
||||
use flowy_derive::Node;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Clone, Node)]
|
||||
pub struct WorkspaceNode2 {
|
||||
tree: Arc<AtomicNodeTree>,
|
||||
#[node]
|
||||
pub id: String,
|
||||
// pub name: String,
|
||||
// pub path: Path,
|
||||
}
|
@ -5,6 +5,7 @@ use std::sync::Arc;
|
||||
pub enum FolderNodePadScript {
|
||||
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 },
|
||||
@ -58,7 +59,12 @@ impl FolderNodePadTest {
|
||||
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();
|
||||
}
|
||||
FolderNodePadScript::AssertApp { id, expected } => {
|
||||
let workspace_node = self.folder_pad.get_workspace("1").unwrap();
|
||||
let app = workspace_node.get_app(&id);
|
||||
|
@ -32,3 +32,22 @@ fn client_folder_delete_app_test() {
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn client_folder_update_app_test() {
|
||||
let mut test = FolderNodePadTest::new();
|
||||
test.run_scripts(vec![
|
||||
CreateApp {
|
||||
id: "1".to_string(),
|
||||
name: "my first app".to_string(),
|
||||
},
|
||||
UpdateApp {
|
||||
id: "1".to_string(),
|
||||
name: "TODO".to_string(),
|
||||
},
|
||||
AssertAppContent {
|
||||
id: "1".to_string(),
|
||||
name: "TODO".to_string(),
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user