1#![no_std]
30#![feature(doc_cfg)]
31#![feature(macro_metavar_expr)]
32#![feature(never_type)]
33#![feature(try_blocks)]
34
35extern crate alloc;
36
37use alloc::borrow::Cow;
38#[cfg(any(feature = "device", feature = "host"))]
39use alloc::boxed::Box;
40
41#[cfg(feature = "host")]
42pub use connection::*;
43use wasefire_error::Error;
44use wasefire_wire::Wire;
45#[cfg(feature = "host")]
46use wasefire_wire::Yoke;
47
48pub mod applet;
49#[cfg(feature = "host")]
50pub mod bundle;
51pub mod common;
52#[cfg(feature = "host")]
53mod connection;
54pub mod platform;
55pub mod transfer;
56
57pub trait Service: 'static {
59 #[cfg(feature = "host")]
60 const NAME: &str;
61 #[cfg(feature = "host")]
63 const VERSIONS: Versions;
64 type Request<'a>: Wire<'a>;
65 type Response<'a>: Wire<'a>;
66 #[cfg(feature = "host")]
67 fn request(x: Self::Request<'_>) -> Api<'_, Request>;
68 #[cfg(feature = "serde")]
69 fn response(x: Self::Response<'_>) -> Api<'_, Response>;
70}
71
72#[derive(Debug)]
73pub enum Request {}
74impl sealed::Direction for Request {
75 type Type<'a, T: Service> = T::Request<'a>;
76}
77
78#[derive(Debug)]
79#[cfg(any(feature = "_descriptor", feature = "serde"))]
80pub enum Response {}
81#[cfg(any(feature = "_descriptor", feature = "serde"))]
82impl sealed::Direction for Response {
83 type Type<'a, T: Service> = T::Response<'a>;
84}
85
86mod sealed {
87 pub trait Direction: 'static {
88 type Type<'a, T: crate::Service>: wasefire_wire::Wire<'a>;
89 }
90}
91
92#[derive(Wire)]
93#[wire(static = T, range = 2)]
94pub enum ApiResult<'a, T: Service> {
95 #[wire(tag = 0)]
96 Ok(T::Response<'a>),
97 #[wire(tag = 1)]
98 Err(Error),
99}
100
101#[cfg(feature = "host")]
102impl Api<'_, Request> {
103 pub fn encode(&self) -> Result<Box<[u8]>, Error> {
104 wasefire_wire::encode(self)
105 }
106}
107
108#[cfg(feature = "device")]
109impl<'a> Api<'a, Request> {
110 pub fn decode(data: &'a [u8]) -> Result<Self, Error> {
111 wasefire_wire::decode(data)
112 }
113}
114
115impl<T: Service> ApiResult<'_, T> {
116 #[cfg(feature = "device")]
117 pub fn encode(&self) -> Result<Box<[u8]>, Error> {
118 wasefire_wire::encode(self)
119 }
120
121 #[cfg(feature = "host")]
122 pub fn decode(data: &[u8]) -> Result<ApiResult<'_, T>, Error> {
123 wasefire_wire::decode(data)
124 }
125
126 #[cfg(feature = "host")]
127 pub fn decode_yoke(data: Box<[u8]>) -> Result<Yoke<ApiResult<'static, T>>, Error> {
128 wasefire_wire::decode_yoke(data)
129 }
130}
131
132#[derive(Debug, Copy, Clone, Wire)]
133#[cfg(any(feature = "host", feature = "_descriptor"))]
134pub struct Versions {
135 pub min: u32,
136 pub max: Option<u32>,
137}
138
139#[cfg(feature = "host")]
140impl Versions {
141 pub fn contains(&self, version: u32) -> Result<bool, Error> {
142 match (self.min, self.max) {
143 (min, Some(max)) => Ok((min ..= max).contains(&version)),
145 (min, None) if version < min => Ok(false),
147 (_, None) if VERSION < version => Err(Error::world(wasefire_error::Code::OutOfBounds)),
150 _ => Ok(true),
152 }
153 }
154}
155
156#[derive(Debug, Clone, Wire)]
157#[cfg(feature = "_descriptor")]
158pub struct Descriptor {
159 pub tag: u32,
160 pub versions: Versions,
161}
162
163#[cfg(feature = "_descriptor")]
164impl core::fmt::Display for Descriptor {
165 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
166 let Descriptor { tag, versions: Versions { min, max } } = *self;
167 match max {
168 Some(max) => write!(f, "{tag} [{min} - {max}]"),
169 None => write!(f, "{tag} [{min} -]"),
170 }
171 }
172}
173
174#[cfg(all(test, feature = "serde"))]
175fn _api_serde() {
176 fn is_serializable<T: serde::Serialize>() {}
177 fn is_deserializable_owned<T>()
178 where for<'a> T: serde::Deserialize<'a> {
179 }
180 is_serializable::<Api<Request>>();
181 is_deserializable_owned::<Api<Request>>();
182 is_serializable::<Api<Response>>();
183 is_deserializable_owned::<Api<Response>>();
184}
185
186macro_rules! api {
187 ($(#![$api:meta])* $(
188 $(#[doc = $doc:literal])*
189 $tag:literal [$min:literal - $($max:literal)?] $Name:ident: $request:ty => $response:ty,
190 )* next $next:literal [$version:literal - ]) => {
191 $(#[$api])* #[derive(Debug, Wire)]
192 #[wire(static = T)]
193 #[cfg_attr(feature = "host", wire(range = $next))]
194 #[cfg_attr(not(feature = "_exhaustive"), non_exhaustive)]
195 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
196 pub enum Api<'a, T: sealed::Direction> {
197 $(
198 $(#[doc = $doc])* $(#[cfg(feature = "host")] ${ignore($max)})? #[wire(tag = $tag)]
199 $Name(T::Type<'a, $Name>),
200 )*
201 }
202 $(
203 $(#[doc = $doc])* $(#[cfg(feature = "host")] ${ignore($max)})? #[derive(Debug)]
204 pub enum $Name {}
205 $(#[cfg(feature = "host")] ${ignore($max)})?
206 impl Service for $Name {
207 #[cfg(feature = "host")]
208 const NAME: &str = stringify!($Name);
209 #[cfg(feature = "host")]
210 const VERSIONS: Versions = api!(versions $min $($max)?);
211 type Request<'a> = $request;
212 type Response<'a> = $response;
213 #[cfg(feature = "host")]
214 fn request(x: Self::Request<'_>) -> Api<'_, Request> { Api::$Name(x) }
215 #[cfg(feature = "serde")]
216 fn response(x: Self::Response<'_>) -> Api<'_, Response> {
217 #[allow(unreachable_code)] Api::$Name(x)
218 }
219 }
220 )*
221 pub const VERSION: u32 = $version - 1;
223 #[cfg(feature = "_descriptor")]
224 pub const DESCRIPTORS: &'static [Descriptor] = &[
225 $(
226 $(#[cfg(feature = "host")] ${ignore($max)})?
227 Descriptor { tag: $tag, versions: api!(versions $min $($max)?) },
228 )*
229 ];
230 };
231
232 (max) => (None);
233 (max $max:literal) => (Some($max));
234
235 (versions $min:literal $($max:literal)?) => (Versions { min: $min, max: api!(max $($max)?) });
236}
237
238api! {
239 0 [0 -] ApiVersion: () => u32,
245
246 1 [0 -] AppletRequest: applet::Request<'a> => (),
248
249 2 [0 -] AppletResponse: applet::AppletId => Option<Cow<'a, [u8]>>,
251
252 3 [0 -] PlatformReboot: () => !,
254
255 4 [0 -] AppletTunnel: applet::Tunnel<'a> => (),
257
258 5 [1 - 4] _PlatformInfo0: () => platform::_Info0<'a>,
262
263 6 [2 -] PlatformVendor: Cow<'a, [u8]> => Cow<'a, [u8]>,
265
266 7 [3 - 4] _PlatformUpdateMetadata: () => Cow<'a, [u8]>,
271
272 8 [3 - 6] _PlatformUpdate0: transfer::_Request0<'a> => (),
276
277 9 [4 - 6] _AppletInstall0: transfer::_Request0<'a> => (),
281
282 10 [4 - 6] _AppletUninstall0: () => (),
286
287 11 [4 -] AppletExitStatus: applet::AppletId => Option<applet::ExitStatus>,
289
290 12 [4 -] PlatformLock: () => (),
294
295 13 [5 - 8] _PlatformInfo1: () => platform::_Info1<'a>,
299
300 14 [6 -] PlatformClearStore: usize => (),
304
305 15 [7 -] PlatformUpdate: transfer::Request<'a> => transfer::Response,
307
308 16 [7 - 10] _AppletInstall1: transfer::Request<'a> => transfer::Response,
312
313 17 [8 -] AppletReboot: applet::AppletId => (),
315
316 18 [9 - 9] _PlatformInfo2: () => platform::_Info2<'a>,
320
321 19 [10 -] PlatformInfo3: () => platform::Info3<'a>,
323
324 20 [11 -] AppletMetadata0: applet::AppletId => applet::Metadata0<'a>,
326
327 21 [11 -] AppletInstall2: transfer::Request<'a> => transfer::Response,
332
333 next 22 [12 -]
334}