tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

decimal.rs (8778B)


      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 #[diplomat::bridge]
      6 #[diplomat::abi_rename = "icu4x_{0}_mv1"]
      7 #[diplomat::attr(auto, namespace = "icu4x")]
      8 pub mod ffi {
      9    use alloc::boxed::Box;
     10 
     11    #[cfg(any(feature = "compiled_data", feature = "buffer_provider"))]
     12    use crate::unstable::locale_core::ffi::Locale;
     13    #[cfg(feature = "buffer_provider")]
     14    use crate::unstable::provider::ffi::DataProvider;
     15    use crate::unstable::{errors::ffi::DataError, fixed_decimal::ffi::Decimal};
     16    use icu_decimal::options::DecimalFormatterOptions;
     17    #[cfg(any(feature = "compiled_data", feature = "buffer_provider"))]
     18    use icu_decimal::DecimalFormatterPreferences;
     19 
     20    use writeable::Writeable;
     21 
     22    #[diplomat::opaque]
     23    /// An ICU4X Decimal Format object, capable of formatting a [`Decimal`] as a string.
     24    #[diplomat::rust_link(icu::decimal::DecimalFormatter, Struct)]
     25    #[diplomat::rust_link(icu::decimal::FormattedDecimal, Struct, hidden)]
     26    pub struct DecimalFormatter(pub icu_decimal::DecimalFormatter);
     27 
     28    #[diplomat::rust_link(icu::decimal::options::GroupingStrategy, Enum)]
     29    #[diplomat::enum_convert(icu_decimal::options::GroupingStrategy, needs_wildcard)]
     30    pub enum DecimalGroupingStrategy {
     31        Auto,
     32        Never,
     33        Always,
     34        Min2,
     35    }
     36 
     37    impl DecimalFormatter {
     38        /// Creates a new [`DecimalFormatter`], using compiled data
     39        #[diplomat::rust_link(icu::decimal::DecimalFormatter::try_new, FnInStruct)]
     40        #[diplomat::attr(all(supports = fallible_constructors, supports = named_constructors), named_constructor = "with_grouping_strategy")]
     41        #[diplomat::demo(default_constructor)]
     42        #[cfg(feature = "compiled_data")]
     43        pub fn create_with_grouping_strategy(
     44            locale: &Locale,
     45            grouping_strategy: Option<DecimalGroupingStrategy>,
     46        ) -> Result<Box<DecimalFormatter>, DataError> {
     47            let prefs = DecimalFormatterPreferences::from(&locale.0);
     48 
     49            let mut options = DecimalFormatterOptions::default();
     50            options.grouping_strategy = grouping_strategy.map(Into::into);
     51            Ok(Box::new(DecimalFormatter(
     52                icu_decimal::DecimalFormatter::try_new(prefs, options)?,
     53            )))
     54        }
     55 
     56        /// Creates a new [`DecimalFormatter`], using a particular data source.
     57        #[diplomat::rust_link(icu::decimal::DecimalFormatter::try_new, FnInStruct)]
     58        #[diplomat::attr(all(supports = fallible_constructors, supports = named_constructors), named_constructor = "with_grouping_strategy_and_provider")]
     59        #[diplomat::demo(default_constructor)]
     60        #[cfg(feature = "buffer_provider")]
     61        pub fn create_with_grouping_strategy_and_provider(
     62            provider: &DataProvider,
     63            locale: &Locale,
     64            grouping_strategy: Option<DecimalGroupingStrategy>,
     65        ) -> Result<Box<DecimalFormatter>, DataError> {
     66            let prefs = DecimalFormatterPreferences::from(&locale.0);
     67 
     68            let mut options = DecimalFormatterOptions::default();
     69            options.grouping_strategy = grouping_strategy.map(Into::into);
     70            Ok(Box::new(DecimalFormatter(
     71                icu_decimal::DecimalFormatter::try_new_with_buffer_provider(
     72                    provider.get()?,
     73                    prefs,
     74                    options,
     75                )?,
     76            )))
     77        }
     78 
     79        /// Creates a new [`DecimalFormatter`] from preconstructed locale data.
     80        #[diplomat::rust_link(icu::decimal::provider::DecimalSymbolsV1, Struct)]
     81        #[allow(clippy::too_many_arguments)]
     82        pub fn create_with_manual_data(
     83            plus_sign_prefix: &DiplomatStr,
     84            plus_sign_suffix: &DiplomatStr,
     85            minus_sign_prefix: &DiplomatStr,
     86            minus_sign_suffix: &DiplomatStr,
     87            decimal_separator: &DiplomatStr,
     88            grouping_separator: &DiplomatStr,
     89            primary_group_size: u8,
     90            secondary_group_size: u8,
     91            min_group_size: u8,
     92            digits: &[DiplomatChar],
     93            grouping_strategy: Option<DecimalGroupingStrategy>,
     94        ) -> Result<Box<DecimalFormatter>, DataError> {
     95            use core::cell::RefCell;
     96            use icu_provider::prelude::*;
     97            use zerovec::VarZeroCow;
     98 
     99            fn str_to_cow(s: &'_ diplomat_runtime::DiplomatStr) -> VarZeroCow<'_, str> {
    100                if let Ok(s) = core::str::from_utf8(s) {
    101                    VarZeroCow::new_borrowed(s)
    102                } else {
    103                    VarZeroCow::new_owned(
    104                        alloc::string::String::from_utf8_lossy(s)
    105                            .into_owned()
    106                            .into_boxed_str(),
    107                    )
    108                }
    109            }
    110 
    111            use icu_decimal::provider::{
    112                DecimalDigitsV1, DecimalSymbolStrsBuilder, DecimalSymbols, DecimalSymbolsV1,
    113                GroupingSizes,
    114            };
    115            let mut new_digits = ['\0'; 10];
    116            for (old, new) in digits
    117                .iter()
    118                .copied()
    119                .chain(core::iter::repeat(char::REPLACEMENT_CHARACTER as u32))
    120                .zip(new_digits.iter_mut())
    121            {
    122                *new = char::from_u32(old).unwrap_or(char::REPLACEMENT_CHARACTER);
    123            }
    124            let digits = new_digits;
    125            let strings = DecimalSymbolStrsBuilder {
    126                plus_sign_prefix: str_to_cow(plus_sign_prefix),
    127                plus_sign_suffix: str_to_cow(plus_sign_suffix),
    128                minus_sign_prefix: str_to_cow(minus_sign_prefix),
    129                minus_sign_suffix: str_to_cow(minus_sign_suffix),
    130                decimal_separator: str_to_cow(decimal_separator),
    131                grouping_separator: str_to_cow(grouping_separator),
    132                numsys: "zyyy".into(),
    133            };
    134 
    135            let grouping_sizes = GroupingSizes {
    136                primary: primary_group_size,
    137                secondary: secondary_group_size,
    138                min_grouping: min_group_size,
    139            };
    140 
    141            let mut options = DecimalFormatterOptions::default();
    142            options.grouping_strategy = grouping_strategy.map(Into::into);
    143 
    144            struct Provider(RefCell<Option<DecimalSymbols<'static>>>, [char; 10]);
    145            impl DataProvider<DecimalSymbolsV1> for Provider {
    146                fn load(
    147                    &self,
    148                    _req: icu_provider::DataRequest,
    149                ) -> Result<icu_provider::DataResponse<DecimalSymbolsV1>, icu_provider::DataError>
    150                {
    151                    Ok(DataResponse {
    152                        metadata: Default::default(),
    153                        payload: DataPayload::from_owned(
    154                            self.0
    155                                .borrow_mut()
    156                                .take()
    157                                // We only have one payload
    158                                .ok_or(DataErrorKind::Custom.into_error())?,
    159                        ),
    160                    })
    161                }
    162            }
    163            impl DataProvider<DecimalDigitsV1> for Provider {
    164                fn load(
    165                    &self,
    166                    _req: icu_provider::DataRequest,
    167                ) -> Result<icu_provider::DataResponse<DecimalDigitsV1>, icu_provider::DataError>
    168                {
    169                    Ok(DataResponse {
    170                        metadata: Default::default(),
    171                        payload: DataPayload::from_owned(self.1),
    172                    })
    173                }
    174            }
    175            let provider = Provider(
    176                RefCell::new(Some(DecimalSymbols {
    177                    strings: VarZeroCow::from_encodeable(&strings),
    178                    grouping_sizes,
    179                })),
    180                digits,
    181            );
    182            Ok(Box::new(DecimalFormatter(
    183                icu_decimal::DecimalFormatter::try_new_unstable(
    184                    &provider,
    185                    Default::default(),
    186                    options,
    187                )?,
    188            )))
    189        }
    190 
    191        /// Formats a [`Decimal`] to a string.
    192        #[diplomat::rust_link(icu::decimal::DecimalFormatter::format, FnInStruct)]
    193        #[diplomat::rust_link(icu::decimal::DecimalFormatter::format_to_string, FnInStruct, hidden)]
    194        #[diplomat::rust_link(icu::decimal::FormattedDecimal, Struct, hidden)]
    195        #[diplomat::rust_link(icu::decimal::FormattedDecimal::to_string, FnInStruct, hidden)]
    196        pub fn format(&self, value: &Decimal, write: &mut diplomat_runtime::DiplomatWrite) {
    197            let _infallible = self.0.format(&value.0).write_to(write);
    198        }
    199    }
    200 }