numcodecs_wasm_guest/
lib.rs

1//! [![CI Status]][workflow] [![MSRV]][repo] [![Latest Version]][crates.io] [![Rust Doc Crate]][docs.rs] [![Rust Doc Main]][docs]
2//!
3//! [CI Status]: https://img.shields.io/github/actions/workflow/status/juntyr/numcodecs-rs/ci.yml?branch=main
4//! [workflow]: https://github.com/juntyr/numcodecs-rs/actions/workflows/ci.yml?query=branch%3Amain
5//!
6//! [MSRV]: https://img.shields.io/badge/MSRV-1.76.0-blue
7//! [repo]: https://github.com/juntyr/numcodecs-rs
8//!
9//! [Latest Version]: https://img.shields.io/crates/v/numcodecs-wasm-guest
10//! [crates.io]: https://crates.io/crates/numcodecs-wasm-guest
11//!
12//! [Rust Doc Crate]: https://img.shields.io/docsrs/numcodecs-wasm-guest
13//! [docs.rs]: https://docs.rs/numcodecs-wasm-guest/
14//!
15//! [Rust Doc Main]: https://img.shields.io/badge/docs-main-blue
16//! [docs]: https://juntyr.github.io/numcodecs-rs/numcodecs_wasm_guest
17//!
18//! wasm32 guest-side bindings for the [`numcodecs`] API, which allows you to
19//! export one [`StaticCodec`] from a WASM component.
20
21// hashbrown 0.14 (wasmparser) and 0.15 (indexmap)
22#![allow(clippy::multiple_crate_versions)]
23
24// Required in docs and the [`export_codec`] macro
25#[doc(hidden)]
26pub use numcodecs;
27
28#[cfg(doc)]
29use numcodecs::StaticCodec;
30
31#[cfg(target_arch = "wasm32")]
32use ::{
33    numcodecs::{Codec, StaticCodec},
34    schemars::schema_for,
35    serde::Deserialize,
36};
37
38#[cfg(target_arch = "wasm32")]
39mod convert;
40
41#[cfg(target_arch = "wasm32")]
42use crate::{
43    bindings::exports::numcodecs::abc::codec as wit,
44    convert::{
45        from_wit_any_array, into_wit_any_array, into_wit_error, zeros_from_wit_any_array_prototype,
46    },
47};
48
49#[doc(hidden)]
50#[allow(clippy::missing_safety_doc)]
51pub mod bindings {
52    wit_bindgen::generate!({
53        world: "numcodecs:abc/exports@0.1.1",
54        with: {
55            "numcodecs:abc/codec@0.1.1": generate,
56        },
57        pub_export_macro: true,
58    });
59}
60
61#[macro_export]
62/// Export a [`StaticCodec`] type using the WASM component model.
63///
64/// ```rust,ignore
65/// # use numcodecs_wasm_guest::export_codec;
66///
67/// struct MyCodec {
68///     // ...
69/// }
70///
71/// impl numcodecs::Codec for MyCodec {
72///     // ...
73/// }
74///
75/// impl numcodecs::StaticCodec for MyCodec {
76///     // ...
77/// }
78///
79/// export_codec!(MyCodec);
80/// ```
81macro_rules! export_codec {
82    ($codec:ty) => {
83        #[cfg(target_arch = "wasm32")]
84        const _: () = {
85            type Codec = $codec;
86
87            $crate::bindings::export!(
88                Codec with_types_in $crate::bindings
89            );
90        };
91
92        const _: () = {
93            const fn can_only_export_static_codec<T: $crate::numcodecs::StaticCodec>() {}
94
95            can_only_export_static_codec::<$codec>()
96        };
97    };
98}
99
100#[cfg(target_arch = "wasm32")]
101#[doc(hidden)]
102impl<T: StaticCodec> wit::Guest for T {
103    type Codec = Self;
104
105    fn codec_id() -> String {
106        String::from(<Self as StaticCodec>::CODEC_ID)
107    }
108
109    fn codec_config_schema() -> wit::JsonSchema {
110        schema_for!(<Self as StaticCodec>::Config<'static>)
111            .as_value()
112            .to_string()
113    }
114}
115
116#[cfg(target_arch = "wasm32")]
117impl<T: StaticCodec> wit::GuestCodec for T {
118    fn from_config(config: String) -> Result<wit::Codec, wit::Error> {
119        let err = match <Self as StaticCodec>::Config::deserialize(
120            &mut serde_json::Deserializer::from_str(&config),
121        ) {
122            Ok(config) => return Ok(wit::Codec::new(<Self as StaticCodec>::from_config(config))),
123            Err(err) => err,
124        };
125
126        let err = format_serde_error::SerdeError::new(config, err);
127        Err(into_wit_error(err))
128    }
129
130    fn encode(&self, data: wit::AnyArray) -> Result<wit::AnyArray, wit::Error> {
131        let data = match from_wit_any_array(data) {
132            Ok(data) => data,
133            Err(err) => return Err(into_wit_error(err)),
134        };
135
136        match <Self as Codec>::encode(self, data.into_cow()) {
137            Ok(encoded) => match into_wit_any_array(encoded) {
138                Ok(encoded) => Ok(encoded),
139                Err(err) => Err(into_wit_error(err)),
140            },
141            Err(err) => Err(into_wit_error(err)),
142        }
143    }
144
145    fn decode(&self, encoded: wit::AnyArray) -> Result<wit::AnyArray, wit::Error> {
146        let encoded = match from_wit_any_array(encoded) {
147            Ok(encoded) => encoded,
148            Err(err) => return Err(into_wit_error(err)),
149        };
150
151        match <Self as Codec>::decode(self, encoded.into_cow()) {
152            Ok(decoded) => match into_wit_any_array(decoded) {
153                Ok(decoded) => Ok(decoded),
154                Err(err) => Err(into_wit_error(err)),
155            },
156            Err(err) => Err(into_wit_error(err)),
157        }
158    }
159
160    fn decode_into(
161        &self,
162        encoded: wit::AnyArray,
163        decoded: wit::AnyArrayPrototype,
164    ) -> Result<wit::AnyArray, wit::Error> {
165        let encoded = match from_wit_any_array(encoded) {
166            Ok(encoded) => encoded,
167            Err(err) => return Err(into_wit_error(err)),
168        };
169
170        let mut decoded = zeros_from_wit_any_array_prototype(decoded);
171
172        match <Self as Codec>::decode_into(self, encoded.view(), decoded.view_mut()) {
173            Ok(()) => match into_wit_any_array(decoded) {
174                Ok(decoded) => Ok(decoded),
175                Err(err) => Err(into_wit_error(err)),
176            },
177            Err(err) => Err(into_wit_error(err)),
178        }
179    }
180
181    fn get_config(&self) -> Result<wit::Json, wit::Error> {
182        match serde_json::to_string(&<Self as StaticCodec>::get_config(self)) {
183            Ok(config) => Ok(config),
184            Err(err) => Err(into_wit_error(err)),
185        }
186    }
187}