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