Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce impl TryFrom for Number that succeeds iff the value is within the safe range #3847

Merged
merged 14 commits into from
Feb 23, 2024
Merged
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
* Add bindings for `CanvasState.reset()`, affecting `CanvasRenderingContext2D` and `OffscreenCanvasRenderingContext2D`.
[#3844](https://github.com/rustwasm/wasm-bindgen/pull/3844)

* Add `TryFrom` implementations for `Number`, that allow losslessly converting from 64- and 128-bits numbers
Ekleog marked this conversation as resolved.
Show resolved Hide resolved

--------------------------------------------------------------------------------

## [0.2.91](https://github.com/rustwasm/wasm-bindgen/compare/0.2.90...0.2.91)
Expand Down
39 changes: 39 additions & 0 deletions crates/js-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2793,6 +2793,45 @@ macro_rules! number_from {
}
number_from!(i8 u8 i16 u16 i32 u32 f32 f64);

/// The error type returned when a checked integral type conversion fails.
///
/// This type is copy-pasted from the standard library
Ekleog marked this conversation as resolved.
Show resolved Hide resolved
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct TryFromIntError(pub(crate) ());
Ekleog marked this conversation as resolved.
Show resolved Hide resolved

impl fmt::Display for TryFromIntError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
#[allow(deprecated)]
std::error::Error::description(&self).fmt(fmt)
}
}

impl std::error::Error for TryFromIntError {
#[allow(deprecated)]
fn description(&self) -> &str {
"out of range integral type conversion attempted"
}
}
Ekleog marked this conversation as resolved.
Show resolved Hide resolved

macro_rules! number_try_from {
($($x:ident)*) => ($(
impl TryFrom<$x> for Number {
type Error = $x;
Ekleog marked this conversation as resolved.
Show resolved Hide resolved

#[inline]
fn try_from(x: $x) -> Result<Number, Self::Error> {
let x_f64 = x as f64;
if x_f64 >= Number::MIN_SAFE_INTEGER && x_f64 <= Number::MAX_SAFE_INTEGER {
Ok(Number::unchecked_from_js(JsValue::from(x_f64)))
Ekleog marked this conversation as resolved.
Show resolved Hide resolved
} else {
Err(x)
}
}
}
)*)
}
number_try_from!(i64 u64 i128 u128);

// TODO: add this on the next major version, when blanket impl is removed
/*
impl convert::TryFrom<JsValue> for Number {
Expand Down
20 changes: 19 additions & 1 deletion crates/js-sys/tests/wasm/Number.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::f64::{INFINITY, NAN};
use std::{
convert::TryFrom,
f64::{INFINITY, NAN},
daxpedda marked this conversation as resolved.
Show resolved Hide resolved
};

use js_sys::*;
use wasm_bindgen::prelude::*;
Expand Down Expand Up @@ -71,6 +74,21 @@ fn new() {
assert_eq!(Number::from(v).value_of(), 42.);
}

#[wasm_bindgen_test]
fn try_from() {
assert_eq!(Number::try_from(42u128).unwrap(), 42.);
assert_eq!(
Number::try_from(Number::MAX_SAFE_INTEGER as u64).unwrap(),
Number::MAX_SAFE_INTEGER
);
assert_eq!(
Number::try_from(Number::MIN_SAFE_INTEGER as i128).unwrap(),
Number::MIN_SAFE_INTEGER
);
assert!(Number::try_from(Number::MAX_SAFE_INTEGER as u128 + 1).is_err());
assert!(Number::try_from(Number::MIN_SAFE_INTEGER as i64 - 1).is_err());
}

#[wasm_bindgen_test]
fn parse_int_float() {
assert_eq!(Number::parse_int("42", 10), 42.);
Expand Down