From 557e2e65188470e578f19a8364d7e6b23eec12fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Gaspard?= Date: Fri, 23 Feb 2024 09:06:00 +0100 Subject: [PATCH] Introduce impl TryFrom for Number that succeeds iff the value is within the safe range (#3847) --- CHANGELOG.md | 3 +++ crates/js-sys/src/lib.rs | 31 ++++++++++++++++++++++++++++++ crates/js-sys/tests/wasm/Number.rs | 20 ++++++++++++++++++- 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f35f358483b..16e9e1d3963 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ * 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. + [#3847](https://github.com/rustwasm/wasm-bindgen/pull/3847) + ### Fixed * Make .wasm output deterministic when using `--reference-types`. diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index b18faf6cae0..a244b7b70a0 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -2793,6 +2793,37 @@ 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. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct TryFromIntError(()); + +impl fmt::Display for TryFromIntError { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str("out of range integral type conversion attempted") + } +} + +impl std::error::Error for TryFromIntError {} + +macro_rules! number_try_from { + ($($x:ident)*) => ($( + impl TryFrom<$x> for Number { + type Error = TryFromIntError; + + #[inline] + fn try_from(x: $x) -> Result { + let x_f64 = x as f64; + if x_f64 >= Number::MIN_SAFE_INTEGER && x_f64 <= Number::MAX_SAFE_INTEGER { + Ok(Number::from(x_f64)) + } else { + Err(TryFromIntError(())) + } + } + } + )*) +} +number_try_from!(i64 u64 i128 u128); + // TODO: add this on the next major version, when blanket impl is removed /* impl convert::TryFrom for Number { diff --git a/crates/js-sys/tests/wasm/Number.rs b/crates/js-sys/tests/wasm/Number.rs index 94fd551f33e..31f3f565fe8 100644 --- a/crates/js-sys/tests/wasm/Number.rs +++ b/crates/js-sys/tests/wasm/Number.rs @@ -1,4 +1,7 @@ -use std::f64::{INFINITY, NAN}; +use std::{ + convert::TryFrom, + f64::{INFINITY, NAN}, +}; use js_sys::*; use wasm_bindgen::prelude::*; @@ -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.);