1use std::collections::BTreeMap;
2use std::path::{Path, PathBuf};
3
4#[deprecated]
5pub type ManifestMemory = MemoryOptions;
6
7#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
9#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
10#[serde(deny_unknown_fields)]
11pub struct MemoryOptions {
12 pub max_pages: Option<u32>,
14
15 #[serde(default)]
17 pub max_http_response_bytes: Option<u64>,
18
19 #[serde(default = "default_var_bytes")]
22 pub max_var_bytes: Option<u64>,
23}
24
25impl MemoryOptions {
26 pub fn new() -> Self {
28 Default::default()
29 }
30
31 pub fn with_max_pages(mut self, pages: u32) -> Self {
33 self.max_pages = Some(pages);
34 self
35 }
36
37 pub fn with_max_http_response_bytes(mut self, bytes: u64) -> Self {
39 self.max_http_response_bytes = Some(bytes);
40 self
41 }
42
43 pub fn with_max_var_bytes(mut self, bytes: u64) -> Self {
45 self.max_var_bytes = Some(bytes);
46 self
47 }
48}
49
50fn default_var_bytes() -> Option<u64> {
51 Some(1024 * 1024)
52}
53
54#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
56#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
57#[serde(deny_unknown_fields)]
58pub struct HttpRequest {
59 pub url: String,
61
62 #[serde(default)]
64 pub headers: std::collections::BTreeMap<String, String>,
65
66 pub method: Option<String>,
68}
69
70impl HttpRequest {
71 pub fn new(url: impl Into<String>) -> HttpRequest {
73 HttpRequest {
74 url: url.into(),
75 headers: Default::default(),
76 method: None,
77 }
78 }
79
80 pub fn with_method(mut self, method: impl Into<String>) -> HttpRequest {
82 self.method = Some(method.into());
83 self
84 }
85
86 pub fn with_header(mut self, key: impl Into<String>, value: impl Into<String>) -> HttpRequest {
88 self.headers.insert(key.into(), value.into());
89 self
90 }
91}
92
93#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
95#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
96#[serde(deny_unknown_fields)]
97pub struct WasmMetadata {
98 pub name: Option<String>,
100
101 pub hash: Option<String>,
103}
104
105impl From<HttpRequest> for Wasm {
106 fn from(req: HttpRequest) -> Self {
107 Wasm::Url {
108 req,
109 meta: WasmMetadata::default(),
110 }
111 }
112}
113
114impl From<std::path::PathBuf> for Wasm {
115 fn from(path: std::path::PathBuf) -> Self {
116 Wasm::File {
117 path,
118 meta: WasmMetadata::default(),
119 }
120 }
121}
122
123impl From<Vec<u8>> for Wasm {
124 fn from(data: Vec<u8>) -> Self {
125 Wasm::Data {
126 data,
127 meta: WasmMetadata::default(),
128 }
129 }
130}
131
132#[deprecated]
133pub type ManifestWasm = Wasm;
134
135#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
137#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
138#[serde(untagged)]
139#[serde(deny_unknown_fields)]
140pub enum Wasm {
141 File {
143 path: PathBuf,
144 #[serde(flatten)]
145 meta: WasmMetadata,
146 },
147
148 Data {
150 #[serde(with = "wasmdata")]
151 #[cfg_attr(feature = "json_schema", schemars(schema_with = "wasmdata_schema"))]
152 data: Vec<u8>,
153 #[serde(flatten)]
154 meta: WasmMetadata,
155 },
156
157 Url {
159 #[serde(flatten)]
160 req: HttpRequest,
161 #[serde(flatten)]
162 meta: WasmMetadata,
163 },
164}
165
166impl Wasm {
167 pub fn file(path: impl AsRef<std::path::Path>) -> Self {
169 Wasm::File {
170 path: path.as_ref().to_path_buf(),
171 meta: Default::default(),
172 }
173 }
174
175 pub fn data(data: impl Into<Vec<u8>>) -> Self {
177 Wasm::Data {
178 data: data.into(),
179 meta: Default::default(),
180 }
181 }
182
183 pub fn url(url: impl Into<String>) -> Self {
185 Wasm::Url {
186 req: HttpRequest {
187 url: url.into(),
188 headers: Default::default(),
189 method: None,
190 },
191 meta: Default::default(),
192 }
193 }
194
195 pub fn http(req: impl Into<HttpRequest>) -> Self {
197 Wasm::Url {
198 req: req.into(),
199 meta: Default::default(),
200 }
201 }
202
203 pub fn meta(&self) -> &WasmMetadata {
205 match self {
206 Wasm::File { path: _, meta } => meta,
207 Wasm::Data { data: _, meta } => meta,
208 Wasm::Url { req: _, meta } => meta,
209 }
210 }
211
212 pub fn meta_mut(&mut self) -> &mut WasmMetadata {
214 match self {
215 Wasm::File { path: _, meta } => meta,
216 Wasm::Data { data: _, meta } => meta,
217 Wasm::Url { req: _, meta } => meta,
218 }
219 }
220
221 pub fn with_name(mut self, name: impl Into<String>) -> Self {
223 self.meta_mut().name = Some(name.into());
224 self
225 }
226
227 pub fn with_hash(mut self, hash: impl Into<String>) -> Self {
229 self.meta_mut().hash = Some(hash.into());
230 self
231 }
232}
233
234#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
235#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
236#[serde(deny_unknown_fields)]
237struct DataPtrLength {
238 ptr: u64,
239 len: u64,
240}
241
242#[cfg(feature = "json_schema")]
243fn wasmdata_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
244 use schemars::{schema::SchemaObject, JsonSchema};
245 let mut schema: SchemaObject = <String>::json_schema(gen).into();
246 let objschema: SchemaObject = <DataPtrLength>::json_schema(gen).into();
247 let types = schemars::schema::SingleOrVec::<schemars::schema::InstanceType>::Vec(vec![
248 schemars::schema::InstanceType::String,
249 schemars::schema::InstanceType::Object,
250 ]);
251 schema.instance_type = Some(types);
252 schema.object = objschema.object;
253 schema.into()
254}
255
256#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
258#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
259#[serde(deny_unknown_fields)]
260pub struct Manifest {
261 #[serde(default)]
263 pub wasm: Vec<Wasm>,
264
265 #[serde(default)]
267 pub memory: MemoryOptions,
268
269 #[serde(default)]
271 pub config: BTreeMap<String, String>,
272
273 #[serde(default)]
274 pub allowed_hosts: Option<Vec<String>>,
277
278 #[serde(default)]
282 pub allowed_paths: Option<BTreeMap<String, PathBuf>>,
283
284 #[serde(default)]
286 pub timeout_ms: Option<u64>,
287}
288
289impl Manifest {
290 pub fn new(wasm: impl IntoIterator<Item = impl Into<Wasm>>) -> Manifest {
292 Manifest {
293 wasm: wasm.into_iter().map(|x| x.into()).collect(),
294 ..Default::default()
295 }
296 }
297
298 pub fn with_wasm(mut self, wasm: impl Into<Wasm>) -> Self {
299 self.wasm.push(wasm.into());
300 self
301 }
302
303 pub fn disallow_all_hosts(mut self) -> Self {
305 self.allowed_hosts = Some(vec![]);
306 self
307 }
308
309 pub fn with_memory_options(mut self, memory: MemoryOptions) -> Self {
311 self.memory = memory;
312 self
313 }
314
315 pub fn with_memory_max(mut self, max: u32) -> Self {
317 self.memory.max_pages = Some(max);
318 self
319 }
320
321 pub fn with_allowed_host(mut self, host: impl Into<String>) -> Self {
323 match &mut self.allowed_hosts {
324 Some(h) => {
325 h.push(host.into());
326 }
327 None => self.allowed_hosts = Some(vec![host.into()]),
328 }
329
330 self
331 }
332
333 pub fn with_allowed_hosts(mut self, hosts: impl Iterator<Item = String>) -> Self {
335 self.allowed_hosts = Some(hosts.collect());
336 self
337 }
338
339 pub fn with_allowed_path(mut self, src: String, dest: impl AsRef<Path>) -> Self {
341 let dest = dest.as_ref().to_path_buf();
342 match &mut self.allowed_paths {
343 Some(p) => {
344 p.insert(src, dest);
345 }
346 None => {
347 let mut p = BTreeMap::new();
348 p.insert(src, dest);
349 self.allowed_paths = Some(p);
350 }
351 }
352
353 self
354 }
355
356 pub fn with_allowed_paths(mut self, paths: impl Iterator<Item = (String, PathBuf)>) -> Self {
358 self.allowed_paths = Some(paths.collect());
359 self
360 }
361
362 pub fn with_config(
364 mut self,
365 c: impl Iterator<Item = (impl Into<String>, impl Into<String>)>,
366 ) -> Self {
367 for (k, v) in c {
368 self.config.insert(k.into(), v.into());
369 }
370 self
371 }
372
373 pub fn with_config_key(mut self, k: impl Into<String>, v: impl Into<String>) -> Self {
375 self.config.insert(k.into(), v.into());
376 self
377 }
378
379 pub fn with_timeout(mut self, timeout: std::time::Duration) -> Self {
383 self.timeout_ms = Some(timeout.as_millis() as u64);
384 self
385 }
386}
387
388mod wasmdata {
389 use crate::DataPtrLength;
390 use base64::{engine::general_purpose, Engine as _};
391 use serde::{Deserialize, Serialize};
392 use serde::{Deserializer, Serializer};
393 use std::slice;
394
395 pub fn serialize<S: Serializer>(v: &Vec<u8>, s: S) -> Result<S::Ok, S::Error> {
396 let base64 = general_purpose::STANDARD.encode(v.as_slice());
397 String::serialize(&base64, s)
398 }
399
400 pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
401 #[derive(Deserialize)]
402 #[serde(untagged)]
403 enum WasmDataTypes {
404 String(String),
405 DataPtrLength(DataPtrLength),
406 }
407 Ok(match WasmDataTypes::deserialize(d)? {
408 WasmDataTypes::String(string) => general_purpose::STANDARD
409 .decode(string.as_bytes())
410 .map_err(serde::de::Error::custom)?,
411 WasmDataTypes::DataPtrLength(ptrlen) => {
412 let slice =
413 unsafe { slice::from_raw_parts(ptrlen.ptr as *const u8, ptrlen.len as usize) };
414 slice.to_vec()
415 }
416 })
417 }
418}
419
420impl From<Manifest> for std::borrow::Cow<'_, [u8]> {
421 fn from(m: Manifest) -> Self {
422 let s = serde_json::to_vec(&m).unwrap();
423 std::borrow::Cow::Owned(s)
424 }
425}
426
427impl From<&Manifest> for std::borrow::Cow<'_, [u8]> {
428 fn from(m: &Manifest) -> Self {
429 let s = serde_json::to_vec(&m).unwrap();
430 std::borrow::Cow::Owned(s)
431 }
432}