use uuid::Uuid;
use maf_schemas::apps;
use crate::{
app::hooks::{self, HookBody, HookRequestError, HookRequestInit},
bindings::bindgen,
platform::{ListenError, Platform, PlatformHookRequest, PlatformUser, SendError},
tasks,
user::{UserMeta, UserNextMessageError},
};
pub struct WasiPlatform {
future_user: bindgen::FutureUser,
future_hook_request: bindgen::FutureHookRequest,
}
impl Platform for WasiPlatform {
type Config = ();
fn init(_config: Self::Config) -> anyhow::Result<Self> {
Ok(Self {
future_user: bindgen::listen_user()?,
future_hook_request: bindgen::listen_hook_request()?,
})
}
async fn next_user(&self) -> Result<RawUser, ListenError> {
let pollable = self.future_user.subscribe()?;
tasks::wait_for(pollable).await;
Ok(RawUser::new(self.future_user.get()?))
}
async fn next_hook_request(&self) -> Result<RawHookRequest, ListenError> {
let pollable = self.future_hook_request.subscribe()?;
tasks::wait_for(pollable).await;
Ok(RawHookRequest::new(self.future_hook_request.get()?))
}
fn report_app_schema(&self, schema: &str) {
bindgen::report_app_schema(schema);
}
fn set_meta(
&self,
visibility: apps::MetaVisibility,
key: &str,
value: &str,
) -> Option<apps::MetaEntry> {
bindgen::set_meta(visibility.into(), key, value).map(|entry| entry.into())
}
fn get_meta(&self, key: &str) -> Option<apps::MetaEntry> {
bindgen::get_meta(key).map(|entry| entry.into())
}
fn delete_meta(&self, key: &str) -> Option<apps::MetaEntry> {
bindgen::delete_meta(key).map(|entry| entry.into())
}
fn list_meta(&self) -> Vec<(String, apps::MetaEntry)> {
bindgen::list_meta()
.into_iter()
.map(|(k, v)| (k, v.into()))
.collect()
}
}
#[derive(Debug)]
pub struct RawUser {
inner: bindgen::User,
messages: bindgen::FutureMessage,
}
impl RawUser {
pub fn new(raw: bindgen::User) -> Self {
Self {
messages: raw.listen_message().expect("Failed to listen for messages"),
inner: raw,
}
}
}
impl PlatformUser for RawUser {
fn meta(&self) -> UserMeta {
let bindings_meta = self.inner.meta();
UserMeta {
id: Uuid::from_u64_pair(bindings_meta.id.0, bindings_meta.id.1),
auth: match bindings_meta.auth {
Some(auth_str) => Some(
serde_json::from_str(&auth_str)
.expect("Failed to deserialize user auth metadata"),
),
None => None,
},
}
}
fn send(&self, message: super::Message) -> Result<(), super::SendError> {
self.inner.send(&message.into()).map_err(SendError::from)
}
async fn next_message(&self) -> Result<super::Message, UserNextMessageError> {
tasks::wait_for(
self.messages
.subscribe()
.map_err(|e| UserNextMessageError::Listen(e.into()))?,
)
.await;
let message = self
.messages
.get()
.map_err(|e| UserNextMessageError::Listen(e.into()))?;
Ok(super::Message::from(message))
}
}
#[derive(Debug)]
pub struct RawHookRequest {
inner: bindgen::HookRequest,
}
impl RawHookRequest {
pub fn new(raw: bindgen::HookRequest) -> Self {
Self { inner: raw }
}
}
impl PlatformHookRequest for RawHookRequest {
fn init(&self) -> Result<HookRequestInit, HookRequestError> {
self.inner
.init()
.map(|init| HookRequestInit {
caller: init.caller.into(),
method: init.method,
data: init.data.into(),
})
.map_err(Into::into)
}
fn respond(&self, body: HookBody) -> Result<(), SendError> {
self.inner.respond(&body.into()).map_err(SendError::from)
}
}
mod conversion_impls {
use super::*;
use crate::platform;
impl From<bindgen::Message> for platform::Message {
fn from(value: bindgen::Message) -> Self {
match value {
bindgen::Message::Text(text) => platform::Message::Text(text),
bindgen::Message::Binary(data) => platform::Message::Binary(data),
}
}
}
impl From<platform::Message> for bindgen::Message {
fn from(value: platform::Message) -> Self {
match value {
platform::Message::Text(text) => bindgen::Message::Text(text),
platform::Message::Binary(data) => bindgen::Message::Binary(data),
}
}
}
impl From<bindgen::SendError> for platform::SendError {
fn from(value: bindgen::SendError) -> Self {
match value {
bindgen::SendError::Closed => super::SendError::Closed,
bindgen::SendError::BufferFull => super::SendError::BufferFull,
}
}
}
impl From<bindgen::ListenError> for platform::ListenError {
fn from(value: bindgen::ListenError) -> Self {
match value {
bindgen::ListenError::Closed => super::ListenError::Closed,
bindgen::ListenError::NotReady => super::ListenError::NotReady,
bindgen::ListenError::AlreadyListening => super::ListenError::AlreadyListening,
}
}
}
impl From<bindgen::HookBody> for HookBody {
fn from(value: bindgen::HookBody) -> Self {
match value {
bindgen::HookBody::Json(text) => HookBody::Json(text),
bindgen::HookBody::None => HookBody::None,
}
}
}
impl From<HookBody> for bindgen::HookBody {
fn from(value: HookBody) -> Self {
match value {
HookBody::Json(text) => bindgen::HookBody::Json(text),
HookBody::None => bindgen::HookBody::None,
}
}
}
impl From<bindgen::HookRequestCaller> for hooks::HookRequestCaller {
fn from(value: bindgen::HookRequestCaller) -> Self {
match value {
bindgen::HookRequestCaller::Service => hooks::HookRequestCaller::Service,
}
}
}
impl From<bindgen::HookRequestError> for hooks::HookRequestError {
fn from(value: bindgen::HookRequestError) -> Self {
match value {
bindgen::HookRequestError::InitConsumed => hooks::HookRequestError::InitConsumed,
}
}
}
impl From<bindgen::MetaVisibility> for apps::MetaVisibility {
fn from(value: bindgen::MetaVisibility) -> Self {
match value {
bindgen::MetaVisibility::Public => apps::MetaVisibility::Public,
bindgen::MetaVisibility::Private => apps::MetaVisibility::Private,
}
}
}
impl From<apps::MetaVisibility> for bindgen::MetaVisibility {
fn from(value: apps::MetaVisibility) -> Self {
match value {
apps::MetaVisibility::Public => bindgen::MetaVisibility::Public,
apps::MetaVisibility::Private => bindgen::MetaVisibility::Private,
}
}
}
impl From<bindgen::MetaEntry> for apps::MetaEntry {
fn from(value: bindgen::MetaEntry) -> Self {
apps::MetaEntry {
visibility: value.visibility.into(),
value: value.value,
}
}
}
impl From<apps::MetaEntry> for bindgen::MetaEntry {
fn from(value: apps::MetaEntry) -> Self {
bindgen::MetaEntry {
visibility: value.visibility.into(),
value: value.value,
}
}
}
}