Skip to main content

numcodecs_wasm_host/
wit.rs

1use std::sync::OnceLock;
2
3use semver::Version;
4use wasm_component_layer::{InterfaceIdentifier, PackageIdentifier, PackageName, Value};
5
6use crate::error::{CodecError, RuntimeError};
7
8/// WebAssembly Interface Type (WIT) interfaces for `numcodecs`
9#[non_exhaustive]
10pub struct NumcodecsWitInterfaces {
11    /// The `numcodecs:abc` package
12    pub package: PackageIdentifier,
13    /// The `numcodecs:abc/codec` interface
14    pub codec: InterfaceIdentifier,
15    /// The `numcodecs:abc/registry` interface
16    pub registry: InterfaceIdentifier,
17    /// The `numcodecs:abc/types` interface
18    pub types: InterfaceIdentifier,
19}
20
21impl NumcodecsWitInterfaces {
22    /// Get the once-computed interfaces
23    #[must_use]
24    pub fn get() -> &'static Self {
25        static NUMCODECS_WIT_INTERFACES: OnceLock<NumcodecsWitInterfaces> = OnceLock::new();
26
27        NUMCODECS_WIT_INTERFACES.get_or_init(|| {
28            let package = PackageIdentifier::new(
29                PackageName::new("numcodecs", "abc"),
30                Some(Version::new(0, 1, 1)),
31            );
32
33            Self {
34                package: package.clone(),
35                codec: InterfaceIdentifier::new(package.clone(), "codec"),
36                registry: InterfaceIdentifier::new(package.clone(), "registry"),
37                types: InterfaceIdentifier::new(package, "types"),
38            }
39        })
40    }
41}
42
43pub fn guest_error_from_wasm(err: Option<&Value>) -> Result<CodecError, RuntimeError> {
44    let Some(Value::Record(record)) = err else {
45        return Err(RuntimeError::from(anyhow::anyhow!(
46            "unexpected err value {err:?}"
47        )));
48    };
49
50    let Some(Value::String(message)) = record.field("message") else {
51        return Err(RuntimeError::from(anyhow::anyhow!(
52            "numcodecs:abc/codec::error is missing the `message` field"
53        )));
54    };
55
56    let Some(Value::List(chain)) = record.field("chain") else {
57        return Err(RuntimeError::from(anyhow::anyhow!(
58            "numcodecs:abc/codec::error is missing the `chain` field"
59        )));
60    };
61
62    let Ok(chain) = chain
63        .iter()
64        .map(|msg| match msg {
65            Value::String(msg) => Ok(msg),
66            _ => Err(()),
67        })
68        .collect::<Result<Vec<_>, _>>()
69    else {
70        return Err(RuntimeError::from(anyhow::anyhow!(
71            "numcodecs:abc/codec::error chain contains unexpected non-string values: {chain:?}"
72        )));
73    };
74
75    Ok(CodecError::new(message, chain))
76}