Derive
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Andrey Tkachenko 2023-03-28 21:10:35 +04:00
parent 1a57aa0a18
commit da1e0c9163
7 changed files with 444 additions and 173 deletions

View File

@ -11,7 +11,7 @@ license = "MIT OR Apache-2.0"
edition = "2021"
[dependencies]
messagebus_derive = "0.2.5"
messagebus_derive = { path = "./crates/derive" }
anyhow = "1.0"
thiserror = "1.0"

19
crates/derive/Cargo.toml Normal file
View File

@ -0,0 +1,19 @@
[package]
name = "messagebus_derive"
version = "0.2.5"
authors = ["Andrey Tkachenko <andrey@aidev.ru>"]
repository = "https://github.com/andreytkachenko/messagebus.git"
keywords = ["futures", "async", "tokio", "message", "bus"]
categories = ["network-programming", "asynchronous"]
description = "MessageBus allows intercommunicate with messages between modules"
license = "MIT OR Apache-2.0"
exclude = [".gitignore", ".cargo/config", ".github/**", "codecov.yml"]
edition = "2018"
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1"
quote = "1"
syn = {version = "1", features = ["full"]}

328
crates/derive/src/lib.rs Normal file
View File

@ -0,0 +1,328 @@
#![recursion_limit = "128"]
extern crate proc_macro;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use std::collections::hash_map;
use std::fmt::Write;
use std::hash::Hasher;
use syn::parse::{Parse, ParseStream};
use syn::{parenthesized, Result};
use syn::{punctuated::Punctuated, token::Comma, DeriveInput};
fn shared_part(_ast: &syn::DeriveInput, has_shared: bool) -> proc_macro2::TokenStream {
if has_shared {
quote! {
fn as_shared_ref(&self) -> std::option::Option<&dyn messagebus::SharedMessage> {Some(self)}
fn as_shared_mut(&mut self) -> std::option::Option<&mut dyn messagebus::SharedMessage>{Some(self)}
fn as_shared_boxed(self: Box<Self>) -> Result<Box<dyn messagebus::SharedMessage>, Box<dyn messagebus::Message>> {Ok(self)}
fn as_shared_arc(self: std::sync::Arc<Self>) -> Option<std::sync::Arc<dyn messagebus::SharedMessage>>{Some(self)}
}
} else {
quote! {
fn as_shared_ref(&self) -> std::option::Option<&dyn messagebus::SharedMessage> {None}
fn as_shared_mut(&mut self) -> std::option::Option<&mut dyn messagebus::SharedMessage> {None}
fn as_shared_boxed(self: Box<Self>) -> Result<Box<dyn messagebus::SharedMessage>, Box<dyn messagebus::Message>> {Err(self)}
fn as_shared_arc(self: std::sync::Arc<Self>) -> Option<std::sync::Arc<dyn messagebus::SharedMessage>> {None}
}
}
}
fn clone_part(_ast: &syn::DeriveInput, has_clone: bool) -> proc_macro2::TokenStream {
if has_clone {
quote! {
fn try_clone_into(&self, into: &mut dyn messagebus::cell::MessageCell) -> bool {
into.into_typed::<Self>()
.map(|c| c.put(self.clone()))
.is_ok()
}
fn try_clone_boxed(&self) -> std::option::Option<std::boxed::Box<dyn messagebus::Message>>{
Some(Box::new(self.clone()))
}
fn try_clone(&self) -> Option<Self> {
Some(core::clone::Clone::clone(self))
}
fn is_cloneable(&self) -> bool { true }
}
} else {
quote! {
fn try_clone_into(&self, into: &mut dyn messagebus::cell::MessageCell) -> bool {false}
fn try_clone_boxed(&self) -> std::option::Option<std::boxed::Box<dyn messagebus::Message>>{ None }
fn try_clone(&self) -> Option<Self> { None }
fn is_cloneable(&self) -> bool { false }
}
}
}
fn type_tag_part(
ast: &syn::DeriveInput,
type_tag: Option<String>,
namespace: Option<String>,
) -> proc_macro2::TokenStream {
let class_name = &ast.ident;
let name = if let Some(tt) = type_tag {
tt
} else if let Some(ns) = namespace {
format!("{}::{}", ns, class_name)
} else {
class_name.to_string()
};
let (_, ty_generics, where_clause) = ast.generics.split_for_impl();
let mut impl_generics = ast.generics.clone();
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::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);
}
_ => unimplemented!(),
}
}
if need_close {
type_name.push('>');
}
let const_name = syn::Ident::new(
&format!("DERIVE_TYPE_TAG_{}", class_name.to_string().to_uppercase()),
Span::call_site(),
);
let type_tag = 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! { format!(#type_name, #type_values) }
} else {
quote! { #type_name }
};
quote! {
messagebus::__derive_private::lazy_static::lazy_static! {
static ref #const_name: messagebus::TypeTag = messagebus::TypeTagInfo::parse(#type_tag).unwrap().into();
}
impl #impl_generics messagebus::TypeTagged for #class_name #ty_generics #where_clause {
fn TYPE_TAG() -> messagebus::TypeTag { #const_name.clone() }
fn type_tag(&self) -> messagebus::TypeTag { Self::TYPE_TAG() }
fn type_layout(&self) -> std::alloc::Layout { std::alloc::Layout::for_value(self) }
}
}
}
struct TypeTag {
pub inner: syn::LitStr,
}
impl Parse for TypeTag {
fn parse(input: ParseStream) -> Result<Self> {
let content;
parenthesized!(content in input);
let punctuated = Punctuated::<syn::LitStr, Comma>::parse_terminated(&content)?;
let inner = punctuated.pairs().map(|x| x.into_value()).next();
Ok(TypeTag {
inner: inner.unwrap().to_owned(),
})
}
}
#[derive(Default, Debug)]
struct Tags {
has_clone: bool,
has_shared: bool,
}
impl Tags {
pub fn add(&mut self, other: Tags) {
self.has_clone = self.has_clone || other.has_clone;
self.has_shared = self.has_shared || other.has_shared;
}
}
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)?;
for pair in punctuated.pairs() {
let val = pair.into_value().to_string();
match val.as_str() {
"shared" => has_shared = true,
"clone" => has_clone = true,
_ => (),
}
}
Ok(Tags {
has_clone,
has_shared,
})
}
}
#[proc_macro_derive(Message, attributes(type_tag, message, namespace))]
pub fn derive_message(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let mut tags = Tags::default();
let mut type_tag = None;
let mut namespace = None;
let ast: DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
let (_, ty_generics, where_clause) = ast.generics.split_for_impl();
for attr in &ast.attrs {
if let Some(i) = attr.path.get_ident() {
match i.to_string().as_str() {
"message" => {
let tt: Tags = syn::parse2(attr.tokens.clone()).unwrap();
tags.add(tt);
}
"type_tag" => {
let tt: TypeTag = syn::parse2(attr.tokens.clone()).unwrap();
type_tag = Some(tt.inner.value());
}
"namespace" => {
let tt: TypeTag = syn::parse2(attr.tokens.clone()).unwrap();
namespace = Some(tt.inner.value());
}
_ => (),
}
}
}
let mut impl_generics = ast.generics.clone();
for mut param in impl_generics.params.pairs_mut() {
match &mut param.value_mut() {
syn::GenericParam::Type(params) => {
let bound: syn::TypeParamBound = syn::parse_str("messagebus::TypeTagged").unwrap();
params.bounds.push(bound);
}
_ => {}
}
}
let type_tag_part = type_tag_part(&ast, type_tag, namespace);
let shared_part = shared_part(&ast, tags.has_shared);
let clone_part = clone_part(&ast, tags.has_clone);
// let init = Ident::new(
// &format!("__init_{}", hash(ast.clone().into_token_stream())),
// Span::call_site(),
// );
// let init_impl = if tags.has_shared && impl_generics.params.is_empty() {
// quote! {
// #[allow(non_upper_case_globals)]
// #[messagebus::ctor::ctor]
// fn #init() {
// messagebus::register_shared_message::<#name>();
// }
// }
// } else {
// quote! {}
// };
if !impl_generics.params.is_empty() && tags.has_shared {
impl_generics
.params
.push(syn::GenericParam::Lifetime(syn::LifetimeDef::new(
syn::Lifetime::new("'de", Span::call_site()),
)))
}
let tokens = quote! {
#type_tag_part
impl #impl_generics messagebus::Message for #name #ty_generics #where_clause {
fn as_any_ref(&self) -> &dyn core::any::Any {self}
fn as_any_mut(&mut self) -> &mut dyn core::any::Any {self}
fn as_any_boxed(self: std::boxed::Box<Self>) -> std::boxed::Box<dyn core::any::Any> {self}
fn as_any_arc(self: std::sync::Arc<Self>) -> std::sync::Arc<dyn core::any::Any> {self}
#shared_part
#clone_part
}
};
// #init_impl
tokens.into()
}
#[proc_macro_derive(TypeTagged, attributes(type_tag, namespace))]
pub fn derive_typetagged(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let mut type_tag = None;
let mut namespace = None;
let ast: DeriveInput = syn::parse(input).unwrap();
for attr in &ast.attrs {
if let Some(i) = attr.path.get_ident() {
match i.to_string().as_str() {
"type_tag" => {
let tt: TypeTag = syn::parse2(attr.tokens.clone()).unwrap();
type_tag = Some(tt.inner.value());
}
"namespace" => {
let tt: TypeTag = syn::parse2(attr.tokens.clone()).unwrap();
namespace = Some(tt.inner.value());
}
_ => (),
}
}
}
let type_tag_part = type_tag_part(&ast, type_tag, namespace);
let tokens = quote! {
#type_tag_part
};
tokens.into()
}
fn hash(input: TokenStream) -> u64 {
let mut hasher = hash_map::DefaultHasher::new();
hasher.write(input.to_string().as_bytes());
hasher.finish()
}

