#![no_std]
#![feature(doc_cfg)]
#![feature(macro_metavar_expr)]
#![feature(never_type)]
#![feature(try_blocks)]
extern crate alloc;
use alloc::borrow::Cow;
#[cfg(any(feature = "device", feature = "host"))]
use alloc::boxed::Box;
#[cfg(feature = "host")]
pub use connection::*;
use wasefire_error::Error;
use wasefire_wire::Wire;
#[cfg(feature = "host")]
use wasefire_wire::Yoke;
pub mod applet;
#[cfg(feature = "host")]
pub mod bundle;
pub mod common;
#[cfg(feature = "host")]
mod connection;
pub mod platform;
pub mod transfer;
pub trait Service: 'static {
#[cfg(feature = "host")]
const NAME: &str;
#[cfg(feature = "host")]
const VERSIONS: Versions;
type Request<'a>: Wire<'a>;
type Response<'a>: Wire<'a>;
#[cfg(feature = "host")]
fn request(x: Self::Request<'_>) -> Api<'_, Request>;
#[cfg(feature = "serde")]
fn response(x: Self::Response<'_>) -> Api<'_, Response>;
}
#[derive(Debug)]
pub enum Request {}
impl sealed::Direction for Request {
type Type<'a, T: Service> = T::Request<'a>;
}
#[derive(Debug)]
#[cfg(any(feature = "_descriptor", feature = "serde"))]
pub enum Response {}
#[cfg(any(feature = "_descriptor", feature = "serde"))]
impl sealed::Direction for Response {
type Type<'a, T: Service> = T::Response<'a>;
}
mod sealed {
pub trait Direction: 'static {
type Type<'a, T: crate::Service>: wasefire_wire::Wire<'a>;
}
}
#[derive(Wire)]
#[wire(static = T, range = 2)]
pub enum ApiResult<'a, T: Service> {
#[wire(tag = 0)]
Ok(T::Response<'a>),
#[wire(tag = 1)]
Err(Error),
}
#[cfg(feature = "host")]
impl Api<'_, Request> {
pub fn encode(&self) -> Result<Box<[u8]>, Error> {
wasefire_wire::encode(self)
}
}
#[cfg(feature = "device")]
impl<'a> Api<'a, Request> {
pub fn decode(data: &'a [u8]) -> Result<Self, Error> {
wasefire_wire::decode(data)
}
}
impl<T: Service> ApiResult<'_, T> {
#[cfg(feature = "device")]
pub fn encode(&self) -> Result<Box<[u8]>, Error> {
wasefire_wire::encode(self)
}
#[cfg(feature = "host")]
pub fn decode(data: &[u8]) -> Result<ApiResult<'_, T>, Error> {
wasefire_wire::decode(data)
}
#[cfg(feature = "host")]
pub fn decode_yoke(data: Box<[u8]>) -> Result<Yoke<ApiResult<'static, T>>, Error> {
wasefire_wire::decode_yoke(data)
}
}
#[derive(Debug, Copy, Clone, Wire)]
#[cfg(any(feature = "host", feature = "_descriptor"))]
pub struct Versions {
pub min: u32,
pub max: Option<u32>,
}
#[cfg(feature = "host")]
impl Versions {
pub fn contains(&self, version: u32) -> Result<bool, Error> {
match (self.min, self.max) {
(min, Some(max)) => Ok((min ..= max).contains(&version)),
(min, None) if version < min => Ok(false),
(_, None) if VERSION < version => Err(Error::world(wasefire_error::Code::OutOfBounds)),
_ => Ok(true),
}
}
}
#[derive(Debug, Clone, Wire)]
#[cfg(feature = "_descriptor")]
pub struct Descriptor {
pub tag: u32,
pub versions: Versions,
}
#[cfg(feature = "_descriptor")]
impl core::fmt::Display for Descriptor {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let Descriptor { tag, versions: Versions { min, max } } = *self;
match max {
Some(max) => write!(f, "{tag} [{min} - {max}]"),
None => write!(f, "{tag} [{min} -]"),
}
}
}
#[cfg(all(test, feature = "serde"))]
fn _api_serde() {
fn is_serializable<T: serde::Serialize>() {}
fn is_deserializable_owned<T>()
where for<'a> T: serde::Deserialize<'a> {
}
is_serializable::<Api<Request>>();
is_deserializable_owned::<Api<Request>>();
is_serializable::<Api<Response>>();
is_deserializable_owned::<Api<Response>>();
}
macro_rules! api {
($(#![$api:meta])* $(
$(#[doc = $doc:literal])*
$tag:literal [$min:literal - $($max:literal)?] $Name:ident: $request:ty => $response:ty,
)* next $next:literal [$version:literal - ]) => {
$(#[$api])* #[derive(Debug, Wire)]
#[wire(static = T)]
#[cfg_attr(feature = "host", wire(range = $next))]
#[cfg_attr(not(feature = "_exhaustive"), non_exhaustive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Api<'a, T: sealed::Direction> {
$(
$(#[doc = $doc])* $(#[cfg(feature = "host")] ${ignore($max)})? #[wire(tag = $tag)]
$Name(T::Type<'a, $Name>),
)*
}
$(
$(#[doc = $doc])* $(#[cfg(feature = "host")] ${ignore($max)})? #[derive(Debug)]
pub enum $Name {}
$(#[cfg(feature = "host")] ${ignore($max)})?
impl Service for $Name {
#[cfg(feature = "host")]
const NAME: &str = stringify!($Name);
#[cfg(feature = "host")]
const VERSIONS: Versions = api!(versions $min $($max)?);
type Request<'a> = $request;
type Response<'a> = $response;
#[cfg(feature = "host")]
fn request(x: Self::Request<'_>) -> Api<'_, Request> { Api::$Name(x) }
#[cfg(feature = "serde")]
fn response(x: Self::Response<'_>) -> Api<'_, Response> {
#[allow(unreachable_code)] Api::$Name(x)
}
}
)*
pub const VERSION: u32 = $version - 1;
#[cfg(feature = "_descriptor")]
pub const DESCRIPTORS: &'static [Descriptor] = &[
$(
$(#[cfg(feature = "host")] ${ignore($max)})?
Descriptor { tag: $tag, versions: api!(versions $min $($max)?) },
)*
];
};
(max) => (None);
(max $max:literal) => (Some($max));
(versions $min:literal $($max:literal)?) => (Versions { min: $min, max: api!(max $($max)?) });
}
api! {
0 [0 -] ApiVersion: () => u32,
1 [0 -] AppletRequest: applet::Request<'a> => (),
2 [0 -] AppletResponse: applet::AppletId => Option<Cow<'a, [u8]>>,
3 [0 -] PlatformReboot: () => !,
4 [0 -] AppletTunnel: applet::Tunnel<'a> => (),
5 [1 - 4] _PlatformInfo0: () => platform::_Info0<'a>,
6 [2 -] PlatformVendor: Cow<'a, [u8]> => Cow<'a, [u8]>,
7 [3 - 4] _PlatformUpdateMetadata: () => Cow<'a, [u8]>,
8 [3 - 6] _PlatformUpdate0: transfer::_Request0<'a> => (),
9 [4 - 6] _AppletInstall0: transfer::_Request0<'a> => (),
10 [4 - 6] _AppletUninstall0: () => (),
11 [4 -] AppletExitStatus: applet::AppletId => Option<applet::ExitStatus>,
12 [4 -] PlatformLock: () => (),
13 [5 - 8] _PlatformInfo1: () => platform::_Info1<'a>,
14 [6 -] PlatformClearStore: usize => (),
15 [7 -] PlatformUpdate: transfer::Request<'a> => transfer::Response,
16 [7 - 10] _AppletInstall1: transfer::Request<'a> => transfer::Response,
17 [8 -] AppletReboot: applet::AppletId => (),
18 [9 - 9] _PlatformInfo2: () => platform::_Info2<'a>,
19 [10 -] PlatformInfo3: () => platform::Info3<'a>,
20 [11 -] AppletMetadata0: applet::AppletId => applet::Metadata0<'a>,
21 [11 -] AppletInstall2: transfer::Request<'a> => transfer::Response,
next 22 [12 -]
}