use std::marker::PhantomData; use tokio::sync::mpsc; use anyhow::Result; // Define a trait for RPC methods pub trait RpcMethod { type Input; type Output; fn call(client: &C, input: Self::Input) -> Result; } // Generic Proxy struct pub struct Proxy { sender: mpsc::UnboundedSender Result<()> + Send>>, _phantom: PhantomData, } impl Proxy { pub fn new(client: C) -> Self { let (sender, mut receiver) = mpsc::unbounded_channel(); tokio::spawn(async move { while let Some(task) = receiver.recv().await { task(&client)?; } }); Self { sender, _phantom: PhantomData, } } pub async fn call + 'static>(&self, input: M::Input) -> Result { let (response_sender, response_receiver) = tokio::sync::oneshot::channel(); self.sender.send(Box::new(move |client| { let result = M::call(client, input); response_sender.send(result).map_err(|_| anyhow::anyhow!("Failed to send response"))?; Ok(()) }))?; response_receiver.await? } } // Macro to generate client implementations macro_rules! impl_client { ($client:ident, $($method:ident($input:ty) -> $output:ty),*) => { $( pub struct $method; impl RpcMethod<$client> for $method { type Input = $input; type Output = $output; fn call(client: &$client, input: Self::Input) -> Result { client.$method(input) } } )* }; } // EchoClient implementation pub struct EchoClient { // Add any necessary fields } impl EchoClient { pub fn new() -> Self { Self {} } pub fn echo(&self, message: String) -> Result { Ok(message) } } impl_client!(EchoClient, echo(String) -> String); // Usage example async fn example() -> Result<()> { let echo_client = EchoClient::new(); let proxy = Proxy::new(echo_client); let result = proxy.call::(String::from("Hello, world!")).await?; println!("Echo result: {}", result); Ok(()) }