// This file is part of ICU4X. For terms of use, please see the file // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). use ffi::DecimalSignedRoundingMode; #[diplomat::bridge] #[diplomat::abi_rename = "icu4x_{0}_mv1"] pub mod ffi { use alloc::boxed::Box; use crate::unstable::errors::ffi::{DecimalLimitError, DecimalParseError}; use writeable::Writeable; #[diplomat::opaque] #[diplomat::rust_link(fixed_decimal::Decimal, Typedef)] pub struct Decimal(pub fixed_decimal::Decimal); /// The sign of a Decimal, as shown in formatting. #[diplomat::rust_link(fixed_decimal::Sign, Enum)] #[diplomat::enum_convert(fixed_decimal::Sign, needs_wildcard)] #[non_exhaustive] pub enum DecimalSign { /// No sign (implicitly positive, e.g., 1729). #[diplomat::attr(auto, default)] None, /// A negative sign, e.g., -1729. Negative, /// An explicit positive sign, e.g., +1729. Positive, } /// ECMA-402 compatible sign display preference. #[diplomat::rust_link(fixed_decimal::SignDisplay, Enum)] #[diplomat::enum_convert(fixed_decimal::SignDisplay, needs_wildcard)] #[non_exhaustive] pub enum DecimalSignDisplay { #[diplomat::attr(auto, default)] Auto, Never, Always, ExceptZero, Negative, } /// Increment used in a rounding operation. #[diplomat::rust_link(fixed_decimal::RoundingIncrement, Enum)] #[diplomat::enum_convert(fixed_decimal::RoundingIncrement, needs_wildcard)] #[non_exhaustive] pub enum DecimalRoundingIncrement { #[diplomat::attr(auto, default)] MultiplesOf1, MultiplesOf2, MultiplesOf5, MultiplesOf25, } /// Mode used in a rounding operation for signed numbers. #[diplomat::rust_link(fixed_decimal::SignedRoundingMode, Enum)] #[non_exhaustive] pub enum DecimalSignedRoundingMode { Expand, Trunc, HalfExpand, HalfTrunc, HalfEven, Ceil, Floor, HalfCeil, HalfFloor, } impl Decimal { /// Construct an [`Decimal`] from an integer. #[diplomat::rust_link(fixed_decimal::Decimal, Typedef)] #[diplomat::attr(dart, disable)] #[diplomat::attr(js, rename = "from_number")] #[diplomat::attr(supports = method_overloading, rename = "from")] #[diplomat::attr(auto, named_constructor)] pub fn from_int32(v: i32) -> Box { Box::new(Decimal(fixed_decimal::Decimal::from(v))) } /// Construct an [`Decimal`] from an integer. #[diplomat::rust_link(fixed_decimal::Decimal, Typedef)] #[diplomat::attr(dart, disable)] #[diplomat::attr(js, disable)] #[diplomat::attr(supports = method_overloading, rename = "from")] #[diplomat::attr(auto, named_constructor)] pub fn from_uint32(v: u32) -> Box { Box::new(Decimal(fixed_decimal::Decimal::from(v))) } /// Construct an [`Decimal`] from an integer. #[diplomat::rust_link(fixed_decimal::Decimal, Typedef)] #[diplomat::attr(dart, rename = "from_int")] #[diplomat::attr(js, rename = "from_big_int")] #[diplomat::attr(supports = method_overloading, rename = "from")] #[diplomat::attr(auto, named_constructor)] pub fn from_int64(v: i64) -> Box { Box::new(Decimal(fixed_decimal::Decimal::from(v))) } /// Construct an [`Decimal`] from an integer. #[diplomat::rust_link(fixed_decimal::Decimal, Typedef)] #[diplomat::attr(any(dart, js), disable)] #[diplomat::attr(supports = method_overloading, rename = "from")] #[diplomat::attr(auto, named_constructor)] pub fn from_uint64(v: u64) -> Box { Box::new(Decimal(fixed_decimal::Decimal::from(v))) } /// Construct an [`Decimal`] from an integer-valued float #[diplomat::rust_link(fixed_decimal::Decimal::try_from_f64, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::FloatPrecision, Enum)] #[diplomat::rust_link(fixed_decimal::DoublePrecision, Enum, hidden)] #[diplomat::attr(any(dart, js), disable)] #[diplomat::attr(all(supports = fallible_constructors, supports = named_constructors), named_constructor)] pub fn from_double_with_integer_precision( f: f64, ) -> Result, DecimalLimitError> { let precision = fixed_decimal::DoublePrecision::Integer; Ok(Box::new(Decimal(fixed_decimal::Decimal::try_from_f64( f, precision, )?))) } /// Construct an [`Decimal`] from an float, with a given power of 10 for the lower magnitude #[diplomat::rust_link(fixed_decimal::Decimal::try_from_f64, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::FloatPrecision, Enum)] #[diplomat::rust_link(fixed_decimal::DoublePrecision, Enum, hidden)] #[diplomat::attr(js, rename = "from_number_with_lower_magnitude")] #[diplomat::attr(all(supports = fallible_constructors, supports = named_constructors), named_constructor)] pub fn from_double_with_lower_magnitude( f: f64, magnitude: i16, ) -> Result, DecimalLimitError> { let precision = fixed_decimal::DoublePrecision::Magnitude(magnitude); Ok(Box::new(Decimal(fixed_decimal::Decimal::try_from_f64( f, precision, )?))) } /// Construct an [`Decimal`] from an float, for a given number of significant digits #[diplomat::rust_link(fixed_decimal::Decimal::try_from_f64, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::FloatPrecision, Enum)] #[diplomat::rust_link(fixed_decimal::DoublePrecision, Enum, hidden)] #[diplomat::attr(js, rename = "from_number_with_significant_digits")] #[diplomat::attr(all(supports = fallible_constructors, supports = named_constructors), named_constructor)] pub fn from_double_with_significant_digits( f: f64, digits: u8, ) -> Result, DecimalLimitError> { let precision = fixed_decimal::DoublePrecision::SignificantDigits(digits); Ok(Box::new(Decimal(fixed_decimal::Decimal::try_from_f64( f, precision, )?))) } /// Construct an [`Decimal`] from an float, with enough digits to recover /// the original floating point in IEEE 754 without needing trailing zeros #[diplomat::rust_link(fixed_decimal::Decimal::try_from_f64, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::FloatPrecision, Enum)] #[diplomat::rust_link(fixed_decimal::DoublePrecision, Enum, hidden)] #[diplomat::attr(js, rename = "from_number_with_round_trip_precision")] #[diplomat::attr(all(supports = fallible_constructors, supports = named_constructors), named_constructor)] pub fn from_double_with_round_trip_precision( f: f64, ) -> Result, DecimalLimitError> { let precision = fixed_decimal::DoublePrecision::RoundTrip; Ok(Box::new(Decimal(fixed_decimal::Decimal::try_from_f64( f, precision, )?))) } /// Construct an [`Decimal`] from a string. #[diplomat::rust_link(fixed_decimal::Decimal::try_from_str, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::Decimal::try_from_utf8, FnInTypedef, hidden)] #[diplomat::rust_link(fixed_decimal::Decimal::from_str, FnInTypedef, hidden)] #[diplomat::attr(all(supports = fallible_constructors, supports = named_constructors), named_constructor)] #[diplomat::demo(default_constructor)] pub fn from_string(v: &DiplomatStr) -> Result, DecimalParseError> { Ok(Box::new(Decimal(fixed_decimal::Decimal::try_from_utf8(v)?))) } #[diplomat::rust_link(fixed_decimal::Decimal::digit_at, FnInTypedef)] pub fn digit_at(&self, magnitude: i16) -> u8 { self.0.absolute.digit_at(magnitude) } #[diplomat::rust_link(fixed_decimal::Decimal::magnitude_range, FnInTypedef)] #[diplomat::attr(auto, getter)] pub fn magnitude_start(&self) -> i16 { *self.0.absolute.magnitude_range().start() } #[diplomat::rust_link(fixed_decimal::Decimal::magnitude_range, FnInTypedef)] #[diplomat::attr(auto, getter)] pub fn magnitude_end(&self) -> i16 { *self.0.absolute.magnitude_range().end() } #[diplomat::rust_link(fixed_decimal::Decimal::nonzero_magnitude_start, FnInTypedef)] #[diplomat::attr(auto, getter)] pub fn nonzero_magnitude_start(&self) -> i16 { self.0.absolute.nonzero_magnitude_start() } #[diplomat::rust_link(fixed_decimal::Decimal::nonzero_magnitude_end, FnInTypedef)] #[diplomat::attr(auto, getter)] pub fn nonzero_magnitude_end(&self) -> i16 { self.0.absolute.nonzero_magnitude_end() } #[diplomat::rust_link(fixed_decimal::Decimal::is_zero, FnInTypedef)] #[diplomat::attr(auto, getter)] pub fn is_zero(&self) -> bool { self.0.absolute.is_zero() } /// Multiply the [`Decimal`] by a given power of ten. #[diplomat::rust_link(fixed_decimal::Decimal::multiply_pow10, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::Decimal::multiplied_pow10, FnInTypedef, hidden)] pub fn multiply_pow10(&mut self, power: i16) { self.0.multiply_pow10(power) } #[diplomat::rust_link(fixed_decimal::Decimal::sign, FnInTypedef)] #[diplomat::attr(auto, getter)] pub fn sign(&self) -> DecimalSign { self.0.sign().into() } /// Set the sign of the [`Decimal`]. #[diplomat::rust_link(fixed_decimal::Decimal::set_sign, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::Decimal::with_sign, FnInTypedef, hidden)] #[diplomat::attr(auto, setter = "sign")] pub fn set_sign(&mut self, sign: DecimalSign) { self.0.set_sign(sign.into()) } #[diplomat::rust_link(fixed_decimal::Decimal::apply_sign_display, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::Decimal::with_sign_display, FnInTypedef, hidden)] pub fn apply_sign_display(&mut self, sign_display: DecimalSignDisplay) { self.0.apply_sign_display(sign_display.into()) } #[diplomat::rust_link(fixed_decimal::Decimal::trim_start, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::Decimal::trimmed_start, FnInTypedef, hidden)] pub fn trim_start(&mut self) { self.0.absolute.trim_start() } #[diplomat::rust_link(fixed_decimal::Decimal::trim_end, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::Decimal::trimmed_end, FnInTypedef, hidden)] pub fn trim_end(&mut self) { self.0.absolute.trim_end() } #[diplomat::rust_link(fixed_decimal::Decimal::trim_end_if_integer, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::Decimal::trimmed_end_if_integer, FnInTypedef, hidden)] pub fn trim_end_if_integer(&mut self) { self.0.absolute.trim_end_if_integer() } /// Zero-pad the [`Decimal`] on the left to a particular position #[diplomat::rust_link(fixed_decimal::Decimal::pad_start, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::Decimal::padded_start, FnInTypedef, hidden)] pub fn pad_start(&mut self, position: i16) { self.0.absolute.pad_start(position) } /// Zero-pad the [`Decimal`] on the right to a particular position #[diplomat::rust_link(fixed_decimal::Decimal::pad_end, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::Decimal::padded_end, FnInTypedef, hidden)] pub fn pad_end(&mut self, position: i16) { self.0.absolute.pad_end(position) } /// Truncate the [`Decimal`] on the left to a particular position, deleting digits if necessary. This is useful for, e.g. abbreviating years /// ("2022" -> "22") #[diplomat::rust_link(fixed_decimal::Decimal::set_max_position, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::Decimal::with_max_position, FnInTypedef, hidden)] pub fn set_max_position(&mut self, position: i16) { self.0.absolute.set_max_position(position) } /// Round the number at a particular digit position. /// /// This uses half to even rounding, which resolves ties by selecting the nearest /// even integer to the original value. #[diplomat::rust_link(fixed_decimal::Decimal::round, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::Decimal::rounded, FnInTypedef, hidden)] pub fn round(&mut self, position: i16) { self.0.round(position) } #[diplomat::rust_link(fixed_decimal::Decimal::ceil, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::Decimal::ceiled, FnInTypedef, hidden)] pub fn ceil(&mut self, position: i16) { self.0.ceil(position) } #[diplomat::rust_link(fixed_decimal::Decimal::expand, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::Decimal::expanded, FnInTypedef, hidden)] pub fn expand(&mut self, position: i16) { self.0.expand(position) } #[diplomat::rust_link(fixed_decimal::Decimal::floor, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::Decimal::floored, FnInTypedef, hidden)] pub fn floor(&mut self, position: i16) { self.0.floor(position) } #[diplomat::rust_link(fixed_decimal::Decimal::trunc, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::Decimal::trunced, FnInTypedef, hidden)] pub fn trunc(&mut self, position: i16) { self.0.trunc(position) } #[diplomat::rust_link(fixed_decimal::Decimal::round_with_mode, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::Decimal::rounded_with_mode, FnInTypedef, hidden)] pub fn round_with_mode(&mut self, position: i16, mode: DecimalSignedRoundingMode) { self.0.round_with_mode(position, mode.into()) } #[diplomat::rust_link(fixed_decimal::Decimal::round_with_mode_and_increment, FnInTypedef)] #[diplomat::rust_link( fixed_decimal::Decimal::rounded_with_mode_and_increment, FnInTypedef, hidden )] pub fn round_with_mode_and_increment( &mut self, position: i16, mode: DecimalSignedRoundingMode, increment: DecimalRoundingIncrement, ) { self.0 .round_with_mode_and_increment(position, mode.into(), increment.into()) } /// Concatenates `other` to the end of `self`. /// /// If successful, `other` will be set to 0 and a successful status is returned. /// /// If not successful, `other` will be unchanged and an error is returned. #[diplomat::rust_link(fixed_decimal::Decimal::concatenate_end, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::Decimal::concatenated_end, FnInTypedef, hidden)] pub fn concatenate_end(&mut self, other: &mut Decimal) -> Result<(), ()> { let x = core::mem::take(&mut other.0); self.0.absolute.concatenate_end(x.absolute).map_err(|y| { other.0.absolute = y; }) } /// Format the [`Decimal`] as a string. #[diplomat::rust_link(fixed_decimal::Decimal::write_to, FnInTypedef)] #[diplomat::rust_link(fixed_decimal::Decimal::to_string, FnInTypedef, hidden)] #[diplomat::attr(auto, stringifier)] #[diplomat::attr(demo_gen, disable)] // this just returns the single constructor argument pub fn to_string(&self, to: &mut diplomat_runtime::DiplomatWrite) { let _ = self.0.write_to(to); } } } impl From for fixed_decimal::SignedRoundingMode { fn from(mode: DecimalSignedRoundingMode) -> Self { match mode { DecimalSignedRoundingMode::Expand => fixed_decimal::SignedRoundingMode::Unsigned( fixed_decimal::UnsignedRoundingMode::Expand, ), DecimalSignedRoundingMode::Trunc => fixed_decimal::SignedRoundingMode::Unsigned( fixed_decimal::UnsignedRoundingMode::Trunc, ), DecimalSignedRoundingMode::HalfExpand => fixed_decimal::SignedRoundingMode::Unsigned( fixed_decimal::UnsignedRoundingMode::HalfExpand, ), DecimalSignedRoundingMode::HalfTrunc => fixed_decimal::SignedRoundingMode::Unsigned( fixed_decimal::UnsignedRoundingMode::HalfTrunc, ), DecimalSignedRoundingMode::HalfEven => fixed_decimal::SignedRoundingMode::Unsigned( fixed_decimal::UnsignedRoundingMode::HalfEven, ), DecimalSignedRoundingMode::Ceil => fixed_decimal::SignedRoundingMode::Ceil, DecimalSignedRoundingMode::Floor => fixed_decimal::SignedRoundingMode::Floor, DecimalSignedRoundingMode::HalfCeil => fixed_decimal::SignedRoundingMode::HalfCeil, DecimalSignedRoundingMode::HalfFloor => fixed_decimal::SignedRoundingMode::HalfFloor, } } }