mirror of
https://github.com/sinui0/ludi.git
synced 2026-01-09 12:58:04 -05:00
add async_trait and foreign trait support
This commit is contained in:
@@ -8,6 +8,7 @@ ludi = { path = "../ludi" }
|
||||
|
||||
[dev-dependencies]
|
||||
ludi-macros = { path = "../ludi-macros" }
|
||||
async-trait = "0.1"
|
||||
|
||||
[[test]]
|
||||
name = "message_struct"
|
||||
@@ -25,6 +26,10 @@ path = "tests/message_enum.rs"
|
||||
name = "interface_basic"
|
||||
path = "tests/interface_basic.rs"
|
||||
|
||||
[[test]]
|
||||
name = "interface_async_trait"
|
||||
path = "tests/interface_async_trait.rs"
|
||||
|
||||
[[test]]
|
||||
name = "implement_basic"
|
||||
path = "tests/implement_basic.rs"
|
||||
@@ -32,3 +37,11 @@ path = "tests/implement_basic.rs"
|
||||
[[test]]
|
||||
name = "implement_trait"
|
||||
path = "tests/implement_trait.rs"
|
||||
|
||||
[[test]]
|
||||
name = "implement_async_trait"
|
||||
path = "tests/implement_async_trait.rs"
|
||||
|
||||
[[test]]
|
||||
name = "implement_trait_foreign"
|
||||
path = "tests/implement_trait_foreign.rs"
|
||||
|
||||
37
ludi-macros-test/tests/implement_async_trait.rs
Normal file
37
ludi-macros-test/tests/implement_async_trait.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use ludi_macros_test::*;
|
||||
|
||||
#[derive(Default, ludi::Controller)]
|
||||
pub struct Foo;
|
||||
|
||||
impl ludi::Actor for Foo {
|
||||
type Stop = ();
|
||||
type Error = ();
|
||||
|
||||
async fn stopped(&mut self) -> Result<Self::Stop, Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[ludi::interface(msg(wrap))]
|
||||
#[async_trait::async_trait]
|
||||
pub trait Bar {
|
||||
async fn bar(&self) -> String;
|
||||
}
|
||||
|
||||
#[ludi::implement(ctrl)]
|
||||
#[async_trait::async_trait]
|
||||
impl Bar for Foo {
|
||||
async fn bar(&self) -> String {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_implement_trait() {
|
||||
assert_message::<BarMsg, BarMsgReturn>();
|
||||
assert_message::<BarMsgBar, String>();
|
||||
assert_wrap::<BarMsg, BarMsgBar>();
|
||||
assert_handler::<Foo, BarMsgBar>();
|
||||
}
|
||||
38
ludi-macros-test/tests/implement_trait_foreign.rs
Normal file
38
ludi-macros-test/tests/implement_trait_foreign.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use ludi_macros_test::*;
|
||||
|
||||
#[derive(Default, ludi::Controller)]
|
||||
pub struct Foo;
|
||||
|
||||
impl ludi::Actor for Foo {
|
||||
type Stop = ();
|
||||
type Error = ();
|
||||
|
||||
async fn stopped(&mut self) -> Result<Self::Stop, Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod foreign_trait {
|
||||
#[async_trait::async_trait]
|
||||
pub trait Bar {
|
||||
async fn bar(&self) -> String;
|
||||
}
|
||||
}
|
||||
|
||||
#[ludi::implement(ctrl, msg(foreign, wrap))]
|
||||
#[async_trait::async_trait]
|
||||
impl foreign_trait::Bar for Foo {
|
||||
async fn bar(&self) -> String {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_implement_trait() {
|
||||
assert_message::<FooBarMsg, FooBarMsgReturn>();
|
||||
assert_message::<BarMsgBar, String>();
|
||||
assert_wrap::<FooBarMsg, BarMsgBar>();
|
||||
assert_handler::<Foo, BarMsgBar>();
|
||||
}
|
||||
16
ludi-macros-test/tests/interface_async_trait.rs
Normal file
16
ludi-macros-test/tests/interface_async_trait.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use ludi_macros_test::*;
|
||||
|
||||
#[ludi::interface(msg(wrap))]
|
||||
#[async_trait::async_trait]
|
||||
pub trait Bar {
|
||||
async fn bar(&self) -> String;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_implement_trait() {
|
||||
assert_message::<BarMsg, BarMsgReturn>();
|
||||
assert_message::<BarMsgBar, String>();
|
||||
assert_wrap::<BarMsg, BarMsgBar>();
|
||||
}
|
||||
@@ -7,10 +7,11 @@ use syn::{parse_quote, spanned::Spanned};
|
||||
use crate::{
|
||||
items::method::Method,
|
||||
options::{CtrlOptions, MsgOptions, WrapOptions},
|
||||
utils::ctrl_ident,
|
||||
utils::{ctrl_ident, is_ludi_attr},
|
||||
};
|
||||
|
||||
pub(crate) struct ItemImpl {
|
||||
attrs: Vec<syn::Attribute>,
|
||||
msg_options: Option<MsgOptions>,
|
||||
ctrl_options: Option<CtrlOptions>,
|
||||
impl_trait: Option<ImplTrait>,
|
||||
@@ -32,7 +33,9 @@ impl ItemImpl {
|
||||
mut msg_options: Option<MsgOptions>,
|
||||
mut ctrl_options: Option<CtrlOptions>,
|
||||
) -> Self {
|
||||
let item_msg_options = MsgOptions::maybe_from_attributes(&item.attrs);
|
||||
let mut attrs = item.attrs.clone();
|
||||
|
||||
let item_msg_options = MsgOptions::maybe_from_attributes(&attrs);
|
||||
if let Some(item_msg_options) = item_msg_options {
|
||||
if let Some(msg_options) = msg_options.as_mut() {
|
||||
msg_options.merge(&item_msg_options);
|
||||
@@ -41,7 +44,7 @@ impl ItemImpl {
|
||||
}
|
||||
}
|
||||
|
||||
let item_ctrl_options = CtrlOptions::maybe_from_attributes(&item.attrs);
|
||||
let item_ctrl_options = CtrlOptions::maybe_from_attributes(&attrs);
|
||||
if let Some(item_ctrl_options) = item_ctrl_options {
|
||||
if let Some(ctrl_options) = ctrl_options.as_mut() {
|
||||
ctrl_options.merge(&item_ctrl_options);
|
||||
@@ -50,6 +53,8 @@ impl ItemImpl {
|
||||
}
|
||||
}
|
||||
|
||||
attrs.retain(|attr| !is_ludi_attr(attr));
|
||||
|
||||
let syn::Type::Path(syn::TypePath {
|
||||
path: actor_path, ..
|
||||
}) = *(item.self_ty).clone()
|
||||
@@ -122,6 +127,7 @@ impl ItemImpl {
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Self {
|
||||
attrs,
|
||||
msg_options,
|
||||
ctrl_options,
|
||||
impl_trait,
|
||||
@@ -157,10 +163,21 @@ impl ItemImpl {
|
||||
.unwrap_or_default();
|
||||
|
||||
let wrap_ident = name.clone().unwrap_or_else(|| {
|
||||
syn::Ident::new(
|
||||
&format!("{}Msg", self.actor_ident.to_string()),
|
||||
Span::call_site(),
|
||||
)
|
||||
if let Some(impl_trait) = &self.impl_trait {
|
||||
syn::Ident::new(
|
||||
&format!(
|
||||
"{}{}Msg",
|
||||
self.actor_ident.to_string(),
|
||||
impl_trait.trait_ident.to_string()
|
||||
),
|
||||
Span::call_site(),
|
||||
)
|
||||
} else {
|
||||
syn::Ident::new(
|
||||
&format!("{}Msg", self.actor_ident.to_string()),
|
||||
Span::call_site(),
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
let mut wrap_type_params = IdentSet::default();
|
||||
@@ -198,6 +215,7 @@ impl ItemImpl {
|
||||
|
||||
fn expand_ctrl(&self) -> TokenStream {
|
||||
let Self {
|
||||
attrs,
|
||||
ctrl_options,
|
||||
actor_path,
|
||||
..
|
||||
@@ -249,6 +267,7 @@ impl ItemImpl {
|
||||
let methods = self.methods.iter().map(|method| method.expand_ctrl(true));
|
||||
let (impl_generics, _, where_clause) = generics.split_for_impl();
|
||||
quote!(
|
||||
#(#attrs)*
|
||||
impl #impl_generics #trait_path for #ctrl_path #where_clause {
|
||||
#(#methods)*
|
||||
}
|
||||
@@ -266,6 +285,7 @@ impl ItemImpl {
|
||||
let (impl_generics, _, where_clause) = generics.split_for_impl();
|
||||
|
||||
quote!(
|
||||
#(#attrs)*
|
||||
impl #impl_generics #ctrl_path #where_clause {
|
||||
#impl_method
|
||||
}
|
||||
@@ -281,7 +301,13 @@ impl ItemImpl {
|
||||
pub(crate) fn expand(&self) -> TokenStream {
|
||||
let mut tokens = TokenStream::new();
|
||||
|
||||
if self.impl_trait.is_none() {
|
||||
if self.impl_trait.is_none()
|
||||
|| self
|
||||
.msg_options
|
||||
.as_ref()
|
||||
.map(|opts| opts.foreign.is_present())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
tokens.extend(self.expand_messages());
|
||||
tokens.extend(self.expand_wrap());
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ pub(crate) struct MsgOptions {
|
||||
pub(crate) skip: Flag,
|
||||
/// Skip message handler
|
||||
pub(crate) skip_handler: Flag,
|
||||
/// Generate messages for a foreign trait
|
||||
pub(crate) foreign: Flag,
|
||||
}
|
||||
|
||||
impl MsgOptions {
|
||||
@@ -54,6 +56,7 @@ impl MsgOptions {
|
||||
|
||||
self.skip = other.skip.clone();
|
||||
self.skip_handler = other.skip_handler.clone();
|
||||
self.foreign = other.foreign.clone();
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_from_attributes(attrs: &[syn::Attribute]) -> Option<Self> {
|
||||
|
||||
Reference in New Issue
Block a user