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.82.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// Required in docs and the [`export_codec`] macro
22#[doc(hidden)]
23pub use numcodecs;
24
25#[cfg(doc)]
26use numcodecs::StaticCodec;
27
28#[cfg(target_arch = "wasm32")]
29use ::{
30    numcodecs::{Codec, StaticCodec},
31    schemars::schema_for,
32    serde::Deserialize,
33};
34
35#[cfg(target_arch = "wasm32")]
36mod convert;
37
38#[cfg(target_arch = "wasm32")]
39use crate::{
40    bindings::exports::numcodecs::abc::codec as wit,
41    convert::{
42        from_wit_any_array, into_wit_any_array, into_wit_error, zeros_from_wit_any_array_prototype,
43    },
44};
45
46#[doc(hidden)]
47pub mod bindings {
48    wit_bindgen::generate!({
49        world: "numcodecs:abc/exports@0.1.1",
50        with: {
51            "numcodecs:abc/codec@0.1.1": generate,
52        },
53        pub_export_macro: true,
54    });
55}
56
57#[macro_export]
58/// Export a [`StaticCodec`] type using the WASM component model.
59///
60/// ```rust,ignore
61/// # use numcodecs_wasm_guest::export_codec;
62///
63/// struct MyCodec {
64///     // ...
65/// }
66///
67/// impl numcodecs::Codec for MyCodec {
68///     // ...
69/// }
70///
71/// impl numcodecs::StaticCodec for MyCodec {
72///     // ...
73/// }
74///
75/// export_codec!(MyCodec);
76/// ```
77macro_rules! export_codec {
78    ($codec:ty) => {
79        #[cfg(target_arch = "wasm32")]
80        const _: () = {
81            type Codec = $codec;
82
83            $crate::bindings::export!(
84                Codec with_types_in $crate::bindings
85            );
86        };
87
88        const _: () = {
89            const fn can_only_export_static_codec<T: $crate::numcodecs::StaticCodec>() {}
90
91            can_only_export_static_codec::<$codec>()
92        };
93    };
94}
95
96#[cfg(target_arch = "wasm32")]
97#[doc(hidden)]
98impl<T: StaticCodec> wit::Guest for T {
99    type Codec = Self;
100
101    fn codec_id() -> String {
102        String::from(<Self as StaticCodec>::CODEC_ID)
103    }
104
105    fn codec_config_schema() -> wit::JsonSchema {
106        schema_for!(<Self as StaticCodec>::Config<'static>)
107            .as_value()
108            .to_string()
109    }
110}
111
112#[cfg(target_arch = "wasm32")]
113impl<T: StaticCodec> wit::GuestCodec for T {
114    fn from_config(config: String) -> Result<wit::Codec, wit::Error> {
115        let err = match <Self as StaticCodec>::Config::deserialize(
116            &mut serde_json::Deserializer::from_str(&config),
117        ) {
118            Ok(config) => return Ok(wit::Codec::new(<Self as StaticCodec>::from_config(config))),
119            Err(err) => err,
120        };
121
122        let err = format_serde_error::SerdeError::new(config, err);
123        Err(into_wit_error(err))
124    }
125
126    fn encode(&self, data: wit::AnyArray) -> Result<wit::AnyArray, wit::Error> {
127        let data = match from_wit_any_array(data) {
128            Ok(data) => data,
129            Err(err) => return Err(into_wit_error(err)),
130        };
131
132        match <Self as Codec>::encode(self, data.into_cow()) {
133            Ok(encoded) => match into_wit_any_array(encoded) {
134                Ok(encoded) => Ok(encoded),
135                Err(err) => Err(into_wit_error(err)),
136            },
137            Err(err) => Err(into_wit_error(err)),
138        }
139    }
140
141    fn decode(&self, encoded: wit::AnyArray) -> Result<wit::AnyArray, wit::Error> {
142        let encoded = match from_wit_any_array(encoded) {
143            Ok(encoded) => encoded,
144            Err(err) => return Err(into_wit_error(err)),
145        };
146
147        match <Self as Codec>::decode(self, encoded.into_cow()) {
148            Ok(decoded) => match into_wit_any_array(decoded) {
149                Ok(decoded) => Ok(decoded),
150                Err(err) => Err(into_wit_error(err)),
151            },
152            Err(err) => Err(into_wit_error(err)),
153        }
154    }
155
156    fn decode_into(
157        &self,
158        encoded: wit::AnyArray,
159        decoded: wit::AnyArrayPrototype,
160    ) -> Result<wit::AnyArray, wit::Error> {
161        let encoded = match from_wit_any_array(encoded) {
162            Ok(encoded) => encoded,
163            Err(err) => return Err(into_wit_error(err)),
164        };
165
166        let mut decoded = zeros_from_wit_any_array_prototype(decoded);
167
168        match <Self as Codec>::decode_into(self, encoded.view(), decoded.view_mut()) {
169            Ok(()) => match into_wit_any_array(decoded) {
170                Ok(decoded) => Ok(decoded),
171                Err(err) => Err(into_wit_error(err)),
172            },
173            Err(err) => Err(into_wit_error(err)),
174        }
175    }
176
177    fn get_config(&self) -> Result<wit::Json, wit::Error> {
178        match serde_json::to_string(&<Self as StaticCodec>::get_config(self)) {
179            Ok(config) => Ok(config),
180            Err(err) => Err(into_wit_error(err)),
181        }
182    }
183}