omnia_wasi_blobstore/
host.rs1mod blobstore_impl;
4mod container_impl;
5pub mod default_impl;
6mod resource;
7mod types_impl;
8
9mod generated {
10 #![allow(missing_docs)]
11
12 pub type Error = String;
13
14 pub use super::{ContainerProxy, IncomingValue, OutgoingValue, StreamObjectNames};
15
16 wasmtime::component::bindgen!({
17 world: "imports",
18 path: "wit",
19 imports: {
20 default: store | tracing | trappable,
21 },
22 with: {
23 "wasi:io": wasmtime_wasi::p2::bindings::io,
24 "wasi:blobstore/types.incoming-value": IncomingValue,
25 "wasi:blobstore/types.outgoing-value": OutgoingValue,
26 "wasi:blobstore/container.container": ContainerProxy,
27 "wasi:blobstore/container.stream-object-names": StreamObjectNames,
28 },
29 trappable_error_type: {
30 "wasi:blobstore/types.error" => Error,
31 },
32 });
33}
34
35use std::fmt::Debug;
36use std::sync::Arc;
37
38use bytes::Bytes;
39pub use omnia::FutureResult;
40use omnia::{Host, Server, State};
41pub use resource::*;
42use wasmtime::component::{HasData, Linker, ResourceTable};
43use wasmtime_wasi::p2::pipe::MemoryOutputPipe;
44
45pub use self::default_impl::BlobstoreDefault;
46pub use self::generated::wasi::blobstore::container::{ContainerMetadata, ObjectMetadata};
47pub use self::generated::wasi::blobstore::types::Error;
48use self::generated::wasi::blobstore::{blobstore, container, types};
49
50pub type IncomingValue = Bytes;
52#[derive(Debug, Clone)]
54pub struct OutgoingValue {
55 pub(crate) pipe: MemoryOutputPipe,
56 pub(crate) write_body_taken: bool,
57 pub(crate) finished: bool,
58}
59
60impl OutgoingValue {
61 #[must_use]
63 pub fn new(capacity: usize) -> Self {
64 Self {
65 pipe: MemoryOutputPipe::new(capacity),
66 write_body_taken: false,
67 finished: false,
68 }
69 }
70
71 pub(crate) const fn take_write_body(&mut self) -> std::result::Result<(), ()> {
72 if self.finished || self.write_body_taken {
73 return Err(());
74 }
75 self.write_body_taken = true;
76 Ok(())
77 }
78
79 pub(crate) const fn finalize(&mut self) -> std::result::Result<(), &'static str> {
80 if self.finished {
81 return Err("outgoing value already finished");
82 }
83 if !self.write_body_taken {
84 return Err("outgoing value write body was never requested");
85 }
86 self.finished = true;
87 Ok(())
88 }
89}
90pub struct StreamObjectNames {
92 pub(crate) names: Vec<String>,
94 pub(crate) offset: usize,
96}
97
98impl StreamObjectNames {
99 #[must_use]
101 pub const fn new(names: Vec<String>) -> Self {
102 Self { names, offset: 0 }
103 }
104}
105
106pub type Result<T> = anyhow::Result<T, Error>;
108
109#[derive(Debug)]
111pub struct WasiBlobstore;
112
113impl HasData for WasiBlobstore {
114 type Data<'a> = WasiBlobstoreCtxView<'a>;
115}
116
117impl<T> Host<T> for WasiBlobstore
118where
119 T: WasiBlobstoreView + 'static,
120{
121 fn add_to_linker(linker: &mut Linker<T>) -> anyhow::Result<()> {
122 blobstore::add_to_linker::<_, Self>(linker, T::blobstore)?;
123 container::add_to_linker::<_, Self>(linker, T::blobstore)?;
124 Ok(types::add_to_linker::<_, Self>(linker, T::blobstore)?)
125 }
126}
127
128impl<S> Server<S> for WasiBlobstore where S: State {}
129
130pub trait WasiBlobstoreView: Send {
135 fn blobstore(&mut self) -> WasiBlobstoreCtxView<'_>;
137}
138
139pub struct WasiBlobstoreCtxView<'a> {
141 pub ctx: &'a mut dyn WasiBlobstoreCtx,
143
144 pub table: &'a mut ResourceTable,
146}
147
148pub trait WasiBlobstoreCtx: Debug + Send + Sync + 'static {
153 fn create_container(&self, name: String) -> FutureResult<Arc<dyn Container>>;
155
156 fn get_container(&self, name: String) -> FutureResult<Arc<dyn Container>>;
158
159 fn delete_container(&self, name: String) -> FutureResult<()>;
161
162 fn container_exists(&self, name: String) -> FutureResult<bool>;
164}
165
166#[macro_export]
168macro_rules! omnia_wasi_view {
169 ($store_ctx:ty, $field_name:ident) => {
170 impl omnia_wasi_blobstore::WasiBlobstoreView for $store_ctx {
171 fn blobstore(&mut self) -> omnia_wasi_blobstore::WasiBlobstoreCtxView<'_> {
172 omnia_wasi_blobstore::WasiBlobstoreCtxView {
173 ctx: &mut self.$field_name,
174 table: &mut self.table,
175 }
176 }
177 }
178 };
179}
180
181#[cfg(test)]
202mod tests {
203 use super::OutgoingValue;
204
205 #[test]
206 fn outgoing_value_write_body_is_one_shot() {
207 let mut outgoing = OutgoingValue::new(16);
208 assert_eq!(outgoing.take_write_body(), Ok(()));
209 assert_eq!(outgoing.take_write_body(), Err(()));
210 }
211
212 #[test]
213 fn outgoing_value_finish_requires_write_body() {
214 let mut outgoing = OutgoingValue::new(16);
215 assert_eq!(outgoing.finalize(), Err("outgoing value write body was never requested"));
216 }
217
218 #[test]
219 fn outgoing_value_finish_is_single_use() {
220 let mut outgoing = OutgoingValue::new(16);
221 assert_eq!(outgoing.take_write_body(), Ok(()));
222 assert_eq!(outgoing.finalize(), Ok(()));
223 assert_eq!(outgoing.finalize(), Err("outgoing value already finished"));
224 }
225}