use proc_macro2::{TokenStream, TokenTree};
use quote::{TokenStreamExt, format_ident, quote};
use syn::{
Attribute, Block, FnArg, GenericArgument, Generics, Ident, LitStr, Meta, Pat, PatType, Path, PathArguments,
ReceiverKind, ReturnType, Stmt, Token, Type, TypeParamBound, braced, parenthesized,
parse::{Parse, ParseStream},
punctuated::Punctuated,
spanned::Spanned,
token::{self, Comma},
};
use crate::{
assoc_type::{AssocType, remove_self_type},
util::{attribute_tokens, to_pascal_case},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SelfRef {
Value,
Ref,
RefMut,
}
const COMPACT_RESPONDER_NAME: &str = "_59";
const FULL_RESPONDER_NAME: &str = "__reply_tx";
fn is_numerical_name(name: &str) -> bool {
match name.strip_prefix('_').map(str::parse::<usize>) {
Some(Ok(id)) => id < 60,
_ => false,
}
}
fn skip_meta_value(input: ParseStream) -> syn::Result<()> {
input.step(|cursor| {
let mut rest = *cursor;
while let Some((tt, next)) = rest.token_tree() {
match &tt {
TokenTree::Punct(punct) if punct.as_char() == ',' => break,
_ => rest = next,
}
}
Ok(((), rest))
})
}
fn numerical_serde_rename(attrs: &[Attribute]) -> syn::Result<bool> {
let mut numerical = false;
for attr in attrs {
if !attr.path().is_ident("serde") {
continue;
}
attr.parse_nested_meta(|meta| {
if !meta.path.is_ident("rename") {
return skip_meta_value(meta.input);
}
let mut check = |value: ParseStream| -> syn::Result<()> {
let name: LitStr = value.parse()?;
if matches!(name.value().as_str(), COMPACT_RESPONDER_NAME | FULL_RESPONDER_NAME) {
return Err(syn::Error::new(
name.span(),
format!("the name `{}` is reserved by remoc", name.value()),
));
}
numerical |= is_numerical_name(&name.value());
Ok(())
};
if meta.input.peek(Token![=]) {
check(meta.value()?)?;
} else {
meta.parse_nested_meta(|meta| check(meta.value()?))?;
}
Ok(())
})?;
}
Ok(numerical)
}
#[derive(Debug)]
pub struct NamedArg {
pub attrs: Vec<Attribute>,
pub ident: Ident,
pub ty: Type,
}
impl NamedArg {
fn extract(pat_type: &PatType) -> syn::Result<(Self, bool)> {
let ident = if let Pat::Ident(pat_ident) = &*pat_type.pat {
pat_ident.ident.clone()
} else {
return Err(syn::Error::new(pat_type.pat.span(), "expected identifier"));
};
let numerical_rename = numerical_serde_rename(&pat_type.attrs)?;
Ok((Self { attrs: pat_type.attrs.clone(), ident, ty: (*pat_type.ty).clone() }, numerical_rename))
}
}
#[derive(Debug)]
pub struct TraitMethod {
pub trait_name: Ident,
pub doc_attrs: Vec<Attribute>,
pub serde_attrs: Vec<Attribute>,
pub numerical_rename: bool,
pub attrs: Vec<Attribute>,
pub ident: Ident,
pub self_ref: SelfRef,
pub args: Vec<NamedArg>,
pub ret_ty: Type,
pub bounds: Punctuated<TypeParamBound, Token![+]>,
pub cancel: bool,
pub pipelinable: bool,
pub pipelined_name: Option<Ident>,
pub body: Option<Vec<Stmt>>,
}
fn future_output_type(path: &Path) -> Option<&Type> {
let args = match (path.segments.get(0), path.segments.get(1), path.segments.get(2)) {
(Some(p0), None, None) if p0.ident == "Future" => &p0.arguments,
(Some(p0), Some(p1), Some(p2))
if (p0.ident == "std" || p0.ident == "core") && p1.ident == "future" && p2.ident == "Future" =>
{
&p2.arguments
}
_ => return None,
};
let PathArguments::AngleBracketed(args) = args else { return None };
for arg in &args.args {
let GenericArgument::AssocType(ty) = arg else { continue };
if ty.ident == "Output" {
return Some(&ty.ty);
}
}
None
}
fn is_send(path: &Path) -> bool {
match (path.segments.get(0), path.segments.get(1), path.segments.get(2)) {
(Some(p0), None, None) if p0.ident == "Send" => true,
(Some(p0), Some(p1), Some(p2))
if (p0.ident == "std" || p0.ident == "core") && p1.ident == "marker" && p2.ident == "Send" =>
{
true
}
_ => false,
}
}
impl TraitMethod {
pub fn parse(trait_name: &Ident, mut attrs: Vec<Attribute>, input: ParseStream) -> syn::Result<Self> {
let is_async = input.parse::<Option<Token![async]>>()?.is_some();
input.parse::<Token![fn]>()?;
let ident: Ident = input.parse()?;
let mut cancel = true;
let mut pipelinable = false;
let mut pipelined_name = None;
let mut attr_err = None;
attrs.retain(|attr| {
let Some(name) = attr.path().get_ident() else { return true };
if *name == "no_cancel" {
cancel = false;
return false;
}
if *name == "pipelinable" {
pipelinable = true;
match &attr.meta {
Meta::Path(_) => (),
Meta::List(_) => match attr.parse_args::<Ident>() {
Ok(name) => pipelined_name = Some(name),
Err(err) => attr_err = Some(err),
},
Meta::NameValue(_) => {
attr_err = Some(syn::Error::new_spanned(
attr,
"expected `#[pipelinable]` or `#[pipelinable(name)]`",
))
}
}
return false;
}
true
});
if let Some(err) = attr_err {
return Err(err);
}
let mut doc_attrs = Vec::new();
let mut serde_attrs = Vec::new();
attrs.retain(|attr| {
if attr.path().is_ident("doc") {
doc_attrs.push(attr.clone());
false
} else if attr.path().is_ident("serde") {
serde_attrs.push(attr.clone());
false
} else {
true
}
});
let mut numerical_rename = numerical_serde_rename(&serde_attrs)?;
let generics = input.parse::<Generics>()?;
if generics.lt_token.is_some() {
return Err(input.error("generics and lifetimes are not allowed on remote trait methods"));
}
let content;
parenthesized!(content in input);
let raw_args: Punctuated<FnArg, Comma> = content.parse_terminated(FnArg::parse, Token![,])?;
let mut self_ref = None;
let mut args = Vec::new();
for arg in raw_args {
match arg {
FnArg::Receiver(recv) => {
self_ref = Some(match recv.kind {
ReceiverKind::Reference(_, _, Some(_)) => SelfRef::RefMut,
ReceiverKind::Reference(_, _, None) => SelfRef::Ref,
ReceiverKind::Value => SelfRef::Value,
_ => {
return Err(
input.error("only methods taking self, &self and &mut self are supported")
);
}
});
}
FnArg::Typed(pat_type) => {
let (arg, arg_numerical_rename) = NamedArg::extract(&pat_type)?;
numerical_rename |= arg_numerical_rename;
args.push(arg);
}
}
}
let self_ref =
self_ref.ok_or_else(|| input.error("associated functions are not allowed in remote traits"))?;
let ret: ReturnType = input.parse()?;
let ret_ty = match ret {
ReturnType::Type(_, ty) => {
if is_async {
Some((*ty, true, Punctuated::new()))
} else {
match *ty {
Type::ImplTrait(impl_trait) => {
let mut others: Punctuated<TypeParamBound, Token![+]> = Punctuated::new();
let mut output = None;
let mut has_send = false;
for bound in impl_trait.bounds {
match bound {
TypeParamBound::Trait(tb) if is_send(&tb.path) => has_send = true,
TypeParamBound::Trait(tb) if future_output_type(&tb.path).is_some() => {
output = future_output_type(&tb.path).cloned()
}
_ => others.push(bound),
}
}
output.map(|output| (output, has_send, others))
}
_ => None,
}
}
}
ReturnType::Default => None,
};
let Some((ret_ty, true, bounds)) = ret_ty else {
return Err(
input.error("'async fn' methods must return 'Result<_>' and 'fn' methods must return 'impl Future<Output = Result<_>> + Send'")
);
};
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 {
trait_name: trait_name.clone(),
doc_attrs,
serde_attrs,
numerical_rename,
attrs,
ident,
self_ref,
args,
ret_ty,
bounds,
cancel,
pipelinable,
pipelined_name,
body,
})
}
pub fn call_ident(&self) -> Ident {
format_ident!("{}_call", &self.ident)
}
fn full_name_str(&self) -> TokenStream {
let name = format!("{}::{}", self.trait_name, self.ident);
quote! { #name }
}
pub fn call_trait_method(&self, impl_future: bool) -> TokenStream {
let Self { ident, ret_ty, .. } = self;
let full_name = self.full_name_str();
let call_ident = self.call_ident();
let self_bound = self.twin_self_bound();
let self_ref = match self.self_ref {
SelfRef::Value => quote! { self, },
SelfRef::Ref => quote! { &self, },
SelfRef::RefMut => quote! { &mut self, },
};
let mut args = quote! {};
let mut call_args = quote! {};
for NamedArg { ident, ty, .. } in &self.args {
args.append_all(quote! { #ident : #ty , });
call_args.append_all(quote! { #ident , });
}
let doc = format!(
"Starts [`{ident}`](Self::{ident}) without awaiting its result.\n\n\
Await the returned [call](::remoc::rtc::Call) to obtain the result."
);
let body = quote! {
::remoc::rtc::Call::ready(#full_name, self.#ident(#call_args).await)
};
if impl_future {
quote! {
#[doc=#doc]
#[allow(clippy::async_yields_async)]
fn #call_ident ( #self_ref #args )
-> impl ::std::future::Future<Output = ::remoc::rtc::Call<#ret_ty>>
+ ::std::marker::Send
#self_bound
{
async move { #body }
}
}
} else {
quote! {
#[doc=#doc]
#[allow(clippy::async_yields_async)]
async fn #call_ident ( #self_ref #args ) -> ::remoc::rtc::Call<#ret_ty>
#self_bound
{ #body }
}
}
}
pub fn pipelined_trait_method(&self, impl_future: bool) -> TokenStream {
let Self { ident, .. } = self;
let full_name = self.full_name_str();
let pipelined_ident = self.pipelined_ident();
let ret_ty = self.pipelined_ret_ty(&[]);
let req_rx_ty = self.pipelined_req_rx_ty(&[]);
let self_bound = self.twin_self_bound();
let self_ref = match self.self_ref {
SelfRef::Value => quote! { self, },
SelfRef::Ref => quote! { &self, },
SelfRef::RefMut => quote! { &mut self, },
};
let mut args = quote! {};
let mut call_args = quote! {};
for NamedArg { ident, ty, .. } in &self.args {
args.append_all(quote! { #ident : #ty , });
call_args.append_all(quote! { #ident , });
}
let doc = format!(
"Calls [`{ident}`](Self::{ident}) and lets the returned client execute the requests of the provided request receiver in the background.\n\n\
Await the returned [call](::remoc::rtc::Call) to obtain its result, which tells you whether the session was established. \
The calls you make through the client report their own errors. \
See the [pipelining module](::remoc::rtc::pipelining) for how to combine them.\n\n\
The provided implementation should not be overridden."
);
let body = quote! {
::remoc::rtc::Call::ready(#full_name, async {
let __client = self.#ident(#call_args).await?;
::remoc::rtc::spawn(::remoc::rtc::Instrument::in_current_span(async move {
let mut __req_rx = __req_rx;
let _ = ::remoc::rtc::ReqReceiver::forward(&mut __req_rx, __client).await;
}));
::std::result::Result::Ok(())
}.await)
};
if impl_future {
quote! {
#[doc=#doc]
#[allow(clippy::async_yields_async)]
fn #pipelined_ident ( #self_ref #args __req_rx: #req_rx_ty )
-> impl ::std::future::Future<Output = ::remoc::rtc::Call<#ret_ty>>
+ ::std::marker::Send
#self_bound
{
async move { #body }
}
}
} else {
quote! {
#[doc=#doc]
#[allow(clippy::async_yields_async)]
async fn #pipelined_ident ( #self_ref #args __req_rx: #req_rx_ty )
-> ::remoc::rtc::Call<#ret_ty>
#self_bound
{ #body }
}
}
}
pub fn pipelined_ident(&self) -> Ident {
match &self.pipelined_name {
Some(name) => name.clone(),
None => format_ident!("{}_pipelined", &self.ident),
}
}
fn pipelined_ret_ty(&self, assoc: &[AssocType]) -> TokenStream {
let ret_ty = remove_self_type(&self.ret_ty, assoc);
quote! { <#ret_ty as ::remoc::rtc::Response>::WithoutValue }
}
fn pipelined_req_rx_ty(&self, assoc: &[AssocType]) -> TokenStream {
let ret_ty = remove_self_type(&self.ret_ty, assoc);
quote! { <#ret_ty as ::remoc::rtc::PipelinableResponse>::ReqReceiver }
}
fn twin_self_bound(&self) -> TokenStream {
match self.self_ref {
SelfRef::Ref => quote! { where Self: ::std::marker::Sync },
SelfRef::RefMut => quote! { where Self: ::std::marker::Send },
SelfRef::Value => quote! { where Self: ::std::marker::Send + ::std::marker::Sized },
}
}
pub fn trait_method(&self, impl_future: bool) -> TokenStream {
let Self { doc_attrs, attrs, ident, ret_ty, .. } = self;
let doc_attrs = attribute_tokens(doc_attrs);
let attrs = attribute_tokens(attrs);
let mut args = quote! {};
let self_ref = match self.self_ref {
SelfRef::Value => quote! {self,},
SelfRef::Ref => quote! {&self,},
SelfRef::RefMut => quote! {&mut self,},
};
args.append_all(self_ref);
for NamedArg { attrs: _, ident, ty } in &self.args {
args.append_all(quote! { #ident : #ty , });
}
let body_opt = match &self.body {
Some(stmts) => {
let mut body = quote! {};
body.append_all(stmts);
if impl_future {
quote! { { async move { #body } } }
} else {
quote! { { #body } }
}
}
None => quote! { ; },
};
let sig = if impl_future {
let bounds = if self.bounds.is_empty() {
quote! {}
} else {
let bounds = &self.bounds;
quote! { + #bounds }
};
quote! { #doc_attrs #attrs fn #ident ( #args ) -> impl ::std::future::Future<Output = #ret_ty> + ::std::marker::Send #bounds }
} else {
quote! { #doc_attrs #attrs async fn #ident ( #args ) -> #ret_ty }
};
quote! {
#sig
#body_opt
}
}
pub fn request_enum_entry(&self, assoc: &[AssocType]) -> TokenStream {
let ident = to_pascal_case(&self.ident);
let ret_ty = remove_self_type(&self.ret_ty, assoc);
let responder_rename = if self.numerical_rename {
quote! { #[serde(rename = #COMPACT_RESPONDER_NAME)] }
} else {
quote! { #[serde(rename = #FULL_RESPONDER_NAME)] }
};
let responder_ty = if self.pipelinable {
quote! { ::remoc::rtc::PipelinableResponder<#ret_ty, Codec> }
} else {
quote! { ::remoc::rtc::Responder<#ret_ty, Codec> }
};
let mut entries = quote! {
#[doc="Response channel for sending the result of the method invocation.\n\n"]
#[doc="The channel is closed when the calling async method is cancelled "]
#[doc="or a connection error occurs."]
#responder_rename
__rsp: #responder_ty,
};
for NamedArg { attrs, ident, ty } in &self.args {
if !attrs.iter().any(|attr| attr.path().is_ident("doc")) {
entries.append_all(quote! {
#[doc = concat!(stringify!(#ident), " parameter")]
});
}
let attrs = attribute_tokens(attrs);
let ty = remove_self_type(ty, assoc);
entries.append_all(quote! {
#attrs
#ident : #ty ,
});
}
let doc_attrs = attribute_tokens(&self.doc_attrs);
let serde_attrs = attribute_tokens(&self.serde_attrs);
quote! { #doc_attrs #serde_attrs #ident {#entries} , }
}
pub fn dispatch_discriminator(&self) -> TokenStream {
let ident = &self.ident;
let enum_ident = to_pascal_case(ident);
let mut args = quote! {};
for NamedArg { ident: arg_ident, .. } in &self.args {
args.append_all(quote! { #arg_ident, });
}
let full_name = self.full_name_str();
let invoke = |method: TokenStream, responder: TokenStream, extra_args: TokenStream, is_call: bool| {
let perform = if is_call {
quote! { async { #method(#args #extra_args).await.await } }
} else {
quote! { #method(#args #extra_args) }
};
if self.cancel {
quote! {
::remoc::rtc::select! {
biased;
() = #responder.closed() => (),
result = #perform => {
::remoc::rtc::complete_call(#responder, #full_name, &__err_tx, __guard, result).await;
}
}
}
} else {
quote! {
let result = #perform.await;
::remoc::rtc::complete_call(#responder, #full_name, &__err_tx, __guard, result).await;
}
}
};
let call = if self.pipelinable {
let pipelined_ident = self.pipelined_ident();
let normal = invoke(quote! { __target.#ident }, quote! { __rsp }, quote! {}, false);
let pipeline =
invoke(quote! { __target.#pipelined_ident }, quote! { responder }, quote! { req_rx, }, true);
quote! {
match __rsp {
::remoc::rtc::PipelinableResponder::Normal(__rsp) => { #normal }
::remoc::rtc::PipelinableResponder::Pipeline { req_rx, responder } => { #pipeline }
}
}
} else {
invoke(quote! { __target.#ident }, quote! { __rsp }, quote! {}, false)
};
quote! {
Self :: #enum_ident { #args __rsp } => {
async move { #call }.boxed()
},
}
}
pub fn method_name_clause(&self) -> TokenStream {
let enum_ident = to_pascal_case(&self.ident);
let name = self.ident.to_string();
quote! {
Self :: #enum_ident { .. } => #name,
}
}
pub fn sequential_clause(&self) -> TokenStream {
let enum_ident = to_pascal_case(&self.ident);
quote! {
Self :: #enum_ident { __rsp, .. } => __rsp.sequential(),
}
}
pub fn client_method(
&self, req_value: &Ident, req_ref: &Ident, req_ref_mut: &Ident, assoc: &[AssocType],
) -> TokenStream {
let Self { ident, self_ref, .. } = self;
let ret_ty = remove_self_type(&self.ret_ty, assoc);
let (self_ref, req_enum, req_type) = match self_ref {
SelfRef::Value => (quote! { self }, req_value, quote! { Value }),
SelfRef::Ref => (quote! { &self }, req_ref, quote! { Ref }),
SelfRef::RefMut => (quote! { &mut self }, req_ref_mut, quote! { RefMut }),
};
let req_case = to_pascal_case(ident);
let mut args = quote! {};
let mut entries = quote! {};
for NamedArg { ident, ty, .. } in &self.args {
let ty = remove_self_type(ty, assoc);
args.append_all(quote! { #ident : #ty , });
entries.append_all(quote! { #ident , });
}
let responder = if self.pipelinable {
quote! { ::remoc::rtc::PipelinableResponder::Normal(__rsp) }
} else {
quote! { __rsp }
};
let pipelined_method = self.pipelinable.then(|| self.pipelined_client_method(req_enum, &req_type, assoc));
let call_method = self.call_client_method(req_enum, &req_type, assoc);
quote! {
async fn #ident (#self_ref, #args) -> #ret_ty {
let (__rsp, response_rx) = self.__rsp();
let req_value = #req_enum :: #req_case { __rsp: #responder, #entries };
let req = ::remoc::rtc::Req::#req_type(req_value);
let mut guard = match self.monitor.pre_call(&req).await {
::remoc::rtc::monitor::CallDecision::Pass => ::std::boxed::Box::new(::remoc::rtc::monitor::PassGuard),
::remoc::rtc::monitor::CallDecision::Guard(guard) => guard,
::remoc::rtc::monitor::CallDecision::Drop => return Err(::remoc::rtc::CallError::Dropped.into()),
};
self.req_tx.send(req).await.map_err(::remoc::rtc::CallError::from)?;
match response_rx.await {
Ok(response) => {
let response: #ret_ty = ::std::convert::Into::into(response);
if response.is_err() {
guard.failed();
}
response
}
Err(err) => {
guard.response_failed(&err);
Err(::remoc::rtc::CallError::from(err).into())
}
}
}
#call_method
#pipelined_method
}
}
fn call_client_method(&self, req_enum: &Ident, req_type: &TokenStream, assoc: &[AssocType]) -> TokenStream {
let full_name = self.full_name_str();
let call_ident = self.call_ident();
let ret_ty = remove_self_type(&self.ret_ty, assoc);
let self_bound = self.twin_self_bound();
let req_case = to_pascal_case(&self.ident);
let self_ref = match self.self_ref {
SelfRef::Value => quote! { self, },
SelfRef::Ref => quote! { &self, },
SelfRef::RefMut => quote! { &mut self, },
};
let responder = if self.pipelinable {
quote! { ::remoc::rtc::PipelinableResponder::Normal(__rsp) }
} else {
quote! { __rsp }
};
let mut args = quote! {};
let mut entries = quote! {};
for NamedArg { ident, ty, .. } in &self.args {
let ty = remove_self_type(ty, assoc);
args.append_all(quote! { #ident : #ty , });
entries.append_all(quote! { #ident , });
}
quote! {
#[allow(clippy::async_yields_async)]
async fn #call_ident (#self_ref #args) -> ::remoc::rtc::Call<#ret_ty>
#self_bound
{
let (__rsp, response_rx) = self.__rsp();
let req_value = #req_enum :: #req_case { __rsp: #responder, #entries };
let req = ::remoc::rtc::Req::#req_type(req_value);
let mut guard = match self.monitor.pre_call(&req).await {
::remoc::rtc::monitor::CallDecision::Pass => ::std::boxed::Box::new(::remoc::rtc::monitor::PassGuard),
::remoc::rtc::monitor::CallDecision::Guard(guard) => guard,
::remoc::rtc::monitor::CallDecision::Drop => {
return ::remoc::rtc::Call::ready(
#full_name,
::remoc::rtc::Response::from_call_error(::remoc::rtc::CallError::Dropped)
);
}
};
if let ::std::result::Result::Err(err) = self.req_tx.send(req).await {
return ::remoc::rtc::Call::ready(
#full_name,
::remoc::rtc::Response::from_call_error(::remoc::rtc::CallError::from(err))
);
}
::remoc::rtc::Call::pending(#full_name, async move {
match response_rx.await {
Ok(response) => {
let response: #ret_ty = ::std::convert::Into::into(response);
if ::remoc::rtc::Response::is_error(&response) {
guard.failed();
}
response
}
Err(err) => {
guard.response_failed(&err);
::remoc::rtc::Response::from_call_error(::remoc::rtc::CallError::from(err))
}
}
})
}
}
}
fn pipelined_client_method(
&self, req_enum: &Ident, req_type: &TokenStream, assoc: &[AssocType],
) -> TokenStream {
let full_name = self.full_name_str();
let call_ident = self.pipelined_ident();
let ret_ty = self.pipelined_ret_ty(assoc);
let req_rx_ty = self.pipelined_req_rx_ty(assoc);
let self_bound = self.twin_self_bound();
let req_case = to_pascal_case(&self.ident);
let self_ref = match self.self_ref {
SelfRef::Value => quote! { self, },
SelfRef::Ref => quote! { &self, },
SelfRef::RefMut => quote! { &mut self, },
};
let mut args = quote! {};
let mut entries = quote! {};
for NamedArg { ident, ty, .. } in &self.args {
let ty = remove_self_type(ty, assoc);
args.append_all(quote! { #ident : #ty , });
entries.append_all(quote! { #ident , });
}
quote! {
#[allow(clippy::async_yields_async)]
async fn #call_ident (#self_ref #args __req_rx: #req_rx_ty)
-> ::remoc::rtc::Call<#ret_ty>
#self_bound
{
let (__rsp, response_rx) = self.__rsp();
let req_value = #req_enum :: #req_case {
__rsp: ::remoc::rtc::PipelinableResponder::Pipeline {
req_rx: __req_rx, responder: __rsp,
},
#entries
};
let req = ::remoc::rtc::Req::#req_type(req_value);
let mut guard = match self.monitor.pre_call(&req).await {
::remoc::rtc::monitor::CallDecision::Pass => ::std::boxed::Box::new(::remoc::rtc::monitor::PassGuard),
::remoc::rtc::monitor::CallDecision::Guard(guard) => guard,
::remoc::rtc::monitor::CallDecision::Drop => {
return ::remoc::rtc::Call::ready(#full_name, ::remoc::rtc::Response::from_call_error(
::remoc::rtc::CallError::Dropped,
));
}
};
if let ::std::result::Result::Err(err) = self.req_tx.send(req).await {
return ::remoc::rtc::Call::ready(#full_name, ::remoc::rtc::Response::from_call_error(
::remoc::rtc::CallError::from(err),
));
}
::remoc::rtc::Call::pending(#full_name, async move {
match response_rx.await {
Ok(response) => {
let response: #ret_ty = ::std::convert::Into::into(response);
if ::remoc::rtc::Response::is_error(&response) {
guard.failed();
}
response
}
Err(err) => {
guard.response_failed(&err);
::remoc::rtc::Response::from_call_error(::remoc::rtc::CallError::from(err))
}
}
})
}
}
}
}