add async_trait and foreign trait support

This commit is contained in:
sinu
2024-01-09 12:44:40 -08:00
parent 4ab528d7a7
commit dcfe639a84
6 changed files with 141 additions and 8 deletions

View File

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

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

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

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

View File

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

View File

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