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::future::Future;
18use core::pin::Pin;
19
20use anyhow::Context;
21use wasefire_wire::Yoke;
22
23use crate::{Api, ApiResult, Request, Service};
24
25pub type DynFuture<'a, T> = Pin<Box<dyn Future<Output = anyhow::Result<T>> + 'a>>;
26
27pub trait Connection: Send {
28    /// Receives a raw response (possibly tunneled) from the device.
29    fn read(&mut self) -> DynFuture<Box<[u8]>>;
30
31    /// Sends a raw request (possibly tunneled) to the device.
32    fn write<'a>(&'a mut self, response: &'a [u8]) -> DynFuture<'a, ()>;
33}
34
35impl Connection for Box<dyn Connection> {
36    fn read(&mut self) -> DynFuture<Box<[u8]>> {
37        (**self).read()
38    }
39
40    fn write<'a>(&'a mut self, response: &'a [u8]) -> DynFuture<'a, ()> {
41        (**self).write(response)
42    }
43}
44
45pub trait ConnectionExt: Connection {
46    /// Calls a service on the device.
47    fn call<S: Service>(
48        &mut self, request: S::Request<'_>,
49    ) -> impl Future<Output = anyhow::Result<Yoke<S::Response<'static>>>> {
50        async { self.call_ref::<S>(&S::request(request)).await }
51    }
52
53    fn call_ref<S: Service>(
54        &mut self, request: &Api<Request>,
55    ) -> impl Future<Output = anyhow::Result<Yoke<S::Response<'static>>>> {
56        async {
57            self.send(request).await.with_context(|| format!("sending {}", S::NAME))?;
58            self.receive::<S>().await.with_context(|| format!("receiving {}", S::NAME))
59        }
60    }
61
62    /// Sends a request to the device.
63    fn send(&mut self, request: &Api<'_, Request>) -> impl Future<Output = anyhow::Result<()>> {
64        async {
65            let request = request.encode().context("encoding request")?;
66            self.write(&request).await
67        }
68    }
69
70    /// Receives a response from the device.
71    fn receive<S: Service>(
72        &mut self,
73    ) -> impl Future<Output = anyhow::Result<Yoke<S::Response<'static>>>> {
74        async {
75            let response = self.read().await?;
76            let response = ApiResult::<S>::decode_yoke(response).context("decoding response")?;
77            response.try_map(|x| match x {
78                ApiResult::Ok(x) => Ok(x),
79                ApiResult::Err(error) => Err(anyhow::Error::new(error)),
80            })
81        }
82    }
83}
84
85impl<T: Connection + ?Sized> ConnectionExt for T {}