wasefire_protocol/
connection.rs

1// Copyright 2024 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use alloc::boxed::Box;
16use alloc::format;
17use core::any::Any;
18use core::future::Future;
19use core::pin::Pin;
20
21use anyhow::Context;
22use wasefire_wire::Yoke;
23
24use crate::{Api, ApiResult, Request, Service};
25
26pub type DynFuture<'a, T> = Pin<Box<dyn Future<Output = anyhow::Result<T>> + 'a>>;
27
28pub trait Connection: Any + Send {
29    /// Receives a raw response (possibly tunneled) from the device.
30    fn read(&mut self) -> DynFuture<'_, Box<[u8]>>;
31
32    /// Sends a raw request (possibly tunneled) to the device.
33    fn write<'a>(&'a mut self, response: &'a [u8]) -> DynFuture<'a, ()>;
34}
35
36impl Connection for Box<dyn Connection> {
37    fn read(&mut self) -> DynFuture<'_, Box<[u8]>> {
38        (**self).read()
39    }
40
41    fn write<'a>(&'a mut self, response: &'a [u8]) -> DynFuture<'a, ()> {
42        (**self).write(response)
43    }
44}
45
46pub trait ConnectionExt: Connection {
47    /// Calls a service on the device.
48    fn call<S: Service>(
49        &mut self, request: S::Request<'_>,
50    ) -> impl Future<Output = anyhow::Result<Yoke<S::Response<'static>>>> {
51        async { self.call_ref::<S>(&S::request(request)).await }
52    }
53
54    fn call_ref<S: Service>(
55        &mut self, request: &Api<Request>,
56    ) -> impl Future<Output = anyhow::Result<Yoke<S::Response<'static>>>> {
57        async {
58            self.send(request).await.with_context(|| format!("sending {}", S::NAME))?;
59            self.receive::<S>().await.with_context(|| format!("receiving {}", S::NAME))
60        }
61    }
62
63    /// Sends a request to the device.
64    fn send(&mut self, request: &Api<'_, Request>) -> impl Future<Output = anyhow::Result<()>> {
65        async {
66            let request = request.encode().context("encoding request")?;
67            self.write(&request).await
68        }
69    }
70
71    /// Receives a response from the device.
72    fn receive<S: Service>(
73        &mut self,
74    ) -> impl Future<Output = anyhow::Result<Yoke<S::Response<'static>>>> {
75        async {
76            let response = self.read().await?;
77            let response = ApiResult::<S>::decode_yoke(response).context("decoding response")?;
78            response.try_map(|x| match x {
79                ApiResult::Ok(x) => Ok(x),
80                ApiResult::Err(error) => Err(anyhow::Error::new(error)),
81            })
82        }
83    }
84}
85
86impl<T: Connection + ?Sized> ConnectionExt for T {}