chore: add node ast parser

This commit is contained in:
nathan 2022-12-03 12:03:52 +08:00
parent c89277d507
commit eb1659fd3e
20 changed files with 506 additions and 234 deletions

View File

@ -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()
}

View 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
}

View File

@ -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::*;

View 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()
}
}

View File

@ -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())))

View File

@ -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

View File

@ -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,

View File

@ -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![],

View File

@ -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,

View File

@ -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;

View File

@ -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)*)

View 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)
}

View File

@ -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)

View File

@ -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)

View File

@ -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> {

View File

@ -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>>,
}

View File

@ -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::*;

View 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,
}

View File

@ -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);

View File

@ -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(),
},
]);
}