Skip to main content

omnia_wasi_messaging/
host.rs

1pub mod default_impl;
2mod producer_impl;
3mod request_reply_impl;
4mod resource;
5mod server;
6mod types_impl;
7
8mod generated {
9    #![allow(missing_docs)]
10
11    pub use wasi::messaging::types::Error;
12
13    pub use crate::host::resource::{ClientProxy, MessageProxy, RequestOptions};
14
15    wasmtime::component::bindgen!({
16        world: "messaging-request-reply",
17        path: "wit",
18        imports: {
19            // "wasi:messaging/types.[static]client.connect": store | tracing | trappable,
20            default:  store | tracing | trappable,
21        },
22        exports: {
23            default: store | tracing | trappable,
24        },
25        with: {
26            "wasi:messaging/request-reply.request-options": RequestOptions,
27            "wasi:messaging/types.client": ClientProxy,
28            "wasi:messaging/types.message": MessageProxy,
29        },
30        trappable_error_type: {
31            "wasi:messaging/types.error" => Error,
32        },
33        // include_generated_code_from_file: true,
34    });
35}
36
37use std::fmt::Debug;
38use std::sync::Arc;
39
40pub use omnia::FutureResult;
41use omnia::{Host, Server, State};
42use wasmtime::component::{HasData, Linker};
43use wasmtime_wasi::{ResourceTable, ResourceTableError};
44
45pub use self::default_impl::MessagingDefault;
46pub use self::generated::MessagingRequestReply;
47pub use self::generated::wasi::messaging::types::Error;
48use self::generated::wasi::messaging::{producer, request_reply, types};
49pub use self::resource::*;
50
51/// Result type for messaging operations.
52pub type Result<T, E = Error> = anyhow::Result<T, E>;
53
54/// Host-side service for `wasi:messaging`.
55#[derive(Debug)]
56pub struct WasiMessaging;
57
58impl HasData for WasiMessaging {
59    type Data<'a> = WasiMessagingCtxView<'a>;
60}
61
62impl<T> Host<T> for WasiMessaging
63where
64    T: WasiMessagingView + 'static,
65{
66    fn add_to_linker(linker: &mut Linker<T>) -> anyhow::Result<()> {
67        producer::add_to_linker::<_, Self>(linker, T::messaging)?;
68        request_reply::add_to_linker::<_, Self>(linker, T::messaging)?;
69        Ok(types::add_to_linker::<_, Self>(linker, T::messaging)?)
70    }
71}
72
73impl<S> Server<S> for WasiMessaging
74where
75    S: State,
76    S::StoreCtx: WasiMessagingView,
77{
78    async fn run(&self, state: &S) -> anyhow::Result<()> {
79        server::run(state).await
80    }
81}
82
83/// A trait which provides internal WASI Messaging state.
84///
85/// This is implemented by the `T` in `Linker<T>` — a single type shared across
86/// all WASI components for the runtime build.
87pub trait WasiMessagingView: Send {
88    /// Return a [`WasiMessagingCtxView`] from mutable reference to self.
89    fn messaging(&mut self) -> WasiMessagingCtxView<'_>;
90}
91
92/// View into [`WasiMessagingCtx`] implementation and [`ResourceTable`].
93pub struct WasiMessagingCtxView<'a> {
94    /// Mutable reference to the WASI Messaging context.
95    pub ctx: &'a mut dyn WasiMessagingCtx,
96
97    /// Mutable reference to table used to manage resources.
98    pub table: &'a mut ResourceTable,
99}
100
101/// A trait which provides internal WASI Messaging context.
102///
103/// This is implemented by the resource-specific provider of messaging
104/// functionality. For example, a NATS, or a Kafka broker.
105#[allow(unused)]
106pub trait WasiMessagingCtx: Debug + Send + Sync + 'static {
107    /// Connect to the messaging system and return a client proxy.
108    ///
109    /// # Errors
110    ///
111    /// Returns an error if the connection fails.
112    fn connect(&self) -> FutureResult<Arc<dyn Client>>;
113
114    /// Create a new message with the given payload.
115    ///
116    /// # Errors
117    ///
118    /// Returns an error if message creation fails.
119    fn new_message(&self, data: Vec<u8>) -> anyhow::Result<Arc<dyn Message>>;
120
121    /// Set the content-type on a message.
122    ///
123    /// # Errors
124    ///
125    /// Returns an error if the content-type setting fails.
126    fn set_content_type(
127        &self, message: Arc<dyn Message>, content_type: String,
128    ) -> anyhow::Result<Arc<dyn Message>>;
129
130    /// Set the payload on a message.
131    ///
132    /// # Errors
133    ///
134    /// Returns an error if the payload setting fails.
135    fn set_payload(
136        &self, message: Arc<dyn Message>, data: Vec<u8>,
137    ) -> anyhow::Result<Arc<dyn Message>>;
138
139    /// Append a key-value pair to the metadata of a message.
140    ///
141    /// # Errors
142    ///
143    /// Returns an error if the metadata addition fails.
144    fn add_metadata(
145        &self, message: Arc<dyn Message>, key: String, value: String,
146    ) -> anyhow::Result<Arc<dyn Message>>;
147
148    /// Set all the metadata on a message.
149    ///
150    /// # Errors
151    ///
152    /// Returns an error if the metadata setting fails.
153    fn set_metadata(
154        &self, message: Arc<dyn Message>, metadata: Metadata,
155    ) -> anyhow::Result<Arc<dyn Message>>;
156
157    /// Remove a key-value pair from the metadata of a message.
158    ///
159    /// # Errors
160    ///
161    /// Returns an error if the metadata removal fails.
162    fn remove_metadata(
163        &self, message: Arc<dyn Message>, key: String,
164    ) -> anyhow::Result<Arc<dyn Message>>;
165}
166
167/// `anyhow::Error` to `Error` mapping
168impl From<anyhow::Error> for Error {
169    fn from(err: anyhow::Error) -> Self {
170        Self::Other(err.to_string())
171    }
172}
173
174/// `ResourceTableError` to `Error` mapping
175impl From<ResourceTableError> for Error {
176    fn from(err: ResourceTableError) -> Self {
177        Self::Other(err.to_string())
178    }
179}
180
181/// `wasmtime::Error` to `Error` mapping
182impl From<wasmtime::Error> for Error {
183    fn from(err: wasmtime::Error) -> Self {
184        Self::Other(err.to_string())
185    }
186}
187
188/// Implementation of the `WasiMessagingView` trait for the store context.
189#[macro_export]
190macro_rules! omnia_wasi_view {
191    ($store_ctx:ty, $field_name:ident) => {
192        impl omnia_wasi_messaging::WasiMessagingView for $store_ctx {
193            fn messaging(&mut self) -> omnia_wasi_messaging::WasiMessagingCtxView<'_> {
194                omnia_wasi_messaging::WasiMessagingCtxView {
195                    ctx: &mut self.$field_name,
196                    table: &mut self.table,
197                }
198            }
199        }
200    };
201}