I am puzzled how to do the Response enum, because different operations give different response types. (For example, executor.turn_on_light() gives Result<()> (more precisely, anyhow::Result<()>) , executor.read_something()-> Result<String> etc.) If I have two structures, one for Rx, and one for Tx,
enum ExecutorRx {
TurnOnLight,
SendValueX{device_num: usize},
SendValueY{device_num: usize},
}
enum ExecutorTx {
SendValueXResponse{Result<String>}
SendValueYResponse{Result<i32>}
}
it will take a lot of boilerplate to wire all the variants, and it will be error-prone because there won't be mapping between the Rx command variant and the corresponding Tx response. The other possibility is to have one single enum, where every field represents the executor request, and the response fields will be an enum variant value, like this:
pub enum ExecutorCommandResponse {
Initialize,
SendValueX(SendValueX),
}
pub struct SendValueX {
device_num: usize, // written before sending to the Executor
result: Option<Result<String>>, // executor result, sent back to the caller
}
This doesn't look good either.
Here something I've learnt today while searching how to know which Executor response is a result from which request.
use tokio::sync::{mpsc, oneshot};
struct Request {
data: String,
respond_to: oneshot::Sender<Response>,
}
struct Response {
result: String,
}
struct Worker {
receiver: mpsc::Receiver<Request>,
}
impl Worker {
async fn run(mut self) {
while let Some(req) = self.receiver.recv().await {
let result = format!("Processed: {}", req.data);
let _ = req.respond_to.send(Response { result });
}
}
}
struct Client {
sender: mpsc::Sender<Request>,
}
impl Client {
async fn send_request(&self, data: String) -> String {
let (tx, rx) = oneshot::channel();
let request = Request {
data,
respond_to: tx,
};
self.sender.send(request).await.unwrap();
let response = rx.await.unwrap();
response.result
}
}
#[tokio::main]
async fn main() {
let (tx, rx) = mpsc::channel(100);
let worker = Worker { receiver: rx };
tokio::spawn(worker.run());
let client = Client { sender: tx };
let result = client.send_request("hello".into()).await;
println!("{}", result);
}
I guess my question now is "how to do function calls via channel messages with less boilerplate".