use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct WasmTransformConfig {
pub module: String,
#[serde(default = "default_function")]
pub function: String,
#[serde(default = "default_memory_limit_mb")]
pub memory_limit_mb: u32,
#[serde(default = "default_fuel_limit")]
pub fuel_limit: u64,
#[serde(default)]
pub on_error: WasmOnError,
#[serde(default)]
pub reload_on_change: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum WasmOnError {
#[default]
Fail,
Skip,
Passthrough,
}
fn default_function() -> String {
"transform".to_owned()
}
fn default_memory_limit_mb() -> u32 {
16
}
fn default_fuel_limit() -> u64 {
10_000_000
}
impl WasmTransformConfig {
pub(crate) fn module_label(&self) -> String {
std::path::Path::new(&self.module)
.file_name()
.map(|s| s.to_string_lossy().into_owned())
.unwrap_or_else(|| self.module.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn decodes_minimal_config_with_defaults() {
let cfg: WasmTransformConfig =
serde_json::from_value(json!({"module": "./t.wasm"})).unwrap();
assert_eq!(cfg.module, "./t.wasm");
assert_eq!(cfg.function, "transform");
assert_eq!(cfg.memory_limit_mb, 16);
assert_eq!(cfg.fuel_limit, 10_000_000);
assert_eq!(cfg.on_error, WasmOnError::Fail);
assert!(!cfg.reload_on_change);
}
#[test]
fn decodes_full_config() {
let cfg: WasmTransformConfig = serde_json::from_value(json!({
"module": "/abs/redact.wasm",
"function": "run",
"memory_limit_mb": 32,
"fuel_limit": 5000,
"on_error": "skip",
"reload_on_change": true
}))
.unwrap();
assert_eq!(cfg.function, "run");
assert_eq!(cfg.memory_limit_mb, 32);
assert_eq!(cfg.fuel_limit, 5000);
assert_eq!(cfg.on_error, WasmOnError::Skip);
assert!(cfg.reload_on_change);
}
#[test]
fn rejects_missing_module() {
let err = serde_json::from_value::<WasmTransformConfig>(json!({})).unwrap_err();
assert!(err.to_string().contains("module"), "{err}");
}
#[test]
fn rejects_unknown_on_error() {
let err = serde_json::from_value::<WasmTransformConfig>(
json!({"module": "t.wasm", "on_error": "explode"}),
)
.unwrap_err();
assert!(
err.to_string().to_lowercase().contains("on_error")
|| err.to_string().contains("explode"),
"{err}"
);
}
#[test]
fn module_label_is_basename() {
let cfg: WasmTransformConfig =
serde_json::from_value(json!({"module": "/a/b/redact_email.wasm"})).unwrap();
assert_eq!(cfg.module_label(), "redact_email.wasm");
let bare: WasmTransformConfig =
serde_json::from_value(json!({"module": "x.wasm"})).unwrap();
assert_eq!(bare.module_label(), "x.wasm");
}
#[test]
fn schema_builds_with_module_property() {
let schema = schemars::schema_for!(WasmTransformConfig);
let json = serde_json::to_value(&schema).unwrap();
assert!(
json.get("properties")
.and_then(|p| p.get("module"))
.is_some()
);
}
}