1 // This file is part of ICU4X. For terms of use, please see the file 2 // called LICENSE at the top level of the ICU4X source tree 3 // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). 4 5 use ffi::FixedDecimalSignedRoundingMode; 6 7 #[diplomat::bridge] 8 #[diplomat::abi_rename = "icu4x_{0}_mv1"] 9 #[diplomat::attr(auto, namespace = "icu4x")] 10 pub mod ffi { 11 use alloc::boxed::Box; 12 13 use crate::errors::ffi::{FixedDecimalLimitError, FixedDecimalParseError}; 14 15 use writeable::Writeable; 16 17 #[diplomat::opaque] 18 #[diplomat::rust_link(fixed_decimal::FixedDecimal, Struct)] 19 #[diplomat::rust_link(fixed_decimal::Decimal, Typedef, hidden)] 20 pub struct Decimal(pub fixed_decimal::Decimal); 21 22 /// The sign of a FixedDecimal, as shown in formatting. 23 #[diplomat::rust_link(fixed_decimal::Sign, Enum)] 24 #[diplomat::enum_convert(fixed_decimal::Sign, needs_wildcard)] 25 pub enum FixedDecimalSign { 26 /// No sign (implicitly positive, e.g., 1729). 27 None, 28 /// A negative sign, e.g., -1729. 29 Negative, 30 /// An explicit positive sign, e.g., +1729. 31 Positive, 32 } 33 34 /// ECMA-402 compatible sign display preference. 35 #[diplomat::rust_link(fixed_decimal::SignDisplay, Enum)] 36 #[diplomat::enum_convert(fixed_decimal::SignDisplay, needs_wildcard)] 37 pub enum FixedDecimalSignDisplay { 38 Auto, 39 Never, 40 Always, 41 ExceptZero, 42 Negative, 43 } 44 45 /// Increment used in a rounding operation. 46 #[diplomat::rust_link(fixed_decimal::RoundingIncrement, Enum)] 47 #[diplomat::enum_convert(fixed_decimal::RoundingIncrement, needs_wildcard)] 48 pub enum FixedDecimalRoundingIncrement { 49 MultiplesOf1, 50 MultiplesOf2, 51 MultiplesOf5, 52 MultiplesOf25, 53 } 54 55 /// Mode used in a rounding operation for signed numbers. 56 #[diplomat::rust_link(fixed_decimal::SignedRoundingMode, Enum)] 57 pub enum FixedDecimalSignedRoundingMode { 58 Expand, 59 Trunc, 60 HalfExpand, 61 HalfTrunc, 62 HalfEven, 63 Ceil, 64 Floor, 65 HalfCeil, 66 HalfFloor, 67 } 68 69 impl Decimal { 70 /// Construct an [`Decimal`] from an integer. 71 #[diplomat::rust_link(fixed_decimal::FixedDecimal, Struct)] 72 #[diplomat::attr(dart, disable)] 73 #[diplomat::attr(js, rename = "from_number")] 74 #[diplomat::attr(supports = method_overloading, rename = "from")] 75 #[diplomat::attr(auto, named_constructor)] from_int32(v: i32) -> Box<Decimal>76 pub fn from_int32(v: i32) -> Box<Decimal> { 77 Box::new(Decimal(fixed_decimal::Decimal::from(v))) 78 } 79 80 /// Construct an [`Decimal`] from an integer. 81 #[diplomat::rust_link(fixed_decimal::FixedDecimal, Struct)] 82 #[diplomat::attr(dart, disable)] 83 #[diplomat::attr(js, disable)] 84 #[diplomat::attr(supports = method_overloading, rename = "from")] 85 #[diplomat::attr(auto, named_constructor)] from_uint32(v: u32) -> Box<Decimal>86 pub fn from_uint32(v: u32) -> Box<Decimal> { 87 Box::new(Decimal(fixed_decimal::Decimal::from(v))) 88 } 89 90 /// Construct an [`Decimal`] from an integer. 91 #[diplomat::rust_link(fixed_decimal::FixedDecimal, Struct)] 92 #[diplomat::attr(dart, rename = "from_int")] 93 #[diplomat::attr(js, rename = "from_big_int")] 94 #[diplomat::attr(supports = method_overloading, rename = "from")] 95 #[diplomat::attr(auto, named_constructor)] from_int64(v: i64) -> Box<Decimal>96 pub fn from_int64(v: i64) -> Box<Decimal> { 97 Box::new(Decimal(fixed_decimal::Decimal::from(v))) 98 } 99 100 /// Construct an [`Decimal`] from an integer. 101 #[diplomat::rust_link(fixed_decimal::FixedDecimal, Struct)] 102 #[diplomat::attr(any(dart, js), disable)] 103 #[diplomat::attr(supports = method_overloading, rename = "from")] 104 #[diplomat::attr(auto, named_constructor)] from_uint64(v: u64) -> Box<Decimal>105 pub fn from_uint64(v: u64) -> Box<Decimal> { 106 Box::new(Decimal(fixed_decimal::Decimal::from(v))) 107 } 108 109 /// Construct an [`Decimal`] from an integer-valued float 110 #[diplomat::rust_link(fixed_decimal::FixedDecimal::try_from_f64, FnInStruct)] 111 #[diplomat::rust_link(fixed_decimal::FloatPrecision, Enum)] 112 #[diplomat::rust_link(fixed_decimal::DoublePrecision, Enum, hidden)] 113 #[diplomat::attr(any(dart, js), disable)] 114 #[diplomat::attr(all(supports = fallible_constructors, supports = named_constructors), named_constructor)] from_double_with_integer_precision( f: f64, ) -> Result<Box<Decimal>, FixedDecimalLimitError>115 pub fn from_double_with_integer_precision( 116 f: f64, 117 ) -> Result<Box<Decimal>, FixedDecimalLimitError> { 118 let precision = fixed_decimal::DoublePrecision::Integer; 119 Ok(Box::new(Decimal(fixed_decimal::Decimal::try_from_f64( 120 f, precision, 121 )?))) 122 } 123 124 /// Construct an [`Decimal`] from an float, with a given power of 10 for the lower magnitude 125 #[diplomat::rust_link(fixed_decimal::FixedDecimal::try_from_f64, FnInStruct)] 126 #[diplomat::rust_link(fixed_decimal::FloatPrecision, Enum)] 127 #[diplomat::rust_link(fixed_decimal::DoublePrecision, Enum, hidden)] 128 #[diplomat::attr(js, rename = "from_number_with_lower_magnitude")] 129 #[diplomat::attr(all(supports = fallible_constructors, supports = named_constructors), named_constructor)] 130 #[diplomat::demo(default_constructor)] from_double_with_lower_magnitude( f: f64, magnitude: i16, ) -> Result<Box<Decimal>, FixedDecimalLimitError>131 pub fn from_double_with_lower_magnitude( 132 f: f64, 133 magnitude: i16, 134 ) -> Result<Box<Decimal>, FixedDecimalLimitError> { 135 let precision = fixed_decimal::DoublePrecision::Magnitude(magnitude); 136 Ok(Box::new(Decimal(fixed_decimal::Decimal::try_from_f64( 137 f, precision, 138 )?))) 139 } 140 141 /// Construct an [`Decimal`] from an float, for a given number of significant digits 142 #[diplomat::rust_link(fixed_decimal::FixedDecimal::try_from_f64, FnInStruct)] 143 #[diplomat::rust_link(fixed_decimal::FloatPrecision, Enum)] 144 #[diplomat::rust_link(fixed_decimal::DoublePrecision, Enum, hidden)] 145 #[diplomat::attr(js, rename = "from_number_with_significant_digits")] 146 #[diplomat::attr(all(supports = fallible_constructors, supports = named_constructors), named_constructor)] from_double_with_significant_digits( f: f64, digits: u8, ) -> Result<Box<Decimal>, FixedDecimalLimitError>147 pub fn from_double_with_significant_digits( 148 f: f64, 149 digits: u8, 150 ) -> Result<Box<Decimal>, FixedDecimalLimitError> { 151 let precision = fixed_decimal::DoublePrecision::SignificantDigits(digits); 152 Ok(Box::new(Decimal(fixed_decimal::Decimal::try_from_f64( 153 f, precision, 154 )?))) 155 } 156 157 /// Construct an [`Decimal`] from an float, with enough digits to recover 158 /// the original floating point in IEEE 754 without needing trailing zeros 159 #[diplomat::rust_link(fixed_decimal::Decimal::try_from_f64, FnInStruct)] 160 #[diplomat::rust_link(fixed_decimal::FloatPrecision, Enum)] 161 #[diplomat::rust_link(fixed_decimal::DoublePrecision, Enum, hidden)] 162 #[diplomat::attr(js, rename = "from_number_with_round_trip_precision")] 163 #[diplomat::attr(all(supports = fallible_constructors, supports = named_constructors), named_constructor)] from_double_with_round_trip_precision( f: f64, ) -> Result<Box<Decimal>, FixedDecimalLimitError>164 pub fn from_double_with_round_trip_precision( 165 f: f64, 166 ) -> Result<Box<Decimal>, FixedDecimalLimitError> { 167 let precision = fixed_decimal::DoublePrecision::RoundTrip; 168 Ok(Box::new(Decimal(fixed_decimal::Decimal::try_from_f64( 169 f, precision, 170 )?))) 171 } 172 173 /// Construct an [`Decimal`] from a string. 174 #[diplomat::rust_link(fixed_decimal::Decimal::try_from_str, FnInStruct)] 175 #[diplomat::rust_link(fixed_decimal::Decimal::try_from_utf8, FnInStruct, hidden)] 176 #[diplomat::rust_link(fixed_decimal::Decimal::from_str, FnInStruct, hidden)] 177 #[diplomat::attr(all(supports = fallible_constructors, supports = named_constructors), named_constructor)] from_string(v: &DiplomatStr) -> Result<Box<Decimal>, FixedDecimalParseError>178 pub fn from_string(v: &DiplomatStr) -> Result<Box<Decimal>, FixedDecimalParseError> { 179 Ok(Box::new(Decimal(fixed_decimal::Decimal::try_from_utf8(v)?))) 180 } 181 182 #[diplomat::rust_link(fixed_decimal::UnsignedDecimal::digit_at, FnInStruct)] digit_at(&self, magnitude: i16) -> u8183 pub fn digit_at(&self, magnitude: i16) -> u8 { 184 self.0.absolute.digit_at(magnitude) 185 } 186 187 #[diplomat::rust_link(fixed_decimal::UnsignedDecimal::magnitude_range, FnInStruct)] 188 #[diplomat::attr(auto, getter)] magnitude_start(&self) -> i16189 pub fn magnitude_start(&self) -> i16 { 190 *self.0.absolute.magnitude_range().start() 191 } 192 193 #[diplomat::rust_link(fixed_decimal::UnsignedDecimal::magnitude_range, FnInStruct)] 194 #[diplomat::attr(auto, getter)] magnitude_end(&self) -> i16195 pub fn magnitude_end(&self) -> i16 { 196 *self.0.absolute.magnitude_range().end() 197 } 198 199 #[diplomat::rust_link(fixed_decimal::UnsignedDecimal::nonzero_magnitude_start, FnInStruct)] 200 #[diplomat::attr(auto, getter)] nonzero_magnitude_start(&self) -> i16201 pub fn nonzero_magnitude_start(&self) -> i16 { 202 self.0.absolute.nonzero_magnitude_start() 203 } 204 205 #[diplomat::rust_link(fixed_decimal::UnsignedDecimal::nonzero_magnitude_end, FnInStruct)] 206 #[diplomat::attr(auto, getter)] nonzero_magnitude_end(&self) -> i16207 pub fn nonzero_magnitude_end(&self) -> i16 { 208 self.0.absolute.nonzero_magnitude_end() 209 } 210 211 #[diplomat::rust_link(fixed_decimal::UnsignedDecimal::is_zero, FnInStruct)] 212 #[diplomat::attr(auto, getter)] is_zero(&self) -> bool213 pub fn is_zero(&self) -> bool { 214 self.0.absolute.is_zero() 215 } 216 217 /// Multiply the [`Decimal`] by a given power of ten. 218 #[diplomat::rust_link(fixed_decimal::Decimal::multiply_pow10, FnInStruct)] 219 #[diplomat::rust_link(fixed_decimal::Decimal::multiplied_pow10, FnInStruct, hidden)] multiply_pow10(&mut self, power: i16)220 pub fn multiply_pow10(&mut self, power: i16) { 221 self.0.multiply_pow10(power) 222 } 223 224 #[diplomat::rust_link(fixed_decimal::Decimal::sign, FnInStruct)] 225 #[diplomat::attr(auto, getter)] sign(&self) -> FixedDecimalSign226 pub fn sign(&self) -> FixedDecimalSign { 227 self.0.sign().into() 228 } 229 230 /// Set the sign of the [`Decimal`]. 231 #[diplomat::rust_link(fixed_decimal::Decimal::set_sign, FnInStruct)] 232 #[diplomat::rust_link(fixed_decimal::Decimal::with_sign, FnInStruct, hidden)] 233 #[diplomat::attr(auto, setter = "sign")] set_sign(&mut self, sign: FixedDecimalSign)234 pub fn set_sign(&mut self, sign: FixedDecimalSign) { 235 self.0.set_sign(sign.into()) 236 } 237 238 #[diplomat::rust_link(fixed_decimal::Decimal::apply_sign_display, FnInStruct)] 239 #[diplomat::rust_link(fixed_decimal::Decimal::with_sign_display, FnInStruct, hidden)] apply_sign_display(&mut self, sign_display: FixedDecimalSignDisplay)240 pub fn apply_sign_display(&mut self, sign_display: FixedDecimalSignDisplay) { 241 self.0.apply_sign_display(sign_display.into()) 242 } 243 244 #[diplomat::rust_link(fixed_decimal::FixedDecimal::trim_start, FnInStruct)] 245 #[diplomat::rust_link(fixed_decimal::FixedDecimal::trimmed_start, FnInStruct, hidden)] trim_start(&mut self)246 pub fn trim_start(&mut self) { 247 self.0.absolute.trim_start() 248 } 249 250 #[diplomat::rust_link(fixed_decimal::FixedDecimal::trim_end, FnInStruct)] 251 #[diplomat::rust_link(fixed_decimal::FixedDecimal::trimmed_end, FnInStruct, hidden)] trim_end(&mut self)252 pub fn trim_end(&mut self) { 253 self.0.absolute.trim_end() 254 } 255 256 #[diplomat::rust_link(fixed_decimal::UnsignedDecimal::trim_end_if_integer, FnInStruct)] 257 #[diplomat::rust_link( 258 fixed_decimal::UnsignedDecimal::trimmed_end_if_integer, 259 FnInStruct, 260 hidden 261 )] trim_end_if_integer(&mut self)262 pub fn trim_end_if_integer(&mut self) { 263 self.0.absolute.trim_end_if_integer() 264 } 265 266 /// Zero-pad the [`Decimal`] on the left to a particular position 267 #[diplomat::rust_link(fixed_decimal::FixedDecimal::pad_start, FnInStruct)] 268 #[diplomat::rust_link(fixed_decimal::FixedDecimal::padded_start, FnInStruct, hidden)] pad_start(&mut self, position: i16)269 pub fn pad_start(&mut self, position: i16) { 270 self.0.absolute.pad_start(position) 271 } 272 273 /// Zero-pad the [`Decimal`] on the right to a particular position 274 #[diplomat::rust_link(fixed_decimal::FixedDecimal::pad_end, FnInStruct)] 275 #[diplomat::rust_link(fixed_decimal::FixedDecimal::padded_end, FnInStruct, hidden)] pad_end(&mut self, position: i16)276 pub fn pad_end(&mut self, position: i16) { 277 self.0.absolute.pad_end(position) 278 } 279 280 /// Truncate the [`Decimal`] on the left to a particular position, deleting digits if necessary. This is useful for, e.g. abbreviating years 281 /// ("2022" -> "22") 282 #[diplomat::rust_link(fixed_decimal::FixedDecimal::set_max_position, FnInStruct)] 283 #[diplomat::rust_link(fixed_decimal::FixedDecimal::with_max_position, FnInStruct, hidden)] set_max_position(&mut self, position: i16)284 pub fn set_max_position(&mut self, position: i16) { 285 self.0.absolute.set_max_position(position) 286 } 287 288 /// Round the number at a particular digit position. 289 /// 290 /// This uses half to even rounding, which resolves ties by selecting the nearest 291 /// even integer to the original value. 292 #[diplomat::rust_link(fixed_decimal::Decimal::round, FnInStruct)] 293 #[diplomat::rust_link(fixed_decimal::Decimal::rounded, FnInStruct, hidden)] round(&mut self, position: i16)294 pub fn round(&mut self, position: i16) { 295 self.0.round(position) 296 } 297 298 #[diplomat::rust_link(fixed_decimal::Decimal::ceil, FnInStruct)] 299 #[diplomat::rust_link(fixed_decimal::Decimal::ceiled, FnInStruct, hidden)] ceil(&mut self, position: i16)300 pub fn ceil(&mut self, position: i16) { 301 self.0.ceil(position) 302 } 303 304 #[diplomat::rust_link(fixed_decimal::Decimal::expand, FnInStruct)] 305 #[diplomat::rust_link(fixed_decimal::Decimal::expanded, FnInStruct, hidden)] expand(&mut self, position: i16)306 pub fn expand(&mut self, position: i16) { 307 self.0.expand(position) 308 } 309 310 #[diplomat::rust_link(fixed_decimal::Decimal::floor, FnInStruct)] 311 #[diplomat::rust_link(fixed_decimal::Decimal::floored, FnInStruct, hidden)] floor(&mut self, position: i16)312 pub fn floor(&mut self, position: i16) { 313 self.0.floor(position) 314 } 315 316 #[diplomat::rust_link(fixed_decimal::Decimal::trunc, FnInStruct)] 317 #[diplomat::rust_link(fixed_decimal::Decimal::trunced, FnInStruct, hidden)] trunc(&mut self, position: i16)318 pub fn trunc(&mut self, position: i16) { 319 self.0.trunc(position) 320 } 321 322 #[diplomat::rust_link(fixed_decimal::Decimal::round_with_mode, FnInStruct)] 323 #[diplomat::rust_link(fixed_decimal::Decimal::rounded_with_mode, FnInStruct, hidden)] round_with_mode(&mut self, position: i16, mode: FixedDecimalSignedRoundingMode)324 pub fn round_with_mode(&mut self, position: i16, mode: FixedDecimalSignedRoundingMode) { 325 self.0.round_with_mode(position, mode.into()) 326 } 327 328 #[diplomat::rust_link( 329 fixed_decimal::FixedDecimal::round_with_mode_and_increment, 330 FnInStruct 331 )] 332 #[diplomat::rust_link( 333 fixed_decimal::FixedDecimal::rounded_with_mode_and_increment, 334 FnInStruct, 335 hidden 336 )] round_with_mode_and_increment( &mut self, position: i16, mode: FixedDecimalSignedRoundingMode, increment: FixedDecimalRoundingIncrement, )337 pub fn round_with_mode_and_increment( 338 &mut self, 339 position: i16, 340 mode: FixedDecimalSignedRoundingMode, 341 increment: FixedDecimalRoundingIncrement, 342 ) { 343 self.0 344 .round_with_mode_and_increment(position, mode.into(), increment.into()) 345 } 346 347 /// Concatenates `other` to the end of `self`. 348 /// 349 /// If successful, `other` will be set to 0 and a successful status is returned. 350 /// 351 /// If not successful, `other` will be unchanged and an error is returned. 352 #[diplomat::rust_link(fixed_decimal::FixedDecimal::concatenate_end, FnInStruct)] 353 #[diplomat::rust_link(fixed_decimal::FixedDecimal::concatenated_end, FnInStruct, hidden)] concatenate_end(&mut self, other: &mut Decimal) -> Result<(), ()>354 pub fn concatenate_end(&mut self, other: &mut Decimal) -> Result<(), ()> { 355 let x = core::mem::take(&mut other.0); 356 self.0.absolute.concatenate_end(x.absolute).map_err(|y| { 357 other.0.absolute = y; 358 }) 359 } 360 361 /// Format the [`Decimal`] as a string. 362 #[diplomat::rust_link(fixed_decimal::FixedDecimal::write_to, FnInStruct)] 363 #[diplomat::rust_link(fixed_decimal::FixedDecimal::to_string, FnInStruct, hidden)] 364 #[diplomat::attr(auto, stringifier)] to_string(&self, to: &mut diplomat_runtime::DiplomatWrite)365 pub fn to_string(&self, to: &mut diplomat_runtime::DiplomatWrite) { 366 let _ = self.0.write_to(to); 367 } 368 } 369 } 370 371 impl From<FixedDecimalSignedRoundingMode> for fixed_decimal::SignedRoundingMode { from(mode: FixedDecimalSignedRoundingMode) -> Self372 fn from(mode: FixedDecimalSignedRoundingMode) -> Self { 373 match mode { 374 FixedDecimalSignedRoundingMode::Expand => fixed_decimal::SignedRoundingMode::Unsigned( 375 fixed_decimal::UnsignedRoundingMode::Expand, 376 ), 377 FixedDecimalSignedRoundingMode::Trunc => fixed_decimal::SignedRoundingMode::Unsigned( 378 fixed_decimal::UnsignedRoundingMode::Trunc, 379 ), 380 FixedDecimalSignedRoundingMode::HalfExpand => { 381 fixed_decimal::SignedRoundingMode::Unsigned( 382 fixed_decimal::UnsignedRoundingMode::HalfExpand, 383 ) 384 } 385 FixedDecimalSignedRoundingMode::HalfTrunc => { 386 fixed_decimal::SignedRoundingMode::Unsigned( 387 fixed_decimal::UnsignedRoundingMode::HalfTrunc, 388 ) 389 } 390 FixedDecimalSignedRoundingMode::HalfEven => { 391 fixed_decimal::SignedRoundingMode::Unsigned( 392 fixed_decimal::UnsignedRoundingMode::HalfEven, 393 ) 394 } 395 FixedDecimalSignedRoundingMode::Ceil => fixed_decimal::SignedRoundingMode::Ceil, 396 FixedDecimalSignedRoundingMode::Floor => fixed_decimal::SignedRoundingMode::Floor, 397 FixedDecimalSignedRoundingMode::HalfCeil => fixed_decimal::SignedRoundingMode::HalfCeil, 398 FixedDecimalSignedRoundingMode::HalfFloor => { 399 fixed_decimal::SignedRoundingMode::HalfFloor 400 } 401 } 402 } 403 } 404