1#![no_std]
30#![feature(doc_auto_cfg)]
31#![feature(macro_metavar_expr)]
32#![feature(never_type)]
33
34extern crate alloc;
35
36#[cfg(any(feature = "device", feature = "host"))]
37use alloc::boxed::Box;
38
39#[cfg(feature = "host")]
40pub use connection::*;
41use wasefire_error::Error;
42use wasefire_wire::Wire;
43#[cfg(feature = "host")]
44use wasefire_wire::Yoke;
45
46pub mod applet;
47#[cfg(feature = "host")]
48mod connection;
49pub mod platform;
50pub mod transfer;
51
52pub trait Service: 'static {
54 #[cfg(feature = "host")]
55 const NAME: &str;
56 #[cfg(feature = "host")]
58 const VERSIONS: Versions;
59 type Request<'a>: Wire<'a>;
60 type Response<'a>: Wire<'a>;
61 #[cfg(feature = "host")]
62 fn request(x: Self::Request<'_>) -> Api<'_, Request>;
63}
64
65#[derive(Debug)]
66pub enum Request {}
67impl sealed::Direction for Request {
68 type Type<'a, T: Service> = T::Request<'a>;
69}
70
71#[derive(Debug)]
72#[cfg(feature = "_descriptor")]
73pub enum Response {}
74#[cfg(feature = "_descriptor")]
75impl sealed::Direction for Response {
76 type Type<'a, T: Service> = T::Response<'a>;
77}
78
79mod sealed {
80 pub trait Direction: 'static {
81 type Type<'a, T: crate::Service>: wasefire_wire::Wire<'a>;
82 }
83}
84
85#[derive(Wire)]
86#[wire(static = T, range = 2)]
87pub enum ApiResult<'a, T: Service> {
88 #[wire(tag = 0)]
89 Ok(T::Response<'a>),
90 #[wire(tag = 1)]
91 Err(Error),
92}
93
94#[cfg(feature = "host")]
95impl Api<'_, Request> {
96 pub fn encode(&self) -> Result<Box<[u8]>, Error> {
97 wasefire_wire::encode(self)
98 }
99}
100
101#[cfg(feature = "device")]
102impl<'a> Api<'a, Request> {
103 pub fn decode(data: &'a [u8]) -> Result<Self, Error> {
104 wasefire_wire::decode(data)
105 }
106}
107
108impl<T: Service> ApiResult<'_, T> {
109 #[cfg(feature = "device")]
110 pub fn encode(&self) -> Result<Box<[u8]>, Error> {
111 wasefire_wire::encode(self)
112 }
113
114 #[cfg(feature = "host")]
115 pub fn decode(data: &[u8]) -> Result<ApiResult<'_, T>, Error> {
116 wasefire_wire::decode(data)
117 }
118
119 #[cfg(feature = "host")]
120 pub fn decode_yoke(data: Box<[u8]>) -> Result<Yoke<ApiResult<'static, T>>, Error> {
121 wasefire_wire::decode_yoke(data)
122 }
123}
124
125#[derive(Debug, Copy, Clone, Wire)]
126#[cfg(any(feature = "host", feature = "_descriptor"))]
127pub struct Versions {
128 pub min: u32,
129 pub max: Option<u32>,
130}
131
132#[cfg(feature = "host")]
133impl Versions {
134 pub fn contains(&self, version: u32) -> Result<bool, Error> {
135 match (self.min, self.max) {
136 (min, Some(max)) => Ok((min ..= max).contains(&version)),
138 (min, None) if version < min => Ok(false),
140 (_, None) if VERSION < version => Err(Error::world(wasefire_error::Code::OutOfBounds)),
143 _ => Ok(true),
145 }
146 }
147}
148
149#[derive(Debug, Clone, Wire)]
150#[cfg(feature = "_descriptor")]
151pub struct Descriptor {
152 pub tag: u32,
153 pub versions: Versions,
154}
155
156#[cfg(feature = "_descriptor")]
157impl core::fmt::Display for Descriptor {
158 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
159 let Descriptor { tag, versions: Versions { min, max } } = *self;
160 match max {
161 Some(max) => write!(f, "{tag} [{min} - {max}]"),
162 None => write!(f, "{tag} [{min} -]"),
163 }
164 }
165}
166
167macro_rules! api {
168 ($(#![$api:meta])* $(
169 $(#[doc = $doc:literal])*
170 $tag:literal [$min:literal - $($max:literal)?] $Name:ident: $request:ty => $response:ty,
171 )* next $next:literal [$version:literal - ]) => {
172 $(#[$api])* #[derive(Debug, Wire)]
173 #[wire(static = T)]
174 #[cfg_attr(feature = "host", wire(range = $next))]
175 #[cfg_attr(not(feature = "_exhaustive"), non_exhaustive)]
176 pub enum Api<'a, T: sealed::Direction> {
177 $(
178 $(#[doc = $doc])* $(#[cfg(feature = "host")] ${ignore($max)})? #[wire(tag = $tag)]
179 $Name(T::Type<'a, $Name>),
180 )*
181 }
182 $(
183 $(#[doc = $doc])* $(#[cfg(feature = "host")] ${ignore($max)})? #[derive(Debug)]
184 pub enum $Name {}
185 $(#[cfg(feature = "host")] ${ignore($max)})?
186 impl Service for $Name {
187 #[cfg(feature = "host")]
188 const NAME: &str = stringify!($Name);
189 #[cfg(feature = "host")]
190 const VERSIONS: Versions = api!(versions $min $($max)?);
191 type Request<'a> = $request;
192 type Response<'a> = $response;
193 #[cfg(feature = "host")]
194 fn request(x: Self::Request<'_>) -> Api<'_, Request> { Api::$Name(x) }
195 }
196 )*
197 pub const VERSION: u32 = $version - 1;
199 #[cfg(feature = "_descriptor")]
200 pub const DESCRIPTORS: &'static [Descriptor] = &[
201 $(
202 $(#[cfg(feature = "host")] ${ignore($max)})?
203 Descriptor { tag: $tag, versions: api!(versions $min $($max)?) },
204 )*
205 ];
206 };
207
208 (max) => (None);
209 (max $max:literal) => (Some($max));
210
211 (versions $min:literal $($max:literal)?) => (Versions { min: $min, max: api!(max $($max)?) });
212}
213
214api! {
215 0 [0 -] ApiVersion: () => u32,
221
222 1 [0 -] AppletRequest: applet::Request<'a> => (),
224
225 2 [0 -] AppletResponse: applet::AppletId => Option<&'a [u8]>,
227
228 3 [0 -] PlatformReboot: () => !,
230
231 4 [0 -] AppletTunnel: applet::Tunnel<'a> => (),
233
234 5 [1 - 4] _PlatformInfo0: () => platform::_Info0<'a>,
238
239 6 [2 -] PlatformVendor: &'a [u8] => &'a [u8],
241
242 7 [3 - 4] _PlatformUpdateMetadata: () => &'a [u8],
247
248 8 [3 - 6] _PlatformUpdate0: transfer::_Request0<'a> => (),
252
253 9 [4 - 6] _AppletInstall0: transfer::_Request0<'a> => (),
257
258 10 [4 - 6] _AppletUninstall0: () => (),
262
263 11 [4 -] AppletExitStatus: applet::AppletId => Option<applet::ExitStatus>,
265
266 12 [4 -] PlatformLock: () => (),
270
271 13 [5 -] PlatformInfo: () => platform::Info<'a>,
273
274 14 [6 -] PlatformClearStore: usize => (),
278
279 15 [7 -] PlatformUpdate: transfer::Request<'a> => transfer::Response,
281
282 16 [7 -] AppletInstall: transfer::Request<'a> => transfer::Response,
284
285 17 [8 -] AppletReboot: applet::AppletId => (),
287
288 next 18 [9 -]
289}