use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
#[deprecated]
pub type ManifestMemory = MemoryOptions;
#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
#[serde(deny_unknown_fields)]
pub struct MemoryOptions {
#[serde(alias = "max")]
pub max_pages: Option<u32>,
#[serde(default)]
pub max_http_response_bytes: Option<u64>,
#[serde(default = "default_var_bytes")]
pub max_var_bytes: Option<u64>,
}
impl MemoryOptions {
pub fn new() -> Self {
Default::default()
}
pub fn with_max_pages(mut self, pages: u32) -> Self {
self.max_pages = Some(pages);
self
}
pub fn with_max_http_response_bytes(mut self, bytes: u64) -> Self {
self.max_http_response_bytes = Some(bytes);
self
}
pub fn with_max_var_bytes(mut self, bytes: u64) -> Self {
self.max_var_bytes = Some(bytes);
self
}
}
fn default_var_bytes() -> Option<u64> {
Some(1024 * 1024)
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
#[serde(deny_unknown_fields)]
pub struct HttpRequest {
pub url: String,
#[serde(default)]
#[serde(alias = "header")]
pub headers: std::collections::BTreeMap<String, String>,
pub method: Option<String>,
}
impl HttpRequest {
pub fn new(url: impl Into<String>) -> HttpRequest {
HttpRequest {
url: url.into(),
headers: Default::default(),
method: None,
}
}
pub fn with_method(mut self, method: impl Into<String>) -> HttpRequest {
self.method = Some(method.into());
self
}
pub fn with_header(mut self, key: impl Into<String>, value: impl Into<String>) -> HttpRequest {
self.headers.insert(key.into(), value.into());
self
}
}
#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
#[serde(deny_unknown_fields)]
pub struct WasmMetadata {
pub name: Option<String>,
pub hash: Option<String>,
}
impl From<HttpRequest> for Wasm {
fn from(req: HttpRequest) -> Self {
Wasm::Url {
req,
meta: WasmMetadata::default(),
}
}
}
impl From<std::path::PathBuf> for Wasm {
fn from(path: std::path::PathBuf) -> Self {
Wasm::File {
path,
meta: WasmMetadata::default(),
}
}
}
impl From<Vec<u8>> for Wasm {
fn from(data: Vec<u8>) -> Self {
Wasm::Data {
data,
meta: WasmMetadata::default(),
}
}
}
#[deprecated]
pub type ManifestWasm = Wasm;
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
#[serde(untagged)]
#[serde(deny_unknown_fields)]
pub enum Wasm {
File {
path: PathBuf,
#[serde(flatten)]
meta: WasmMetadata,
},
Data {
#[serde(with = "wasmdata")]
#[cfg_attr(feature = "json_schema", schemars(schema_with = "wasmdata_schema"))]
data: Vec<u8>,
#[serde(flatten)]
meta: WasmMetadata,
},
Url {
#[serde(flatten)]
req: HttpRequest,
#[serde(flatten)]
meta: WasmMetadata,
},
}
impl Wasm {
pub fn file(path: impl AsRef<std::path::Path>) -> Self {
Wasm::File {
path: path.as_ref().to_path_buf(),
meta: Default::default(),
}
}
pub fn data(data: impl Into<Vec<u8>>) -> Self {
Wasm::Data {
data: data.into(),
meta: Default::default(),
}
}
pub fn url(url: impl Into<String>) -> Self {
Wasm::Url {
req: HttpRequest {
url: url.into(),
headers: Default::default(),
method: None,
},
meta: Default::default(),
}
}
pub fn http(req: impl Into<HttpRequest>) -> Self {
Wasm::Url {
req: req.into(),
meta: Default::default(),
}
}
pub fn meta(&self) -> &WasmMetadata {
match self {
Wasm::File { path: _, meta } => meta,
Wasm::Data { data: _, meta } => meta,
Wasm::Url { req: _, meta } => meta,
}
}
pub fn meta_mut(&mut self) -> &mut WasmMetadata {
match self {
Wasm::File { path: _, meta } => meta,
Wasm::Data { data: _, meta } => meta,
Wasm::Url { req: _, meta } => meta,
}
}
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.meta_mut().name = Some(name.into());
self
}
pub fn with_hash(mut self, hash: impl Into<String>) -> Self {
self.meta_mut().hash = Some(hash.into());
self
}
}
#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
#[serde(deny_unknown_fields)]
struct DataPtrLength {
ptr: u64,
len: u64,
}
#[cfg(feature = "json_schema")]
fn wasmdata_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
use schemars::{schema::SchemaObject, JsonSchema};
let mut schema: SchemaObject = <String>::json_schema(gen).into();
let objschema: SchemaObject = <DataPtrLength>::json_schema(gen).into();
let types = schemars::schema::SingleOrVec::<schemars::schema::InstanceType>::Vec(vec![
schemars::schema::InstanceType::String,
schemars::schema::InstanceType::Object,
]);
schema.instance_type = Some(types);
schema.object = objschema.object;
schema.into()
}
#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
#[serde(deny_unknown_fields)]
pub struct Manifest {
#[serde(default)]
pub wasm: Vec<Wasm>,
#[serde(default)]
pub memory: MemoryOptions,
#[serde(default)]
pub config: BTreeMap<String, String>,
#[serde(default)]
pub allowed_hosts: Option<Vec<String>>,
#[serde(default)]
pub allowed_paths: Option<BTreeMap<PathBuf, PathBuf>>,
#[serde(default)]
pub timeout_ms: Option<u64>,
}
impl Manifest {
pub fn new(wasm: impl IntoIterator<Item = impl Into<Wasm>>) -> Manifest {
Manifest {
wasm: wasm.into_iter().map(|x| x.into()).collect(),
..Default::default()
}
}
pub fn with_wasm(mut self, wasm: impl Into<Wasm>) -> Self {
self.wasm.push(wasm.into());
self
}
pub fn disallow_all_hosts(mut self) -> Self {
self.allowed_hosts = Some(vec![]);
self
}
pub fn with_memory_options(mut self, memory: MemoryOptions) -> Self {
self.memory = memory;
self
}
pub fn with_memory_max(mut self, max: u32) -> Self {
self.memory.max_pages = Some(max);
self
}
pub fn with_allowed_host(mut self, host: impl Into<String>) -> Self {
match &mut self.allowed_hosts {
Some(h) => {
h.push(host.into());
}
None => self.allowed_hosts = Some(vec![host.into()]),
}
self
}
pub fn with_allowed_hosts(mut self, hosts: impl Iterator<Item = String>) -> Self {
self.allowed_hosts = Some(hosts.collect());
self
}
pub fn with_allowed_path(mut self, src: impl AsRef<Path>, dest: impl AsRef<Path>) -> Self {
let src = src.as_ref().to_path_buf();
let dest = dest.as_ref().to_path_buf();
match &mut self.allowed_paths {
Some(p) => {
p.insert(src, dest);
}
None => {
let mut p = BTreeMap::new();
p.insert(src, dest);
self.allowed_paths = Some(p);
}
}
self
}
pub fn with_allowed_paths(mut self, paths: impl Iterator<Item = (PathBuf, PathBuf)>) -> Self {
self.allowed_paths = Some(paths.collect());
self
}
pub fn with_config(
mut self,
c: impl Iterator<Item = (impl Into<String>, impl Into<String>)>,
) -> Self {
for (k, v) in c {
self.config.insert(k.into(), v.into());
}
self
}
pub fn with_config_key(mut self, k: impl Into<String>, v: impl Into<String>) -> Self {
self.config.insert(k.into(), v.into());
self
}
pub fn with_timeout(mut self, timeout: std::time::Duration) -> Self {
self.timeout_ms = Some(timeout.as_millis() as u64);
self
}
}
mod wasmdata {
use crate::DataPtrLength;
use base64::{engine::general_purpose, Engine as _};
use serde::{Deserialize, Serialize};
use serde::{Deserializer, Serializer};
use std::slice;
pub fn serialize<S: Serializer>(v: &Vec<u8>, s: S) -> Result<S::Ok, S::Error> {
let base64 = general_purpose::STANDARD.encode(v.as_slice());
String::serialize(&base64, s)
}
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
#[derive(Deserialize)]
#[serde(untagged)]
enum WasmDataTypes {
String(String),
DataPtrLength(DataPtrLength),
}
Ok(match WasmDataTypes::deserialize(d)? {
WasmDataTypes::String(string) => general_purpose::STANDARD
.decode(string.as_bytes())
.map_err(serde::de::Error::custom)?,
WasmDataTypes::DataPtrLength(ptrlen) => {
let slice =
unsafe { slice::from_raw_parts(ptrlen.ptr as *const u8, ptrlen.len as usize) };
slice.to_vec()
}
})
}
}
impl<'a> From<Manifest> for std::borrow::Cow<'a, [u8]> {
fn from(m: Manifest) -> Self {
let s = serde_json::to_vec(&m).unwrap();
std::borrow::Cow::Owned(s)
}
}
impl<'a> From<&Manifest> for std::borrow::Cow<'a, [u8]> {
fn from(m: &Manifest) -> Self {
let s = serde_json::to_vec(&m).unwrap();
std::borrow::Cow::Owned(s)
}
}