Derive TypeTagged correctly

This commit is contained in:
Andrey Tkachenko 2021-08-10 14:23:33 +04:00
parent ba67c8251d
commit 6d8dd039b3
3 changed files with 103 additions and 163 deletions

View File

@ -1,12 +1,13 @@
#![recursion_limit="128"]
#![recursion_limit = "128"]
extern crate proc_macro;
use proc_macro::TokenStream;
use syn::{LitStr, Result, parenthesized};
use syn::{DeriveInput, punctuated::Punctuated, token::Comma};
use syn::parse::{Parse, ParseStream};
use quote::quote;
use std::fmt::Write;
use syn::parse::{Parse, ParseStream};
use syn::{parenthesized, LitStr, Result};
use syn::{punctuated::Punctuated, token::Comma, DeriveInput};
fn shared_part(_ast: &syn::DeriveInput, has_shared: bool) -> proc_macro2::TokenStream {
if has_shared {
@ -38,7 +39,7 @@ fn clone_part(ast: &syn::DeriveInput, has_clone: bool) -> proc_macro2::TokenStre
} else {
return false;
};
into.replace(core::clone::Clone::clone(self));
true
}
@ -55,35 +56,74 @@ fn clone_part(ast: &syn::DeriveInput, has_clone: bool) -> proc_macro2::TokenStre
}
fn type_tag_part(ast: &syn::DeriveInput, type_tag: Option<LitStr>) -> proc_macro2::TokenStream {
let name = &ast.ident;
let class_name = &ast.ident;
let name = if let Some(tt) = type_tag {
tt.value()
} else {
class_name.to_string()
};
let (_, ty_generics, where_clause) = ast.generics.split_for_impl();
let mut impl_generics = ast.generics.clone();
for mut param in impl_generics.params.pairs_mut() {
let mut type_name = String::new();
let mut type_values = String::from("(");
let mut need_close = false;
write!(&mut type_name, "{}", name).unwrap();
for mut param in impl_generics.params.pairs_mut() {
match &mut param.value_mut() {
syn::GenericParam::Lifetime(_) => continue,
syn::GenericParam::Type(param) => {
if !need_close {
type_name.push('<');
need_close = true;
} else {
type_name.push(',');
type_values.push(',');
}
type_name.push_str("{}");
write!(
&mut type_values,
"<{} as messagebus::TypeTagged>::type_tag_()",
param.ident
)
.unwrap();
let bound: syn::TypeParamBound = syn::parse_str("messagebus::TypeTagged").unwrap();
param.bounds.push(bound);
}
syn::GenericParam::Const(_) => {}
syn::GenericParam::Const(_param) => {
unimplemented!()
}
}
}
if let Some(type_tag) = type_tag {
if need_close {
type_name.push('>');
}
if type_values.len() > 1 {
type_values.push_str(",)");
let type_values: syn::ExprTuple = syn::parse_str(&type_values).unwrap();
let type_values = type_values.elems;
quote! {
impl #impl_generics messagebus::TypeTagged for #name #ty_generics #where_clause {
fn type_tag_() -> messagebus::TypeTag { #type_tag.into() }
fn type_tag(&self) -> messagebus::TypeTag { #type_tag.into() }
fn type_name(&self) -> std::borrow::Cow<str> { #type_tag.into() }
impl #impl_generics messagebus::TypeTagged for #class_name #ty_generics #where_clause {
fn type_tag_() -> messagebus::TypeTag { format!(#type_name, #type_values).into() }
fn type_tag(&self) -> messagebus::TypeTag { Self::type_tag_() }
fn type_name(&self) -> std::borrow::Cow<str> { Self::type_tag_() }
}
}
} else {
quote! {
impl #impl_generics messagebus::TypeTagged for #name #ty_generics #where_clause {
fn type_tag_() -> messagebus::TypeTag { std::any::type_name::<Self>().into() }
fn type_tag(&self) -> messagebus::TypeTag { std::any::type_name::<Self>().into() }
fn type_name(&self) -> std::borrow::Cow<str> { std::any::type_name::<Self>().into() }
impl #impl_generics messagebus::TypeTagged for #class_name #ty_generics #where_clause {
fn type_tag_() -> messagebus::TypeTag { #type_name.into() }
fn type_tag(&self) -> messagebus::TypeTag { Self::type_tag_() }
fn type_name(&self) -> std::borrow::Cow<str> { Self::type_tag_() }
}
}
}
@ -103,9 +143,11 @@ impl Parse for TypeTag {
for pair in punctuated.pairs() {
inner = Some(pair.into_value());
break;
}
}
Ok(TypeTag { inner: inner.unwrap().to_owned() })
Ok(TypeTag {
inner: inner.unwrap().to_owned(),
})
}
}
@ -126,7 +168,7 @@ impl Parse for Tags {
fn parse(input: ParseStream) -> Result<Self> {
let mut has_shared = false;
let mut has_clone = false;
let content;
parenthesized!(content in input);
let punctuated = Punctuated::<syn::Ident, Comma>::parse_terminated(&content)?;
@ -136,9 +178,9 @@ impl Parse for Tags {
match val.as_str() {
"shared" => has_shared = true,
"clone" => has_clone = true,
_ => ()
_ => (),
}
}
}
Ok(Tags {
has_clone,
@ -168,7 +210,7 @@ pub fn derive_message(input: TokenStream) -> TokenStream {
type_tag = Some(tt.inner);
}
_ => ()
_ => (),
}
}
}
@ -206,7 +248,7 @@ pub fn derive_error(input: TokenStream) -> TokenStream {
type_tag = Some(tt.inner);
}
_ => ()
_ => (),
}
}
}
@ -217,4 +259,4 @@ pub fn derive_error(input: TokenStream) -> TokenStream {
};
tokens.into()
}
}