View File

@ -6,22 +6,21 @@ use futures::Future;
use messagebus::{
bus::{Bus, MaskMatch},
cell::MsgCell,
derive_message_clone,
derive::Message,
error::Error,
handler::Handler,
receivers::wrapper::HandlerWrapper,
};
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Message)]
struct Msg(pub u32);
derive_message_clone!(EXAMPLE_MSG, Msg, "example::Msg");
struct Test {
inner: u32,
}
impl Handler<Msg> for Test {
type Response = Msg;
type Response = ();
type HandleFuture<'a> = impl Future<Output = Result<Self::Response, Error>> + 'a;
type FlushFuture<'a> = impl Future<Output = Result<(), Error>> + 'a;
type CloseFuture<'a> = impl Future<Output = Result<(), Error>> + 'a;
@ -31,8 +30,8 @@ impl Handler<Msg> for Test {
async move {
println!("msg {msg:?}");
let x = self.inner;
Ok(Msg(x + msg.0))
// let x = self.inner;
Ok(())
}
}
@ -51,7 +50,7 @@ async fn run() -> Result<(), Error> {
let wrapper = HandlerWrapper::new(Arc::new(Test { inner: 12 }));
bus.register(wrapper, MaskMatch::all());
let res: Msg = bus.request(Msg(13)).await?.result().await?;
let res: () = bus.request(Msg(13)).await?.result().await?;
println!("request result got {:?}", res);
bus.send(Msg(12)).await?;

View File

@ -5,6 +5,7 @@ pub mod cell;
pub mod error;
pub mod handler;
pub mod message;
pub mod message_impls;
pub mod polling_pool;
pub mod receiver;
pub mod receivers;
@ -15,172 +16,14 @@ mod utils;
pub use bus::Bus;
pub use handler::*;
pub use message::*;
pub use message_impls::*;
pub use task::*;
pub use type_tag::{TypeTag, TypeTagInfo};
#[macro_export]
macro_rules! derive_message_clone {
($const_name: ident, $struct_name: ty, $name: literal) => {
lazy_static::lazy_static! {
static ref $const_name: $crate::type_tag::TypeTag = $crate::type_tag::TypeTagInfo::parse($name).unwrap().into();
}
impl $crate::Message for $struct_name {
#[allow(non_snake_case)]
fn TYPE_TAG() -> $crate::type_tag::TypeTag
where
Self: Sized,
{
$const_name.clone()
}
fn type_tag(&self) -> $crate::type_tag::TypeTag {
$const_name.clone()
}
fn type_layout(&self) -> std::alloc::Layout {
std::alloc::Layout::new::<Self>()
}
fn as_any_ref(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn as_any_boxed(self: Box<Self>) -> Box<dyn std::any::Any> {
self
}
fn as_any_arc(self: std::sync::Arc<Self>) -> std::sync::Arc<dyn std::any::Any> {
self
}
fn as_shared_ref(&self) -> Option<&dyn $crate::message::SharedMessage> {
None
}
fn as_shared_mut(&mut self) -> Option<&mut dyn $crate::message::SharedMessage> {
None
}
fn as_shared_boxed(
self: Box<Self>,
) -> Result<Box<dyn $crate::message::SharedMessage>, Box<dyn $crate::Message>> {
Err(self)
}
fn as_shared_arc(
self: std::sync::Arc<Self>,
) -> Option<std::sync::Arc<dyn $crate::message::SharedMessage>> {
None
}
fn try_clone_into(&self, into: &mut dyn $crate::cell::MessageCell) -> bool {
into.into_typed::<Self>()
.map(|c| c.put(self.clone()))
.is_ok()
}
fn try_clone_boxed(&self) -> Option<Box<dyn $crate::Message>> {
Some(Box::new(self.clone()))
}
fn is_cloneable(&self) -> bool {
true
}
fn try_clone(&self) -> Option<Self>
where
Self: Sized,
{
Some(self.clone())
}
}
};
pub mod derive {
pub use messagebus_derive::*;
}
#[macro_export]
macro_rules! derive_message{
($const_name: ident, $struct_name: ty, $name: literal) => {
lazy_static::lazy_static! {
static ref $const_name: $crate::type_tag::TypeTag = $crate::type_tag::TypeTagInfo::parse($name).unwrap().into();
}
impl $crate::Message for $struct_name {
#[allow(non_snake_case)]
fn TYPE_TAG() -> $crate::type_tag::TypeTag
where
Self: Sized,
{
$const_name.clone()
}
fn type_tag(&self) -> $crate::type_tag::TypeTag {
$const_name.clone()
}
fn type_layout(&self) -> std::alloc::Layout {
std::alloc::Layout::new::<Self>()
}
fn as_any_ref(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn as_any_boxed(self: Box<Self>) -> Box<dyn std::any::Any> {
self
}
fn as_any_arc(self: std::sync::Arc<Self>) -> std::sync::Arc<dyn std::any::Any> {
self
}
fn as_shared_ref(&self) -> Option<&dyn $crate::message::SharedMessage> {
None
}
fn as_shared_mut(&mut self) -> Option<&mut dyn $crate::message::SharedMessage> {
None
}
fn as_shared_boxed(
self: Box<Self>,
) -> Result<Box<dyn $crate::message::SharedMessage>, Box<dyn $crate::Message>> {
Err(self)
}
fn as_shared_arc(
self: std::sync::Arc<Self>,
) -> Option<std::sync::Arc<dyn $crate::message::SharedMessage>> {
None
}
fn try_clone_into(&self, _into: &mut dyn $crate::cell::MessageCell) -> bool {
false
}
fn try_clone_boxed(&self) -> Option<Box<dyn $crate::Message>> {
None
}
fn is_cloneable(&self) -> bool {
false
}
fn try_clone(&self) -> Option<Self>
where
Self: Sized,
{
None
}
}
};
pub mod __derive_private {
pub use lazy_static;
}
derive_message_clone!(VOID, (), "void");

View File

@ -3,7 +3,7 @@ use std::{alloc::Layout, any::Any, sync::Arc};
use crate::{cell::MessageCell, type_tag::TypeTag};
pub trait Message: fmt::Debug + Unpin + Send + Sync + 'static {
pub trait TypeTagged: fmt::Debug {
#[allow(non_snake_case)]
fn TYPE_TAG() -> TypeTag
where
@ -11,7 +11,9 @@ pub trait Message: fmt::Debug + Unpin + Send + Sync + 'static {
fn type_tag(&self) -> TypeTag;
fn type_layout(&self) -> Layout;
}
pub trait Message: TypeTagged + Unpin + Send + Sync + 'static {
fn as_any_ref(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn as_any_boxed(self: Box<Self>) -> Box<dyn Any>;

80
src/message_impls.rs Normal file
View File

@ -0,0 +1,80 @@
use crate::{Message, TypeTag, TypeTagInfo, TypeTagged};
use lazy_static::lazy_static;
lazy_static! {
static ref DERIVE_VOID: TypeTag = TypeTagInfo::parse("void").unwrap().into();
}
impl TypeTagged for () {
fn TYPE_TAG() -> crate::TypeTag
where
Self: Sized,
{
DERIVE_VOID.clone()
}
fn type_tag(&self) -> crate::TypeTag {
Self::TYPE_TAG()
}
fn type_layout(&self) -> std::alloc::Layout {
std::alloc::Layout::for_value(self)
}
}
impl Message for () {
fn as_any_ref(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn as_any_boxed(self: Box<Self>) -> Box<dyn std::any::Any> {
self
}
fn as_any_arc(self: std::sync::Arc<Self>) -> std::sync::Arc<dyn std::any::Any> {
self
}
fn as_shared_ref(&self) -> Option<&dyn crate::SharedMessage> {
Some(self)
}
fn as_shared_mut(&mut self) -> Option<&mut dyn crate::SharedMessage> {
Some(self)
}
fn as_shared_boxed(self: Box<Self>) -> Result<Box<dyn crate::SharedMessage>, Box<dyn Message>> {
Ok(self)
}
fn as_shared_arc(
self: std::sync::Arc<Self>,
) -> Option<std::sync::Arc<dyn crate::SharedMessage>> {
Some(self)
}
fn try_clone_into(&self, into: &mut dyn crate::cell::MessageCell) -> bool {
into.into_typed::<Self>()
.map(|c| c.put(self.clone()))
.is_ok()
}
fn try_clone_boxed(&self) -> Option<Box<dyn Message>> {
Some(Box::new(self.clone()))
}
fn is_cloneable(&self) -> bool {
true
}
fn try_clone(&self) -> Option<Self>
where
Self: Sized,
{
Some(())
}
}