Skip to content

Commit

Permalink
RTC: default method implementations
Browse files Browse the repository at this point in the history
Allow remote traits to specify default implementations of methods.
  • Loading branch information
surban committed Mar 22, 2024
1 parent 8b92657 commit cfcccdb
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 9 deletions.
5 changes: 5 additions & 0 deletions remoc/src/rtc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,11 @@ pub use async_trait::async_trait;
///
/// Lifetimes are not allowed on remote traits and their methods.
///
/// # Default implementations of methods
///
/// Default implementations of methods may be provided.
/// However, this requires specifying `Send` and `Sync` as supertraits of the remote trait.
///
/// # Attributes
///
/// If the `clone` argument is specified (by invoking the attribute as `#[remoc::rtc::remote(clone)]`),
Expand Down
61 changes: 61 additions & 0 deletions remoc/tests/rtc/default.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use futures::join;

use crate::loop_channel;

// Avoid imports here to test if proc macro works without imports.

#[remoc::rtc::remote]
pub trait DefaultTrait: Sync {
async fn value(&self) -> Result<u32, remoc::rtc::CallError>;

async fn default_method(&self) -> Result<u32, remoc::rtc::CallError> {
let a = 1;
let b = 2;
Ok(a + b)
}
}

pub struct CounterObj {
value: u32,
}

impl CounterObj {
pub fn new() -> Self {
Self { value: 0 }
}
}

#[remoc::rtc::async_trait]
impl DefaultTrait for CounterObj {
async fn value(&self) -> Result<u32, remoc::rtc::CallError> {
Ok(self.value)
}
}

#[tokio::test]
async fn simple() {
use remoc::rtc::ServerRefMut;

crate::init();
let ((mut a_tx, _), (_, mut b_rx)) = loop_channel::<DefaultTraitClient>().await;

println!("Creating default server");
let mut counter_obj = CounterObj::new();
let (server, client) = DefaultTraitServerRefMut::new(&mut counter_obj, 1);

println!("Sending default client");
a_tx.send(client).await.unwrap();

let client_task = async move {
println!("Receiving default client");
let client = b_rx.recv().await.unwrap().unwrap();

println!("value: {}", client.value().await.unwrap());
assert_eq!(client.value().await.unwrap(), 0);

println!("default_method: {}", client.default_method().await.unwrap());
assert_eq!(client.default_method().await.unwrap(), 3);
};

join!(client_task, server.serve());
}
1 change: 1 addition & 0 deletions remoc/tests/rtc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod default;
mod generics;
mod readonly;
mod simple;
Expand Down
33 changes: 28 additions & 5 deletions remoc_macro/src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
use proc_macro2::TokenStream;
use quote::{quote, TokenStreamExt};
use syn::{
parenthesized,
braced, parenthesized,
parse::{Parse, ParseStream},
punctuated::Punctuated,
spanned::Spanned,
token,
token::Comma,
Attribute, FnArg, Generics, Ident, Pat, PatType, ReturnType, Token, Type,
Attribute, Block, FnArg, Generics, Ident, Pat, PatType, ReturnType, Stmt, Token, Type,
};

use crate::util::{attribute_tokens, to_pascal_case};
Expand Down Expand Up @@ -62,6 +63,8 @@ pub struct TraitMethod {
pub ret_ty: Type,
/// Whether method should be cancelled, if client sends hangup message.
pub cancel: bool,
/// Method body.
pub body: Option<Vec<Stmt>>,
}

impl Parse for TraitMethod {
Expand Down Expand Up @@ -129,9 +132,18 @@ impl Parse for TraitMethod {
ReturnType::Type(_, ty) => *ty,
ReturnType::Default => return Err(input.error("all methods must return a Result type")),
};
input.parse::<Token![;]>()?;

Ok(Self { attrs, ident, self_ref, args, ret_ty, cancel })
// Parse default body.
let body = if input.peek(token::Brace) {
let content;
braced!(content in input);
Some(content.call(Block::parse_within)?)
} else {
input.parse::<Token![;]>()?;
None
};

Ok(Self { attrs, ident, self_ref, args, ret_ty, cancel, body })
}
}

Expand All @@ -157,8 +169,19 @@ impl TraitMethod {
args.append_all(quote! { #ident : #ty , });
}

// Body.
let body_opt = match &self.body {
Some(stmts) => {
let mut body = quote! {};
body.append_all(stmts);
quote! { { #body } }
}
None => quote! { ; },
};

quote! {
#attrs async fn #ident ( #args ) -> #ret_ty;
#attrs async fn #ident ( #args ) -> #ret_ty
#body_opt
}
}

Expand Down
27 changes: 23 additions & 4 deletions remoc_macro/src/trait_def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use syn::{
braced,
meta::ParseNestedMeta,
parse::{Parse, ParseStream},
Attribute, GenericParam, Generics, Ident, Lifetime, LifetimeParam, Token, TypeParam, Visibility, WhereClause,
punctuated::Punctuated,
token, Attribute, GenericParam, Generics, Ident, Lifetime, LifetimeParam, Token, TypeParam, TypeParamBound,
Visibility, WhereClause,
};

use crate::{
Expand All @@ -26,6 +28,10 @@ pub struct TraitDef {
/// Generics.
/// Contains type parameter `Codec`.
generics: Generics,
/// Colon before supertraits.
colon: Option<Token![:]>,
/// Supertraits.
supertraits: Punctuated<TypeParamBound, Token![+]>,
/// Methods.
methods: Vec<TraitMethod>,
/// Whether the `clone` attribute is present.
Expand All @@ -50,6 +56,19 @@ impl Parse for TraitDef {
return Err(input.error("lifetimes are not allowed on remote traits"));
}

// Parse supertraits.
let colon: Option<Token![:]> = input.parse()?;
let mut supertraits = Punctuated::new();
if colon.is_some() {
loop {
supertraits.push_value(input.parse()?);
if input.peek(Token![where]) || input.peek(token::Brace) {
break;
}
supertraits.push_punct(input.parse()?);
}
}

// Generics where clause.
if let Some(where_clause) = input.parse::<Option<WhereClause>>()? {
generics.make_where_clause().predicates.extend(where_clause.predicates);
Expand All @@ -65,7 +84,7 @@ impl Parse for TraitDef {
methods.push(content.parse()?);
}

Ok(Self { attrs, vis, ident, generics, methods, clone: false })
Ok(Self { attrs, vis, ident, generics, colon, supertraits, methods, clone: false })
}
}

Expand Down Expand Up @@ -100,7 +119,7 @@ impl TraitDef {

/// Vanilla trait definition, without remote-specific attributes.
pub fn vanilla_trait(&self) -> TokenStream {
let Self { vis, ident, attrs, generics, .. } = self;
let Self { vis, ident, attrs, colon, supertraits, generics, .. } = self;
let where_clause = &generics.where_clause;
let attrs = attribute_tokens(attrs);

Expand All @@ -113,7 +132,7 @@ impl TraitDef {
quote! {
#attrs
#[::remoc::rtc::async_trait]
#vis trait #ident #generics #where_clause {
#vis trait #ident #generics #colon #supertraits #where_clause {
#defs
}
}
Expand Down

0 comments on commit cfcccdb

Please sign in to comment.