wasmer_vbus/
lib.rs

1use std::fmt;
2use std::pin::Pin;
3use std::task::{Context, Poll};
4use thiserror::Error;
5
6pub use wasmer_vfs::FileDescriptor;
7pub use wasmer_vfs::StdioMode;
8
9pub type Result<T> = std::result::Result<T, BusError>;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
12#[repr(transparent)]
13pub struct CallDescriptor(u32);
14
15impl CallDescriptor {
16    pub fn raw(&self) -> u32 {
17        self.0
18    }
19}
20
21impl From<u32> for CallDescriptor {
22    fn from(a: u32) -> Self {
23        Self(a)
24    }
25}
26
27pub trait VirtualBus: fmt::Debug + Send + Sync + 'static {
28    /// Starts a new WAPM sub process
29    fn new_spawn(&self) -> SpawnOptions;
30
31    /// Creates a listener thats used to receive BUS commands
32    fn listen(&self) -> Result<Box<dyn VirtualBusListener + Sync>>;
33}
34
35pub trait VirtualBusSpawner {
36    /// Spawns a new WAPM process by its name
37    fn spawn(&mut self, name: &str, config: &SpawnOptionsConfig) -> Result<BusSpawnedProcess>;
38}
39
40#[derive(Debug, Clone)]
41pub struct SpawnOptionsConfig {
42    reuse: bool,
43    chroot: bool,
44    args: Vec<String>,
45    preopen: Vec<String>,
46    stdin_mode: StdioMode,
47    stdout_mode: StdioMode,
48    stderr_mode: StdioMode,
49    working_dir: String,
50    remote_instance: Option<String>,
51    access_token: Option<String>,
52}
53
54impl SpawnOptionsConfig {
55    pub const fn reuse(&self) -> bool {
56        self.reuse
57    }
58
59    pub const fn chroot(&self) -> bool {
60        self.chroot
61    }
62
63    pub const fn args(&self) -> &Vec<String> {
64        &self.args
65    }
66
67    pub const fn preopen(&self) -> &Vec<String> {
68        &self.preopen
69    }
70
71    pub const fn stdin_mode(&self) -> StdioMode {
72        self.stdin_mode
73    }
74
75    pub const fn stdout_mode(&self) -> StdioMode {
76        self.stdout_mode
77    }
78
79    pub const fn stderr_mode(&self) -> StdioMode {
80        self.stderr_mode
81    }
82
83    pub fn working_dir(&self) -> &str {
84        self.working_dir.as_str()
85    }
86
87    pub fn remote_instance(&self) -> Option<&str> {
88        self.remote_instance.as_deref()
89    }
90
91    pub fn access_token(&self) -> Option<&str> {
92        self.access_token.as_deref()
93    }
94}
95
96pub struct SpawnOptions {
97    spawner: Box<dyn VirtualBusSpawner>,
98    conf: SpawnOptionsConfig,
99}
100
101impl SpawnOptions {
102    pub fn new(spawner: Box<dyn VirtualBusSpawner>) -> Self {
103        Self {
104            spawner,
105            conf: SpawnOptionsConfig {
106                reuse: false,
107                chroot: false,
108                args: Vec::new(),
109                preopen: Vec::new(),
110                stdin_mode: StdioMode::Null,
111                stdout_mode: StdioMode::Null,
112                stderr_mode: StdioMode::Null,
113                working_dir: "/".to_string(),
114                remote_instance: None,
115                access_token: None,
116            },
117        }
118    }
119    pub fn options(&mut self, options: SpawnOptionsConfig) -> &mut Self {
120        self.conf = options;
121        self
122    }
123
124    pub fn reuse(&mut self, reuse: bool) -> &mut Self {
125        self.conf.reuse = reuse;
126        self
127    }
128
129    pub fn chroot(&mut self, chroot: bool) -> &mut Self {
130        self.conf.chroot = chroot;
131        self
132    }
133
134    pub fn args(&mut self, args: Vec<String>) -> &mut Self {
135        self.conf.args = args;
136        self
137    }
138
139    pub fn preopen(&mut self, preopen: Vec<String>) -> &mut Self {
140        self.conf.preopen = preopen;
141        self
142    }
143
144    pub fn stdin_mode(&mut self, stdin_mode: StdioMode) -> &mut Self {
145        self.conf.stdin_mode = stdin_mode;
146        self
147    }
148
149    pub fn stdout_mode(&mut self, stdout_mode: StdioMode) -> &mut Self {
150        self.conf.stdout_mode = stdout_mode;
151        self
152    }
153
154    pub fn stderr_mode(&mut self, stderr_mode: StdioMode) -> &mut Self {
155        self.conf.stderr_mode = stderr_mode;
156        self
157    }
158
159    pub fn working_dir(&mut self, working_dir: String) -> &mut Self {
160        self.conf.working_dir = working_dir;
161        self
162    }
163
164    pub fn remote_instance(&mut self, remote_instance: String) -> &mut Self {
165        self.conf.remote_instance = Some(remote_instance);
166        self
167    }
168
169    pub fn access_token(&mut self, access_token: String) -> &mut Self {
170        self.conf.access_token = Some(access_token);
171        self
172    }
173
174    /// Spawns a new bus instance by its reference name
175    pub fn spawn(&mut self, name: &str) -> Result<BusSpawnedProcess> {
176        self.spawner.spawn(name, &self.conf)
177    }
178}
179
180#[derive(Debug)]
181pub struct BusSpawnedProcess {
182    /// Reference to the spawned instance
183    pub inst: Box<dyn VirtualBusProcess + Sync>,
184}
185
186pub trait VirtualBusScope: fmt::Debug + Send + Sync + 'static {
187    //// Returns true if the invokable target has finished
188    fn poll_finished(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()>;
189}
190
191pub trait VirtualBusInvokable: fmt::Debug + Send + Sync + 'static {
192    /// Invokes a service within this instance
193    fn invoke(
194        &self,
195        topic: String,
196        format: BusDataFormat,
197        buf: &[u8],
198    ) -> Result<Box<dyn VirtualBusInvocation + Sync>>;
199}
200
201pub trait VirtualBusProcess:
202    VirtualBusScope + VirtualBusInvokable + fmt::Debug + Send + Sync + 'static
203{
204    /// Returns the exit code if the instance has finished
205    fn exit_code(&self) -> Option<u32>;
206
207    /// Returns a file descriptor used to read the STDIN
208    fn stdin_fd(&self) -> Option<FileDescriptor>;
209
210    /// Returns a file descriptor used to write to STDOUT
211    fn stdout_fd(&self) -> Option<FileDescriptor>;
212
213    /// Returns a file descriptor used to write to STDERR
214    fn stderr_fd(&self) -> Option<FileDescriptor>;
215}
216
217pub trait VirtualBusInvocation:
218    VirtualBusScope + VirtualBusInvokable + fmt::Debug + Send + Sync + 'static
219{
220    /// Polls for new listen events related to this context
221    fn poll_event(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<BusInvocationEvent>;
222}
223
224#[derive(Debug)]
225pub enum BusInvocationEvent {
226    /// The server has sent some out-of-band data to you
227    Callback {
228        /// Topic that this call relates to
229        topic: String,
230        /// Format of the data we received
231        format: BusDataFormat,
232        /// Data passed in the call
233        data: Vec<u8>,
234    },
235    /// The service has a responded to your call
236    Response {
237        /// Format of the data we received
238        format: BusDataFormat,
239        /// Data returned by the call
240        data: Vec<u8>,
241    },
242}
243
244pub trait VirtualBusListener: fmt::Debug + Send + Sync + 'static {
245    /// Polls for new calls to this service
246    fn poll_call(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<BusCallEvent>;
247}
248
249#[derive(Debug)]
250pub struct BusCallEvent {
251    /// Topic that this call relates to
252    pub topic: String,
253    /// Reference to the call itself
254    pub called: Box<dyn VirtualBusCalled + Sync>,
255    /// Format of the data we received
256    pub format: BusDataFormat,
257    /// Data passed in the call
258    pub data: Vec<u8>,
259}
260
261pub trait VirtualBusCalled: VirtualBusListener + fmt::Debug + Send + Sync + 'static {
262    /// Sends an out-of-band message back to the caller
263    fn callback(&self, topic: String, format: BusDataFormat, buf: &[u8]) -> Result<()>;
264
265    /// Informs the caller that their call has failed
266    fn fault(self, fault: BusError) -> Result<()>;
267
268    /// Finishes the call and returns a particular response
269    fn reply(self, format: BusDataFormat, buf: &[u8]) -> Result<()>;
270}
271
272/// Format that the supplied data is in
273#[derive(Debug, Copy, Clone, PartialEq, Eq)]
274pub enum BusDataFormat {
275    Raw,
276    Bincode,
277    MessagePack,
278    Json,
279    Yaml,
280    Xml,
281}
282
283#[derive(Debug, Default)]
284pub struct UnsupportedVirtualBus {}
285
286impl VirtualBus for UnsupportedVirtualBus {
287    fn new_spawn(&self) -> SpawnOptions {
288        SpawnOptions::new(Box::new(UnsupportedVirtualBusSpawner::default()))
289    }
290
291    fn listen(&self) -> Result<Box<dyn VirtualBusListener + Sync>> {
292        Err(BusError::Unsupported)
293    }
294}
295
296#[derive(Debug, Default)]
297pub struct UnsupportedVirtualBusSpawner {}
298
299impl VirtualBusSpawner for UnsupportedVirtualBusSpawner {
300    fn spawn(&mut self, _name: &str, _config: &SpawnOptionsConfig) -> Result<BusSpawnedProcess> {
301        Err(BusError::Unsupported)
302    }
303}
304
305#[derive(Error, Copy, Clone, Debug, PartialEq, Eq)]
306pub enum BusError {
307    /// Failed during serialization
308    #[error("serialization failed")]
309    Serialization,
310    /// Failed during deserialization
311    #[error("deserialization failed")]
312    Deserialization,
313    /// Invalid WAPM process
314    #[error("invalid wapm")]
315    InvalidWapm,
316    /// Failed to fetch the WAPM process
317    #[error("fetch failed")]
318    FetchFailed,
319    /// Failed to compile the WAPM process
320    #[error("compile error")]
321    CompileError,
322    /// Invalid ABI
323    #[error("WAPM process has an invalid ABI")]
324    InvalidABI,
325    /// Call was aborted
326    #[error("call aborted")]
327    Aborted,
328    /// Bad handle
329    #[error("bad handle")]
330    BadHandle,
331    /// Invalid topic
332    #[error("invalid topic")]
333    InvalidTopic,
334    /// Invalid callback
335    #[error("invalid callback")]
336    BadCallback,
337    /// Call is unsupported
338    #[error("unsupported")]
339    Unsupported,
340    /// Bad request
341    #[error("bad request")]
342    BadRequest,
343    /// Access denied
344    #[error("access denied")]
345    AccessDenied,
346    /// Internal error has occured
347    #[error("internal error")]
348    InternalError,
349    /// Memory allocation failed
350    #[error("memory allocation failed")]
351    MemoryAllocationFailed,
352    /// Invocation has failed
353    #[error("invocation has failed")]
354    InvokeFailed,
355    /// Already consumed
356    #[error("already consumed")]
357    AlreadyConsumed,
358    /// Memory access violation
359    #[error("memory access violation")]
360    MemoryAccessViolation,
361    /// Some other unhandled error. If you see this, it's probably a bug.
362    #[error("unknown error found")]
363    UnknownError,
364}