// 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 ). #[diplomat::bridge] #[diplomat::abi_rename = "icu4x_{0}_mv1"] #[diplomat::attr(auto, namespace = "icu4x")] pub mod ffi { use alloc::boxed::Box; #[cfg(any(feature = "compiled_data", feature = "buffer_provider"))] use crate::locale_core::ffi::Locale; #[cfg(feature = "buffer_provider")] use crate::provider::ffi::DataProvider; use crate::{errors::ffi::DataError, fixed_decimal::ffi::Decimal}; use icu_decimal::options::DecimalFormatterOptions; #[cfg(any(feature = "compiled_data", feature = "buffer_provider"))] use icu_decimal::DecimalFormatterPreferences; use writeable::Writeable; #[diplomat::opaque] /// An ICU4X Decimal Format object, capable of formatting a [`Decimal`] as a string. #[diplomat::rust_link(icu::decimal::DecimalFormatter, Struct)] #[diplomat::rust_link(icu::datetime::FormattedDecimal, Struct, hidden)] pub struct DecimalFormatter(pub icu_decimal::DecimalFormatter); #[diplomat::rust_link(icu::decimal::options::GroupingStrategy, Enum)] #[diplomat::enum_convert(icu_decimal::options::GroupingStrategy, needs_wildcard)] pub enum DecimalGroupingStrategy { Auto, Never, Always, Min2, } impl DecimalFormatter { /// Creates a new [`DecimalFormatter`], using compiled data #[diplomat::rust_link(icu::decimal::DecimalFormatter::try_new, FnInStruct)] #[diplomat::attr(all(supports = fallible_constructors, supports = named_constructors), named_constructor = "with_grouping_strategy")] #[diplomat::demo(default_constructor)] #[cfg(feature = "compiled_data")] pub fn create_with_grouping_strategy( locale: &Locale, grouping_strategy: Option, ) -> Result, DataError> { let prefs = DecimalFormatterPreferences::from(&locale.0); let mut options = DecimalFormatterOptions::default(); options.grouping_strategy = grouping_strategy.map(Into::into); Ok(Box::new(DecimalFormatter( icu_decimal::DecimalFormatter::try_new(prefs, options)?, ))) } /// Creates a new [`DecimalFormatter`], using a particular data source. #[diplomat::rust_link(icu::decimal::DecimalFormatter::try_new, FnInStruct)] #[diplomat::attr(all(supports = fallible_constructors, supports = named_constructors), named_constructor = "with_grouping_strategy_and_provider")] #[diplomat::demo(default_constructor)] #[cfg(feature = "buffer_provider")] pub fn create_with_grouping_strategy_and_provider( provider: &DataProvider, locale: &Locale, grouping_strategy: Option, ) -> Result, DataError> { let prefs = DecimalFormatterPreferences::from(&locale.0); let mut options = DecimalFormatterOptions::default(); options.grouping_strategy = grouping_strategy.map(Into::into); Ok(Box::new(DecimalFormatter( icu_decimal::DecimalFormatter::try_new_with_buffer_provider( provider.get()?, prefs, options, )?, ))) } /// Creates a new [`DecimalFormatter`] from preconstructed locale data. #[diplomat::rust_link(icu::decimal::provider::DecimalSymbolsV2, Struct)] #[allow(clippy::too_many_arguments)] pub fn create_with_manual_data( plus_sign_prefix: &DiplomatStr, plus_sign_suffix: &DiplomatStr, minus_sign_prefix: &DiplomatStr, minus_sign_suffix: &DiplomatStr, decimal_separator: &DiplomatStr, grouping_separator: &DiplomatStr, primary_group_size: u8, secondary_group_size: u8, min_group_size: u8, digits: &[DiplomatChar], grouping_strategy: Option, ) -> Result, DataError> { use core::cell::RefCell; use icu_provider::prelude::*; use zerovec::VarZeroCow; fn str_to_cow(s: &'_ diplomat_runtime::DiplomatStr) -> VarZeroCow<'_, str> { if let Ok(s) = core::str::from_utf8(s) { VarZeroCow::new_borrowed(s) } else { VarZeroCow::new_owned( alloc::string::String::from_utf8_lossy(s) .into_owned() .into_boxed_str(), ) } } use icu_decimal::provider::{ DecimalDigitsV1, DecimalSymbolStrsBuilder, DecimalSymbols, DecimalSymbolsV2, GroupingSizes, }; let mut new_digits = ['\0'; 10]; for (old, new) in digits .iter() .copied() .chain(core::iter::repeat(char::REPLACEMENT_CHARACTER as u32)) .zip(new_digits.iter_mut()) { *new = char::from_u32(old).unwrap_or(char::REPLACEMENT_CHARACTER); } let digits = new_digits; let strings = DecimalSymbolStrsBuilder { plus_sign_prefix: str_to_cow(plus_sign_prefix), plus_sign_suffix: str_to_cow(plus_sign_suffix), minus_sign_prefix: str_to_cow(minus_sign_prefix), minus_sign_suffix: str_to_cow(minus_sign_suffix), decimal_separator: str_to_cow(decimal_separator), grouping_separator: str_to_cow(grouping_separator), numsys: "zyyy".into(), }; let grouping_sizes = GroupingSizes { primary: primary_group_size, secondary: secondary_group_size, min_grouping: min_group_size, }; let mut options = DecimalFormatterOptions::default(); options.grouping_strategy = grouping_strategy.map(Into::into); struct Provider(RefCell>>, [char; 10]); impl DataProvider for Provider { fn load( &self, _req: icu_provider::DataRequest, ) -> Result, icu_provider::DataError> { Ok(DataResponse { metadata: Default::default(), payload: DataPayload::from_owned( self.0 .borrow_mut() .take() // We only have one payload .ok_or(DataErrorKind::Custom.into_error())?, ), }) } } impl DataProvider for Provider { fn load( &self, _req: icu_provider::DataRequest, ) -> Result, icu_provider::DataError> { Ok(DataResponse { metadata: Default::default(), payload: DataPayload::from_owned(self.1), }) } } let provider = Provider( RefCell::new(Some(DecimalSymbols { strings: VarZeroCow::from_encodeable(&strings), grouping_sizes, })), digits, ); Ok(Box::new(DecimalFormatter( icu_decimal::DecimalFormatter::try_new_unstable( &provider, Default::default(), options, )?, ))) } /// Formats a [`Decimal`] to a string. #[diplomat::rust_link(icu::decimal::DecimalFormatter::format, FnInStruct)] #[diplomat::rust_link(icu::decimal::DecimalFormatter::format_to_string, FnInStruct, hidden)] #[diplomat::rust_link(icu::decimal::FormattedDecimal, Struct, hidden)] #[diplomat::rust_link(icu::decimal::FormattedDecimal::write_to, FnInStruct, hidden)] #[diplomat::rust_link(icu::decimal::FormattedDecimal::to_string, FnInStruct, hidden)] pub fn format(&self, value: &Decimal, write: &mut diplomat_runtime::DiplomatWrite) { let _infallible = self.0.format(&value.0).write_to(write); } } }