use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
pub struct WasmLimitsConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeout_secs: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_memory: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_concurrent_calls: Option<usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_wasm_size: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub allow_call_schemes: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_stream_bytes: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_instances: Option<usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_tables: Option<usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_table_elements: Option<usize>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn defaults_to_all_none() {
let cfg = WasmLimitsConfig::default();
assert_eq!(cfg.timeout_secs, None);
assert_eq!(cfg.max_memory, None);
assert_eq!(cfg.max_concurrent_calls, None);
assert_eq!(cfg.max_wasm_size, None);
assert_eq!(cfg.allow_call_schemes, None);
assert_eq!(cfg.max_stream_bytes, None);
assert_eq!(cfg.max_instances, None);
assert_eq!(cfg.max_tables, None);
assert_eq!(cfg.max_table_elements, None);
}
#[test]
fn deserialises_full_block() {
let toml = toml::toml! {
timeout-secs = 600
max-memory = 4294967296i64
max-concurrent-calls = 2
max-wasm-size = 20971520i64
allow-call-schemes = "log,direct"
max-stream-bytes = 52428800i64
max-instances = 100
max-tables = 50
max-table-elements = 200
};
let cfg: WasmLimitsConfig = toml.try_into().expect("deserialize");
assert_eq!(cfg.timeout_secs, Some(600));
assert_eq!(cfg.max_memory, Some(4_294_967_296));
assert_eq!(cfg.max_concurrent_calls, Some(2));
assert_eq!(cfg.max_wasm_size, Some(20_971_520));
assert_eq!(cfg.allow_call_schemes.as_deref(), Some("log,direct"));
assert_eq!(cfg.max_stream_bytes, Some(52_428_800));
assert_eq!(cfg.max_instances, Some(100));
assert_eq!(cfg.max_tables, Some(50));
assert_eq!(cfg.max_table_elements, Some(200));
}
#[test]
fn deserialises_partial_block() {
let toml = toml::toml! {
timeout-secs = 120
};
let cfg: WasmLimitsConfig = toml.try_into().expect("deserialize");
assert_eq!(cfg.timeout_secs, Some(120));
assert_eq!(cfg.max_memory, None);
assert_eq!(cfg.max_concurrent_calls, None);
assert_eq!(cfg.max_wasm_size, None);
assert_eq!(cfg.allow_call_schemes, None);
assert_eq!(cfg.max_stream_bytes, None);
assert_eq!(cfg.max_instances, None);
assert_eq!(cfg.max_tables, None);
assert_eq!(cfg.max_table_elements, None);
}
#[test]
fn deserialises_allow_call_schemes() {
let toml = toml::toml! {
timeout-secs = 10
allow-call-schemes = "log,direct"
};
let cfg: WasmLimitsConfig = toml.try_into().expect("deserialize");
assert_eq!(cfg.allow_call_schemes.as_deref(), Some("log,direct"));
}
#[test]
fn rejects_unknown_field() {
let toml = toml::toml! {
timeout-secs = 10
fuel = 1000
};
let result: Result<WasmLimitsConfig, _> = toml.try_into();
assert!(result.is_err(), "deny_unknown_fields must reject `fuel`");
}
#[test]
fn serde_round_trip_preserves_set_fields() {
let original = WasmLimitsConfig {
timeout_secs: Some(45),
max_memory: Some(64 * 1024 * 1024),
max_concurrent_calls: None,
..WasmLimitsConfig::default()
};
let serialized = toml::to_string(&original).expect("serialize");
let back: WasmLimitsConfig = toml::from_str(&serialized).expect("deserialize");
assert_eq!(original, back);
}
#[test]
fn skip_serializing_none_fields() {
let cfg = WasmLimitsConfig {
timeout_secs: Some(30),
max_memory: None,
max_concurrent_calls: None,
..WasmLimitsConfig::default()
};
let s = toml::to_string(&cfg).expect("serialize");
assert!(s.contains("timeout-secs"));
assert!(!s.contains("max-memory"));
assert!(!s.contains("max-concurrent-calls"));
assert!(!s.contains("max-wasm-size"));
assert!(!s.contains("allow-call-schemes"));
assert!(!s.contains("max-stream-bytes"));
assert!(!s.contains("max-instances"));
assert!(!s.contains("max-tables"));
assert!(!s.contains("max-table-elements"));
}
#[test]
fn max_stream_bytes_round_trip() {
let cfg = WasmLimitsConfig {
max_stream_bytes: Some(52_428_800),
..WasmLimitsConfig::default()
};
let serialized = toml::to_string(&cfg).expect("serialize");
assert!(serialized.contains("max-stream-bytes"));
let back: WasmLimitsConfig = toml::from_str(&serialized).expect("deserialize");
assert_eq!(back.max_stream_bytes, Some(52_428_800));
}
#[test]
fn instances_tables_table_elements_round_trip() {
let cfg = WasmLimitsConfig {
max_instances: Some(100),
max_tables: Some(50),
max_table_elements: Some(200),
..WasmLimitsConfig::default()
};
let serialized = toml::to_string(&cfg).expect("serialize");
assert!(serialized.contains("max-instances"));
assert!(serialized.contains("max-tables"));
assert!(serialized.contains("max-table-elements"));
let back: WasmLimitsConfig = toml::from_str(&serialized).expect("deserialize");
assert_eq!(back.max_instances, Some(100));
assert_eq!(back.max_tables, Some(50));
assert_eq!(back.max_table_elements, Some(200));
}
}