36
tests/test_derive.rs Normal file
View File

@ -0,0 +1,36 @@
use messagebus::{
derive::{Error as MbError, Message},
error, Message, MessageBounds, TypeTagged,
};
use thiserror::Error;
#[derive(Debug, Error, MbError)]
enum Error {
#[error("Error({0})")]
Error(anyhow::Error),
}
impl<M: Message> From<error::Error<M>> for Error {
fn from(err: error::Error<M>) -> Self {
Self::Error(err.into())
}
}
#[derive(Debug, Clone, Message)]
#[type_tag("api::Msg")]
pub struct Msg<F: MessageBounds + Clone>(pub F);
#[derive(Debug, Clone, Message)]
#[type_tag("api::Query")]
pub struct Qqq<F: MessageBounds + Clone, G: MessageBounds + Clone, H: MessageBounds + Clone>(
pub F,
pub G,
pub H,
);
fn main() {
assert_eq!(
Qqq::<Msg<i32>, Msg<()>, u64>::type_tag_(),
"api::Query<api::Msg<i32>,api::Msg<()>,u64>"
);
}

View File

@ -11,144 +11,6 @@ use parking_lot::Mutex;
use thiserror::Error;
use tokio::sync::mpsc;
// macro_rules! type_map {
// (inbound : $($tt:tt)*) => {{
// let mut type_map = TypeMap::new();
// type_map!(@inbound type_map, $($tt)*);
// type_map
// }};
// (outbound : $($tt:tt)*) => {
// let mut type_map = TypeMap::new();
// type_map!(@outbound type_map, $($tt)*);
// type_map
// };
// (@inbound $tm: ident, $msg:ty => $resp: ty, $($tt:tt)*) => {
// $tm.add_inbound(<$msg as TypeTagged>::type_tag_(), <$resp as TypeTagged>::type_tag_(), None);
// type_map!(@inbound $tm, $($tt)*)
// };
// (@inbound $tm: ident, $msg:ty => ($resp:ty) throws $err: ty, $($tt:tt)*) => {
// $tm.add_inbound(<$msg as messagebus::TypeTagged>::type_tag_(), <$resp as messagebus:TypeTagged>::type_tag_(), Some(<$err as messagebus::TypeTagged>::type_tag_()));
// type_map!(@inbound $tm, $($tt)*)
// };
// (@inbound $tm: ident, $msg:ty => $resp: ty) => {
// $tm.add_inbound(<$msg as TypeTagged>::type_tag_(), <$resp as TypeTagged>::type_tag_(), None);
// };
// (@inbound $tm: ident, outbound : $($tt:tt)*) => {
// type_map!(@outbound $tm, $($tt)*)
// };
// (@inbound $tm: ident,) => {};
// (@outbound $tm: ident, $msg:ty => ($resp:ty) throws $err: ty, $($tt:tt)*) => {
// $tm.add_outbound(<$msg as TypeTagged>::type_tag_(), <$resp as TypeTagged>::type_tag_(), Some(<$err as TypeTagged>::type_tag_()));
// type_map!(@outbound $tm, $($tt)*)
// };
// (@outbound $tm: ident, $msg:ty => $resp: ty, $($tt:tt)*) => {
// $tm.add_outbound(<$msg as TypeTagged>::type_tag_(), <$resp as TypeTagged>::type_tag_(), None);
// type_map!(@outbound $tm, $($tt)*)
// };
// (@outbound $tm: ident, $msg:ty => $resp: ty) => {
// $tm.add_outbound(<$msg as TypeTagged>::type_tag_(), <$resp as TypeTagged>::type_tag_(), None);
// };
// (@outbound $tm: ident, inbound : $($tt:tt)*) => {
// type_map!(@inbound $tm, $($tt)*)
// };
// (@outbound $tm: ident,) => {};
// }
// #[derive(Debug, Clone)]
// pub struct TypeMap {
// inbound: HashMap<TypeTag, Vec<(TypeTag, Option<TypeTag>)>>,
// outbound: HashMap<TypeTag, Vec<(TypeTag, Option<TypeTag>)>>,
// }
// impl TypeMap {
// pub fn new() -> Self {
// Self {
// inbound: Default::default(),
// outbound: Default::default(),
// }
// }
// pub fn add_inbound(&mut self, msg: TypeTag, resp: TypeTag, err: Option<TypeTag>) -> &mut Self {
// let vec = self.inbound.entry(msg)
// .or_insert_with(Vec::new);
// vec.push((resp, err));
// self
// }
// pub fn add_outbound(&mut self, msg: TypeTag, resp: TypeTag, err: Option<TypeTag>) -> &mut Self {
// let vec = self.outbound.entry(msg)
// .or_insert_with(Vec::new);
// vec.push((resp, err));
// self
// }
// pub fn accept_inbound(&self, msg: &TypeTag, resp: Option<&TypeTag>, err: Option<&TypeTag>) -> bool {
// if let Some(vec) = self.inbound.get(msg) {
// if let Some(rr) = resp {
// vec.iter().find(|(r, e)| {
// let ee = if let Some(e) = e {
// if let Some(te) = err {
// te.as_ref() == e.as_ref()
// } else {
// true
// }
// } else {
// err.is_none()
// };
// r.as_ref() == rr.as_ref() && ee
// }).is_some()
// } else {
// true
// }
// } else {
// false
// }
// }
// pub fn accept_outbound(&self, msg: &TypeTag, resp: Option<&TypeTag>, err: Option<&TypeTag>) -> bool {
// if let Some(vec) = self.outbound.get(msg) {
// if let Some(rr) = resp {
// vec.iter().find(|(r, e)| {
// let ee = if let Some(e) = e {
// if let Some(te) = err {
// te.as_ref() == e.as_ref()
// } else {
// true
// }
// } else {
// err.is_none()
// };
// r.as_ref() == rr.as_ref() && ee
// }).is_some()
// } else {
// true
// }
// } else {
// false
// }
// }
// #[inline]
// pub fn inbound_iter(&self) -> impl Iterator<Item = (&TypeTag, &TypeTag, Option<&TypeTag>)> {
// self.inbound.iter().map(|(k, v)| v.into_iter().map(move |(r, e)|(k, r, e.as_ref()))).flatten()
// }
// }
#[derive(Debug, Error, MbError)]
enum Error {
#[error("Error({0})")]