From 587addd513f48aa1b5782c946645ee5fbbb9b0c1 Mon Sep 17 00:00:00 2001 From: Christian Ivicevic Date: Wed, 6 Mar 2024 11:34:41 +0100 Subject: [PATCH] Add wasm support to `rust-decimal` A new optional feature 'wasm' has been introduced in the codebase. This enables `wasm-bindgen` support, making `Decimal` compatible with the `wasm_bindgen` attribute macro, and exposes `fromNumber()` and `toNumber()` methods to convert between `Decimal` and the primitive `number` type. --- Cargo.toml | 2 ++ README.md | 7 +++++++ src/decimal.rs | 3 +++ src/lib.rs | 2 ++ src/wasm.rs | 26 ++++++++++++++++++++++++++ 5 files changed, 40 insertions(+) create mode 100644 src/wasm.rs diff --git a/Cargo.toml b/Cargo.toml index b99a14c..2c70c13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ rocket = { default-features = false, optional = true, version = "0.5.0-rc.3" } serde = { default-features = false, optional = true, version = "1.0" } serde_json = { default-features = false, optional = true, version = "1.0" } tokio-postgres = { default-features = false, optional = true, version = "0.7" } +wasm-bindgen = { default-features = false, optional = true, version = "0.2" } [dev-dependencies] bincode = { default-features = false, version = "1.0" } @@ -85,6 +86,7 @@ serde-with-float = ["serde"] serde-with-str = ["serde"] std = ["arrayvec/std", "borsh?/std", "bytes?/std", "rand?/std", "rkyv?/std", "serde?/std", "serde_json?/std"] tokio-pg = ["db-tokio-postgres"] # Backwards compatability +wasm = ["dep:wasm-bindgen"] [[bench]] harness = false diff --git a/README.md b/README.md index 1927044..e87015d 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ assert_eq!(total, dec!(27.26)); * [rocket-traits](#rocket-traits) * [rust-fuzz](#rust-fuzz) * [std](#std) +* [wasm](#wasm) **Database** @@ -331,6 +332,12 @@ Please see the `examples` directory for more information regarding `serde_json` Enable `std` library support. This is enabled by default, however in the future will be opt in. For now, to support `no_std` libraries, this crate can be compiled with `--no-default-features`. +### `wasm` + +Enable [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) support which makes `Decimal` compatible with the +`wasm_bindgen` attribute macro and exposes `fromNumber()` and `toNumber()` methods to convert between `Decimal` and +the primitive `number` type across boundaries. + ## Building Please refer to the [Build document](BUILD.md) for more information on building and testing Rust Decimal. diff --git a/src/decimal.rs b/src/decimal.rs index 45f5661..4f1ddfe 100644 --- a/src/decimal.rs +++ b/src/decimal.rs @@ -27,6 +27,8 @@ use num_traits::float::FloatCore; use num_traits::{FromPrimitive, Num, One, Signed, ToPrimitive, Zero}; #[cfg(feature = "rkyv")] use rkyv::{Archive, Deserialize, Serialize}; +#[cfg(feature = "wasm")] +use wasm_bindgen::prelude::wasm_bindgen; /// The smallest value that can be represented by this decimal type. const MIN: Decimal = Decimal { @@ -121,6 +123,7 @@ pub struct UnpackedDecimal { archive_attr(derive(Clone, Copy, Debug)) )] #[cfg_attr(feature = "rkyv-safe", archive(check_bytes))] +#[cfg_attr(feature = "wasm", wasm_bindgen)] pub struct Decimal { // Bits 0-15: unused // Bits 16-23: Contains "e", a value between 0-28 that indicates the scale diff --git a/src/lib.rs b/src/lib.rs index d49db64..2306695 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,6 +52,8 @@ mod serde; ) ))] pub mod serde; +#[cfg(feature = "wasm")] +pub mod wasm; pub use decimal::{Decimal, RoundingStrategy}; pub use error::Error; diff --git a/src/wasm.rs b/src/wasm.rs new file mode 100644 index 0000000..03ed619 --- /dev/null +++ b/src/wasm.rs @@ -0,0 +1,26 @@ +use num_traits::{FromPrimitive, ToPrimitive}; +use wasm_bindgen::prelude::wasm_bindgen; + +use crate::Decimal; + +#[wasm_bindgen] +impl Decimal { + /// Returns a new `Decimal` object instance by converting a primitive number. + #[wasm_bindgen(js_name = fromNumber)] + #[must_use] + pub fn from_number(value: f64) -> Option { + Decimal::from_f64(value) + } + + /// Returns the value of this `Decimal` converted to a primitive number. + /// + /// # Caution + /// At the time of writing this implementation the conversion from `Decimal` to `f64` cannot + /// fail. To prevent undefined behavior in case the underlying implementation changes `f64::NAN` + /// is returned as a stable fallback value. + #[wasm_bindgen(js_name = toNumber)] + #[must_use] + pub fn to_number(&self) -> f64 { + self.to_f64().unwrap_or(f64::NAN) + } +}