use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn run(toml: &str) -> Result<String, JsValue> {
crate::api::run_toml(toml)
.map(|o| o.json)
.map_err(|e| JsValue::from_str(&e))
}
#[wasm_bindgen]
pub fn chart_svg(toml: &str) -> Result<String, JsValue> {
crate::api::run_toml(toml)
.map(|o| o.svg)
.map_err(|e| JsValue::from_str(&e))
}
#[wasm_bindgen]
pub fn summary(toml: &str) -> Result<String, JsValue> {
crate::api::run_toml(toml)
.map(|o| o.summary)
.map_err(|e| JsValue::from_str(&e))
}
#[wasm_bindgen]
pub fn table_csv(toml: &str) -> Result<Option<String>, JsValue> {
crate::api::run_toml(toml)
.map(|o| o.csv)
.map_err(|e| JsValue::from_str(&e))
}
#[wasm_bindgen]
pub fn run_all(toml: &str) -> Result<String, JsValue> {
let o = crate::api::run_toml(toml).map_err(|e| JsValue::from_str(&e))?;
Ok(serde_json::json!({
"json": o.json,
"svg": o.svg,
"summary": o.summary,
"csv": o.csv,
})
.to_string())
}
#[wasm_bindgen]
pub fn list_kinds() -> String {
crate::api::list_scenario_kinds_json()
}
#[wasm_bindgen]
pub fn error_kind(toml: &str) -> String {
crate::api::run_scenario(toml)
.err()
.map(|e| e.kind_tag().to_string())
.unwrap_or_default()
}
#[wasm_bindgen]
pub fn version() -> String {
env!("CARGO_PKG_VERSION").to_string()
}
#[wasm_bindgen]
pub fn encode_permalink(toml: &str) -> String {
crate::permalink::encode_scenario(toml)
}
#[wasm_bindgen]
pub fn decode_permalink(token: &str) -> String {
crate::permalink::decode_scenario(token).unwrap_or_default()
}
#[wasm_bindgen]
pub fn export_sp3(toml: &str) -> Result<String, JsValue> {
crate::api::export_sp3(toml).map_err(|e| JsValue::from_str(&e))
}
#[wasm_bindgen]
pub fn export_omm(toml: &str) -> Result<String, JsValue> {
crate::api::export_omm(toml).map_err(|e| JsValue::from_str(&e))
}
#[wasm_bindgen]
pub fn export_oem(toml: &str) -> Result<String, JsValue> {
crate::api::export_oem(toml).map_err(|e| JsValue::from_str(&e))
}
#[wasm_bindgen]
pub fn receiver_trust(toml: &str) -> Result<String, JsValue> {
let o = crate::receiver_trust::scenario::run_toml(toml).map_err(|e| JsValue::from_str(&e))?;
Ok(serde_json::json!({
"json": o.json,
"csv": o.csv,
"svg": o.svg,
"summary": o.summary,
})
.to_string())
}
#[wasm_bindgen]
pub fn export_formats(toml: &str) -> Result<String, JsValue> {
crate::surface::export_formats(toml, crate::surface::MAX_INPUT_BYTES)
.map(|v| v.to_string())
.map_err(|e| JsValue::from_str(&e))
}
#[wasm_bindgen]
pub fn export_scenario(toml: &str, format: &str) -> Result<String, JsValue> {
crate::surface::export_scenario(toml, format, crate::surface::MAX_INPUT_BYTES)
.map(|e| e.to_json().to_string())
.map_err(|e| JsValue::from_str(&e))
}
#[wasm_bindgen]
pub fn import_route(toml: &str, geojson: &str) -> Result<String, JsValue> {
crate::surface::import_route(toml, geojson, crate::surface::MAX_INPUT_BYTES)
.map_err(|e| JsValue::from_str(&e))
}
#[wasm_bindgen]
pub fn animate_scenario(
toml: &str,
format: &str,
fps: u32,
duration_s: f64,
width: u32,
) -> Result<String, JsValue> {
let a = crate::surface::animate_scenario(
toml,
(!format.is_empty()).then_some(format),
(fps != 0).then_some(fps),
(!duration_s.is_nan()).then_some(duration_s),
(width != 0).then_some(width),
crate::surface::MAX_INPUT_BYTES,
)
.map_err(|e| JsValue::from_str(&e))?;
let files: serde_json::Map<String, serde_json::Value> =
a.files.into_iter().map(|(k, v)| (k, v.into())).collect();
Ok(serde_json::json!({"summary": a.summary, "files": files}).to_string())
}
#[wasm_bindgen]
pub fn list_examples(kind: &str) -> Result<String, JsValue> {
crate::surface::list_examples((!kind.is_empty()).then_some(kind))
.map(|v| v.to_string())
.map_err(|e| JsValue::from_str(&e))
}
#[wasm_bindgen]
pub fn get_example(name: &str) -> Result<String, JsValue> {
crate::surface::get_example(name)
.map(str::to_string)
.map_err(|e| JsValue::from_str(&e))
}
#[wasm_bindgen]
pub fn evidence_attach_timestamp(
files_json: &str,
token_base64: &str,
replace: bool,
) -> Result<String, JsValue> {
let v: serde_json::Value = serde_json::from_str(files_json)
.map_err(|e| JsValue::from_str(&format!("files_json is not JSON: {e}")))?;
let files = crate::surface::files_from_json(&v).map_err(|e| JsValue::from_str(&e))?;
let token = crate::permalink::base64_decode(token_base64.trim())
.ok_or_else(|| JsValue::from_str("token_base64 is not base64"))?;
let t = crate::surface::evidence_attach_timestamp(
&files,
&token,
replace,
crate::surface::MAX_INPUT_BYTES,
)
.map_err(|e| JsValue::from_str(&e))?;
Ok(
serde_json::json!({"files": crate::surface::files_to_json(&t.files), "notes": t.notes})
.to_string(),
)
}
#[wasm_bindgen]
pub fn bench_export(toml: &str, epoch: &str) -> Result<String, JsValue> {
use crate::surface::{BENCH_NOTICE, MAX_INPUT_BYTES};
let e =
crate::surface::bench_export(toml, (!epoch.is_empty()).then_some(epoch), MAX_INPUT_BYTES)
.map_err(|e| JsValue::from_str(&e))?;
let files: serde_json::Map<String, serde_json::Value> =
e.files.into_iter().map(|(k, v)| (k, v.into())).collect();
Ok(serde_json::json!({"files": files, "notes": e.notes, "notice": BENCH_NOTICE}).to_string())
}
#[wasm_bindgen]
pub fn compliance_report(runs_json: &str) -> Result<String, JsValue> {
use crate::surface::{ComplianceRunText, MAX_INPUT_BYTES};
let v: serde_json::Value = serde_json::from_str(runs_json)
.map_err(|e| JsValue::from_str(&format!("runs_json is not JSON: {e}")))?;
let arr = v.as_array().ok_or_else(|| {
JsValue::from_str("runs_json must be an array of {label, result, scenario?}")
})?;
let mut texts = Vec::with_capacity(arr.len());
for (i, r) in arr.iter().enumerate() {
let field = |k: &str| r.get(k).and_then(|x| x.as_str()).map(str::to_string);
texts.push(ComplianceRunText {
label: field("label")
.ok_or_else(|| JsValue::from_str(&format!("runs[{i}] needs `label`")))?,
result_json: field("result")
.ok_or_else(|| JsValue::from_str(&format!("runs[{i}] needs `result`")))?,
scenario_toml: field("scenario"),
});
}
let out = crate::surface::compliance_report(&texts, MAX_INPUT_BYTES)
.map_err(|e| JsValue::from_str(&e))?;
Ok(serde_json::json!({"report": out.report, "markdown": out.markdown}).to_string())
}
#[wasm_bindgen]
pub fn compliance_mapping(sources: bool) -> String {
crate::surface::compliance_mapping(sources)
}
#[wasm_bindgen]
#[allow(clippy::too_many_arguments)]
pub fn interference_map(
source: &str,
csv: &str,
dataset: &str,
cell_deg: f64,
licence: &str,
licence_url: &str,
attribution: &str,
land_geojson: &str,
) -> Result<String, JsValue> {
use crate::surface::{interference_map as build, CustomDataset, MapSource, MAX_INPUT_BYTES};
let custom =
(!licence.is_empty() || !licence_url.is_empty() || !attribution.is_empty()).then(|| {
CustomDataset {
licence: licence.into(),
licence_url: licence_url.into(),
attribution: attribution.into(),
}
});
let days = build(
MapSource::parse(source).map_err(|e| JsValue::from_str(&e))?,
csv,
dataset,
(!cell_deg.is_nan()).then_some(cell_deg),
custom.as_ref(),
(!land_geojson.is_empty()).then_some(land_geojson),
MAX_INPUT_BYTES,
)
.map_err(|e| JsValue::from_str(&e))?;
Ok(serde_json::Value::Array(
days.into_iter()
.map(|d| {
serde_json::json!({
"file_name": d.file_name,
"date": d.date,
"cells_published": d.cells_published,
"cells_flagged": d.cells_flagged,
"geojson": d.geojson,
})
})
.collect(),
)
.to_string())
}
#[wasm_bindgen]
pub fn route_exposure(
route: &str,
maps_json: &str,
date_from: &str,
date_to: &str,
) -> Result<String, JsValue> {
let maps: Vec<String> = serde_json::from_str(maps_json).map_err(|e| {
JsValue::from_str(&format!("maps_json must be a JSON array of strings: {e}"))
})?;
let refs: Vec<&str> = maps.iter().map(String::as_str).collect();
crate::surface::route_exposure(
route,
&refs,
(!date_from.is_empty()).then_some(date_from),
(!date_to.is_empty()).then_some(date_to),
crate::surface::MAX_INPUT_BYTES,
)
.map_err(|e| JsValue::from_str(&e))
}
#[wasm_bindgen]
pub fn nmea_training(toml: &str, seed: f64) -> Result<String, JsValue> {
let seed = (seed.is_finite() && seed >= 0.0).then_some(seed as u64);
let t = crate::surface::nmea_training(toml, seed, crate::surface::MAX_INPUT_BYTES)
.map_err(|e| JsValue::from_str(&e))?;
Ok(
serde_json::json!({"nmea": t.nmea, "log_json": t.log_json, "log_text": t.log_text})
.to_string(),
)
}
#[wasm_bindgen]
pub fn receiver_trust_replay(session_toml: &str, nmea: &str) -> Result<String, JsValue> {
let r = crate::surface::assess_vessel_excerpt(
session_toml,
nmea.as_bytes(),
crate::surface::MAX_INPUT_BYTES,
)
.map_err(|e| JsValue::from_str(&e))?;
serde_json::to_string(&r).map_err(|e| JsValue::from_str(&e.to_string()))
}
#[wasm_bindgen]
pub fn assess_vessel_log(session_toml: &str, log: &str) -> Result<String, JsValue> {
crate::surface::assess_vessel_log_json(
session_toml,
log.as_bytes(),
crate::surface::MAX_INPUT_BYTES,
)
.map_err(|e| JsValue::from_str(&e))
}
#[wasm_bindgen]
pub fn evidence_verify(
files_json: &str,
public_key: &str,
full_log: &str,
require_timestamp: bool,
) -> Result<String, JsValue> {
let v: serde_json::Value = serde_json::from_str(files_json)
.map_err(|e| JsValue::from_str(&format!("files_json is not JSON: {e}")))?;
let files = crate::surface::files_from_json(&v).map_err(|e| JsValue::from_str(&e))?;
let report = crate::surface::evidence_verify(
&files,
(!public_key.is_empty()).then_some(public_key),
(!full_log.is_empty()).then_some(full_log.as_bytes()),
require_timestamp,
)
.map_err(|e| JsValue::from_str(&e))?;
Ok(report.to_string())
}
#[wasm_bindgen]
pub fn trust_telemetry(
input: &str,
format: &str,
host: &str,
input_kind: &str,
options_json: &str,
) -> Result<String, JsValue> {
use crate::surface::{self, TelemetryFormat, TelemetryInput, TelemetryOptions};
let err = |e: String| JsValue::from_str(&e);
let mut opts = TelemetryOptions::default();
if !options_json.trim().is_empty() {
let v: serde_json::Value = serde_json::from_str(options_json)
.map_err(|e| err(format!("options_json is not JSON: {e}")))?;
opts.expose_position = v["expose_position"].as_bool().unwrap_or(false);
opts.syslog_all = v["syslog_all"].as_bool().unwrap_or(false);
opts.unix_nano = v["unix_nano"].as_f64().unwrap_or(0.0) as u64;
}
surface::trust_telemetry(
input,
TelemetryFormat::parse(format).map_err(err)?,
host,
TelemetryInput::parse(input_kind).map_err(err)?,
opts,
surface::MAX_INPUT_BYTES,
)
.map_err(err)
}
#[wasm_bindgen]
pub fn osnma_verify(
input_base64: &str,
format: &str,
start_gst: Option<u32>,
merkle_root_hex: &str,
public_keys: Vec<String>,
reference_time: Option<u32>,
max_time_error: Option<u32>,
) -> Result<String, JsValue> {
let b64 = input_base64.trim();
if b64.len() / 4 * 3 > crate::surface::MAX_INPUT_BYTES {
return Err(JsValue::from_str(&format!(
"input is about {} bytes, over the {}-byte limit",
b64.len() / 4 * 3,
crate::surface::MAX_INPUT_BYTES
)));
}
let bytes = crate::permalink::base64_decode(b64)
.ok_or_else(|| JsValue::from_str("input_base64 is not base64"))?;
crate::surface::osnma_verify(
&bytes,
format,
start_gst,
merkle_root_hex,
&public_keys,
reference_time,
max_time_error,
crate::surface::MAX_INPUT_BYTES,
)
.map_err(|e| JsValue::from_str(&e))
}