summaryrefslogtreecommitdiffstats
path: root/intl/icu_capi/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /intl/icu_capi/src
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'intl/icu_capi/src')
-rw-r--r--intl/icu_capi/src/bidi.rs267
-rw-r--r--intl/icu_capi/src/calendar.rs148
-rw-r--r--intl/icu_capi/src/casemap.rs336
-rw-r--r--intl/icu_capi/src/collator.rs237
-rw-r--r--intl/icu_capi/src/collections_sets.rs223
-rw-r--r--intl/icu_capi/src/common.rs16
-rw-r--r--intl/icu_capi/src/data_struct.rs90
-rw-r--r--intl/icu_capi/src/date.rs300
-rw-r--r--intl/icu_capi/src/datetime.rs415
-rw-r--r--intl/icu_capi/src/datetime_formatter.rs358
-rw-r--r--intl/icu_capi/src/decimal.rs109
-rw-r--r--intl/icu_capi/src/displaynames.rs156
-rw-r--r--intl/icu_capi/src/errors.rs407
-rw-r--r--intl/icu_capi/src/fallbacker.rs195
-rw-r--r--intl/icu_capi/src/fixed_decimal.rs341
-rw-r--r--intl/icu_capi/src/iana_bcp47_mapper.rs82
-rw-r--r--intl/icu_capi/src/lib.rs145
-rw-r--r--intl/icu_capi/src/list.rs125
-rw-r--r--intl/icu_capi/src/locale.rs212
-rw-r--r--intl/icu_capi/src/locale_directionality.rs101
-rw-r--r--intl/icu_capi/src/locid_transform.rs115
-rw-r--r--intl/icu_capi/src/logging.rs36
-rw-r--r--intl/icu_capi/src/metazone_calculator.rs34
-rw-r--r--intl/icu_capi/src/normalizer.rs152
-rw-r--r--intl/icu_capi/src/normalizer_properties.rs144
-rw-r--r--intl/icu_capi/src/pluralrules.rs150
-rw-r--r--intl/icu_capi/src/properties_iter.rs48
-rw-r--r--intl/icu_capi/src/properties_maps.rs311
-rw-r--r--intl/icu_capi/src/properties_names.rs259
-rw-r--r--intl/icu_capi/src/properties_sets.rs887
-rw-r--r--intl/icu_capi/src/properties_unisets.rs149
-rw-r--r--intl/icu_capi/src/provider.rs344
-rw-r--r--intl/icu_capi/src/script.rs156
-rw-r--r--intl/icu_capi/src/segmenter_grapheme.rs154
-rw-r--r--intl/icu_capi/src/segmenter_line.rs271
-rw-r--r--intl/icu_capi/src/segmenter_sentence.rs141
-rw-r--r--intl/icu_capi/src/segmenter_word.rs213
-rw-r--r--intl/icu_capi/src/time.rs68
-rw-r--r--intl/icu_capi/src/timezone.rs328
-rw-r--r--intl/icu_capi/src/timezone_formatter.rs298
-rw-r--r--intl/icu_capi/src/week.rs93
-rw-r--r--intl/icu_capi/src/zoned_formatter.rs198
42 files changed, 8812 insertions, 0 deletions
diff --git a/intl/icu_capi/src/bidi.rs b/intl/icu_capi/src/bidi.rs
new file mode 100644
index 0000000000..cf4c8af98b
--- /dev/null
+++ b/intl/icu_capi/src/bidi.rs
@@ -0,0 +1,267 @@
+// 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]
+pub mod ffi {
+ use alloc::boxed::Box;
+ use alloc::vec::Vec;
+ use diplomat_runtime::DiplomatWriteable;
+
+ use core::fmt::Write;
+ use icu_properties::bidi::BidiClassAdapter;
+ use icu_properties::maps;
+ use icu_properties::BidiClass;
+ use unicode_bidi::BidiInfo;
+ use unicode_bidi::Level;
+ use unicode_bidi::Paragraph;
+
+ use crate::errors::ffi::ICU4XError;
+ use crate::provider::ffi::ICU4XDataProvider;
+
+ pub enum ICU4XBidiDirection {
+ Ltr,
+ Rtl,
+ Mixed,
+ }
+
+ #[diplomat::opaque]
+ /// An ICU4X Bidi object, containing loaded bidi data
+ #[diplomat::rust_link(icu::properties::bidi::BidiClassAdapter, Struct)]
+ // #[diplomat::rust_link(icu::properties::maps::load_bidi_class, Struct)]
+ pub struct ICU4XBidi(pub maps::CodePointMapData<BidiClass>);
+
+ impl ICU4XBidi {
+ /// Creates a new [`ICU4XBidi`] from locale data.
+ #[diplomat::rust_link(icu::properties::bidi::BidiClassAdapter::new, FnInStruct)]
+ pub fn create(provider: &ICU4XDataProvider) -> Result<Box<ICU4XBidi>, ICU4XError> {
+ Ok(Box::new(ICU4XBidi(call_constructor_unstable!(
+ maps::bidi_class [m => Ok(m.static_to_owned())],
+ maps::load_bidi_class,
+ provider,
+ )?)))
+ }
+
+ /// Use the data loaded in this object to process a string and calculate bidi information
+ ///
+ /// Takes in a Level for the default level, if it is an invalid value it will default to LTR
+ #[diplomat::rust_link(unicode_bidi::BidiInfo::new_with_data_source, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::properties::bidi::BidiClassAdapter::bidi_class,
+ FnInStruct,
+ hidden
+ )]
+ pub fn for_text<'text>(
+ &self,
+ text: &'text str,
+ default_level: u8,
+ ) -> Box<ICU4XBidiInfo<'text>> {
+ let data = self.0.as_borrowed();
+ let adapter = BidiClassAdapter::new(data);
+
+ Box::new(ICU4XBidiInfo(BidiInfo::new_with_data_source(
+ &adapter,
+ text,
+ Level::new(default_level).ok(),
+ )))
+ }
+ /// Utility function for producing reorderings given a list of levels
+ ///
+ /// Produces a map saying which visual index maps to which source index.
+ ///
+ /// The levels array must not have values greater than 126 (this is the
+ /// Bidi maximum explicit depth plus one).
+ /// Failure to follow this invariant may lead to incorrect results,
+ /// but is still safe.
+ #[diplomat::rust_link(unicode_bidi::BidiInfo::reorder_visual, FnInStruct)]
+ pub fn reorder_visual(&self, levels: &[u8]) -> Box<ICU4XReorderedIndexMap> {
+ let levels = Level::from_slice_unchecked(levels);
+ Box::new(ICU4XReorderedIndexMap(BidiInfo::reorder_visual(levels)))
+ }
+
+ /// Check if a Level returned by level_at is an RTL level.
+ ///
+ /// Invalid levels (numbers greater than 125) will be assumed LTR
+ #[diplomat::rust_link(unicode_bidi::Level::is_rtl, FnInStruct)]
+ pub fn level_is_rtl(level: u8) -> bool {
+ Level::new(level).unwrap_or_else(|_| Level::ltr()).is_rtl()
+ }
+
+ /// Check if a Level returned by level_at is an LTR level.
+ ///
+ /// Invalid levels (numbers greater than 125) will be assumed LTR
+ #[diplomat::rust_link(unicode_bidi::Level::is_ltr, FnInStruct)]
+ pub fn level_is_ltr(level: u8) -> bool {
+ Level::new(level).unwrap_or_else(|_| Level::ltr()).is_ltr()
+ }
+
+ /// Get a basic RTL Level value
+ #[diplomat::rust_link(unicode_bidi::Level::rtl, FnInStruct)]
+ pub fn level_rtl() -> u8 {
+ Level::rtl().number()
+ }
+
+ /// Get a simple LTR Level value
+ #[diplomat::rust_link(unicode_bidi::Level::ltr, FnInStruct)]
+ pub fn level_ltr() -> u8 {
+ Level::ltr().number()
+ }
+ }
+
+ /// Thin wrapper around a vector that maps visual indices to source indices
+ ///
+ /// `map[visualIndex] = sourceIndex`
+ ///
+ /// Produced by `reorder_visual()` on [`ICU4XBidi`].
+ #[diplomat::opaque]
+ pub struct ICU4XReorderedIndexMap(pub Vec<usize>);
+
+ impl ICU4XReorderedIndexMap {
+ /// Get this as a slice/array of indices
+ pub fn as_slice<'a>(&'a self) -> &'a [usize] {
+ &self.0
+ }
+
+ /// The length of this map
+ #[allow(clippy::len_without_is_empty)]
+ #[diplomat::attr(dart, rename = "length")]
+ pub fn len(&self) -> usize {
+ self.0.len()
+ }
+
+ /// Get element at `index`. Returns 0 when out of bounds
+ /// (note that 0 is also a valid in-bounds value, please use `len()`
+ /// to avoid out-of-bounds)
+ pub fn get(&self, index: usize) -> usize {
+ self.0.get(index).copied().unwrap_or(0)
+ }
+ }
+
+ /// An object containing bidi information for a given string, produced by `for_text()` on `ICU4XBidi`
+ #[diplomat::rust_link(unicode_bidi::BidiInfo, Struct)]
+ #[diplomat::opaque]
+ pub struct ICU4XBidiInfo<'text>(pub BidiInfo<'text>);
+
+ impl<'text> ICU4XBidiInfo<'text> {
+ /// The number of paragraphs contained here
+ pub fn paragraph_count(&self) -> usize {
+ self.0.paragraphs.len()
+ }
+
+ /// Get the nth paragraph, returning `None` if out of bounds
+ pub fn paragraph_at(&'text self, n: usize) -> Option<Box<ICU4XBidiParagraph<'text>>> {
+ self.0
+ .paragraphs
+ .get(n)
+ .map(|p| Box::new(ICU4XBidiParagraph(Paragraph::new(&self.0, p))))
+ }
+
+ /// The number of bytes in this full text
+ pub fn size(&self) -> usize {
+ self.0.levels.len()
+ }
+
+ /// Get the BIDI level at a particular byte index in the full text.
+ /// This integer is conceptually a `unicode_bidi::Level`,
+ /// and can be further inspected using the static methods on ICU4XBidi.
+ ///
+ /// Returns 0 (equivalent to `Level::ltr()`) on error
+ pub fn level_at(&self, pos: usize) -> u8 {
+ if let Some(l) = self.0.levels.get(pos) {
+ l.number()
+ } else {
+ 0
+ }
+ }
+ }
+
+ /// Bidi information for a single processed paragraph
+ #[diplomat::opaque]
+ pub struct ICU4XBidiParagraph<'info>(pub Paragraph<'info, 'info>);
+
+ impl<'info> ICU4XBidiParagraph<'info> {
+ /// Given a paragraph index `n` within the surrounding text, this sets this
+ /// object to the paragraph at that index. Returns `ICU4XError::OutOfBoundsError` when out of bounds.
+ ///
+ /// This is equivalent to calling `paragraph_at()` on `ICU4XBidiInfo` but doesn't
+ /// create a new object
+ pub fn set_paragraph_in_text(&mut self, n: usize) -> Result<(), ICU4XError> {
+ let para = self
+ .0
+ .info
+ .paragraphs
+ .get(n)
+ .ok_or(ICU4XError::OutOfBoundsError)?;
+ self.0 = Paragraph::new(self.0.info, para);
+ Ok(())
+ }
+ #[diplomat::rust_link(unicode_bidi::Paragraph::level_at, FnInStruct)]
+ /// The primary direction of this paragraph
+ pub fn direction(&self) -> ICU4XBidiDirection {
+ self.0.direction().into()
+ }
+
+ /// The number of bytes in this paragraph
+ #[diplomat::rust_link(unicode_bidi::ParagraphInfo::len, FnInStruct)]
+ pub fn size(&self) -> usize {
+ self.0.para.len()
+ }
+
+ /// The start index of this paragraph within the source text
+ pub fn range_start(&self) -> usize {
+ self.0.para.range.start
+ }
+
+ /// The end index of this paragraph within the source text
+ pub fn range_end(&self) -> usize {
+ self.0.para.range.end
+ }
+
+ /// Reorder a line based on display order. The ranges are specified relative to the source text and must be contained
+ /// within this paragraph's range.
+ #[diplomat::rust_link(unicode_bidi::Paragraph::level_at, FnInStruct)]
+ pub fn reorder_line(
+ &self,
+ range_start: usize,
+ range_end: usize,
+ out: &mut DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ if range_start < self.range_start() || range_end > self.range_end() {
+ return Err(ICU4XError::OutOfBoundsError);
+ }
+
+ let info = self.0.info;
+ let para = self.0.para;
+
+ let reordered = info.reorder_line(para, range_start..range_end);
+
+ Ok(out.write_str(&reordered)?)
+ }
+
+ /// Get the BIDI level at a particular byte index in this paragraph.
+ /// This integer is conceptually a `unicode_bidi::Level`,
+ /// and can be further inspected using the static methods on ICU4XBidi.
+ ///
+ /// Returns 0 (equivalent to `Level::ltr()`) on error
+ #[diplomat::rust_link(unicode_bidi::Paragraph::level_at, FnInStruct)]
+ pub fn level_at(&self, pos: usize) -> u8 {
+ if pos >= self.size() {
+ return 0;
+ }
+
+ self.0.level_at(pos).number()
+ }
+ }
+}
+
+use unicode_bidi::Direction;
+
+impl From<Direction> for ffi::ICU4XBidiDirection {
+ fn from(other: Direction) -> Self {
+ match other {
+ Direction::Ltr => Self::Ltr,
+ Direction::Rtl => Self::Rtl,
+ Direction::Mixed => Self::Mixed,
+ }
+ }
+}
diff --git a/intl/icu_capi/src/calendar.rs b/intl/icu_capi/src/calendar.rs
new file mode 100644
index 0000000000..dd4b2dcbaa
--- /dev/null
+++ b/intl/icu_capi/src/calendar.rs
@@ -0,0 +1,148 @@
+// 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]
+pub mod ffi {
+ use alloc::boxed::Box;
+ use alloc::sync::Arc;
+
+ use core::fmt::Write;
+ use icu_calendar::{AnyCalendar, AnyCalendarKind};
+
+ use crate::errors::ffi::ICU4XError;
+ use crate::locale::ffi::ICU4XLocale;
+ use crate::provider::ffi::ICU4XDataProvider;
+
+ /// The various calendar types currently supported by [`ICU4XCalendar`]
+ #[diplomat::enum_convert(AnyCalendarKind, needs_wildcard)]
+ #[diplomat::rust_link(icu::calendar::AnyCalendarKind, Enum)]
+ pub enum ICU4XAnyCalendarKind {
+ /// The kind of an Iso calendar
+ Iso = 0,
+ /// The kind of a Gregorian calendar
+ Gregorian = 1,
+ /// The kind of a Buddhist calendar
+ Buddhist = 2,
+ /// The kind of a Japanese calendar with modern eras
+ Japanese = 3,
+ /// The kind of a Japanese calendar with modern and historic eras
+ JapaneseExtended = 4,
+ /// The kind of an Ethiopian calendar, with Amete Mihret era
+ Ethiopian = 5,
+ /// The kind of an Ethiopian calendar, with Amete Alem era
+ EthiopianAmeteAlem = 6,
+ /// The kind of a Indian calendar
+ Indian = 7,
+ /// The kind of a Coptic calendar
+ Coptic = 8,
+ /// The kind of a Dangi calendar
+ Dangi = 9,
+ /// The kind of a Chinese calendar
+ Chinese = 10,
+ /// The kind of a Hebrew calendar
+ Hebrew = 11,
+ /// The kind of a Islamic civil calendar
+ IslamicCivil = 12,
+ /// The kind of a Islamic observational calendar
+ IslamicObservational = 13,
+ /// The kind of a Islamic tabular calendar
+ IslamicTabular = 14,
+ /// The kind of a Islamic Umm al-Qura calendar
+ IslamicUmmAlQura = 15,
+ /// The kind of a Persian calendar
+ Persian = 16,
+ /// The kind of a Roc calendar
+ Roc = 17,
+ }
+
+ impl ICU4XAnyCalendarKind {
+ /// Read the calendar type off of the -u-ca- extension on a locale.
+ ///
+ /// Errors if there is no calendar on the locale or if the locale's calendar
+ /// is not known or supported.
+ #[diplomat::rust_link(icu::calendar::AnyCalendarKind::get_for_locale, FnInEnum)]
+ pub fn get_for_locale(locale: &ICU4XLocale) -> Result<ICU4XAnyCalendarKind, ()> {
+ AnyCalendarKind::get_for_locale(&locale.0)
+ .map(Into::into)
+ .ok_or(())
+ }
+
+ /// Obtain the calendar type given a BCP-47 -u-ca- extension string.
+ ///
+ /// Errors if the calendar is not known or supported.
+ #[diplomat::rust_link(icu::calendar::AnyCalendarKind::get_for_bcp47_value, FnInEnum)]
+ #[diplomat::rust_link(
+ icu::calendar::AnyCalendarKind::get_for_bcp47_string,
+ FnInEnum,
+ hidden
+ )]
+ #[diplomat::rust_link(
+ icu::calendar::AnyCalendarKind::get_for_bcp47_bytes,
+ FnInEnum,
+ hidden
+ )]
+ pub fn get_for_bcp47(s: &str) -> Result<ICU4XAnyCalendarKind, ()> {
+ let s = s.as_bytes(); // #2520
+ AnyCalendarKind::get_for_bcp47_bytes(s)
+ .map(Into::into)
+ .ok_or(())
+ }
+
+ /// Obtain the string suitable for use in the -u-ca- extension in a BCP47 locale.
+ #[diplomat::rust_link(icu::calendar::AnyCalendarKind::as_bcp47_string, FnInEnum)]
+ #[diplomat::rust_link(icu::calendar::AnyCalendarKind::as_bcp47_value, FnInEnum, hidden)]
+ pub fn bcp47(
+ self,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ let kind = AnyCalendarKind::from(self);
+ Ok(write.write_str(kind.as_bcp47_string())?)
+ }
+ }
+
+ #[diplomat::opaque]
+ #[diplomat::transparent_convert]
+ #[diplomat::rust_link(icu::calendar::AnyCalendar, Enum)]
+ pub struct ICU4XCalendar(pub Arc<AnyCalendar>);
+
+ impl ICU4XCalendar {
+ /// Creates a new [`ICU4XCalendar`] from the specified date and time.
+ #[diplomat::rust_link(icu::calendar::AnyCalendar::new_for_locale, FnInEnum)]
+ pub fn create_for_locale(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ ) -> Result<Box<ICU4XCalendar>, ICU4XError> {
+ let locale = locale.to_datalocale();
+
+ Ok(Box::new(ICU4XCalendar(Arc::new(call_constructor!(
+ AnyCalendar::new_for_locale [r => Ok(r)],
+ AnyCalendar::try_new_for_locale_with_any_provider,
+ AnyCalendar::try_new_for_locale_with_buffer_provider,
+ provider,
+ &locale
+ )?))))
+ }
+
+ /// Creates a new [`ICU4XCalendar`] from the specified date and time.
+ #[diplomat::rust_link(icu::calendar::AnyCalendar::new, FnInEnum)]
+ pub fn create_for_kind(
+ provider: &ICU4XDataProvider,
+ kind: ICU4XAnyCalendarKind,
+ ) -> Result<Box<ICU4XCalendar>, ICU4XError> {
+ Ok(Box::new(ICU4XCalendar(Arc::new(call_constructor!(
+ AnyCalendar::new [r => Ok(r)],
+ AnyCalendar::try_new_with_any_provider,
+ AnyCalendar::try_new_with_buffer_provider,
+ provider,
+ kind.into()
+ )?))))
+ }
+
+ /// Returns the kind of this calendar
+ #[diplomat::rust_link(icu::calendar::AnyCalendar::kind, FnInEnum)]
+ pub fn kind(&self) -> ICU4XAnyCalendarKind {
+ self.0.kind().into()
+ }
+ }
+}
diff --git a/intl/icu_capi/src/casemap.rs b/intl/icu_capi/src/casemap.rs
new file mode 100644
index 0000000000..cc20ccccec
--- /dev/null
+++ b/intl/icu_capi/src/casemap.rs
@@ -0,0 +1,336 @@
+// 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 icu_casemap::titlecase::TitlecaseOptions;
+
+#[diplomat::bridge]
+pub mod ffi {
+ use crate::{
+ errors::ffi::ICU4XError, locale::ffi::ICU4XLocale, provider::ffi::ICU4XDataProvider,
+ };
+ use alloc::boxed::Box;
+ use diplomat_runtime::DiplomatWriteable;
+ use icu_casemap::titlecase::{LeadingAdjustment, TrailingCase};
+ use icu_casemap::{CaseMapCloser, CaseMapper, TitlecaseMapper};
+ use writeable::Writeable;
+
+ #[diplomat::enum_convert(LeadingAdjustment, needs_wildcard)]
+ #[diplomat::rust_link(icu::casemap::titlecase::LeadingAdjustment, Enum)]
+ pub enum ICU4XLeadingAdjustment {
+ Auto,
+ None,
+ ToCased,
+ }
+
+ #[diplomat::enum_convert(TrailingCase, needs_wildcard)]
+ #[diplomat::rust_link(icu::casemap::titlecase::TrailingCase, Enum)]
+ pub enum ICU4XTrailingCase {
+ Lower,
+ Unchanged,
+ }
+
+ #[diplomat::rust_link(icu::casemap::titlecase::TitlecaseOptions, Struct)]
+ pub struct ICU4XTitlecaseOptionsV1 {
+ pub leading_adjustment: ICU4XLeadingAdjustment,
+ pub trailing_case: ICU4XTrailingCase,
+ }
+
+ impl ICU4XTitlecaseOptionsV1 {
+ #[diplomat::rust_link(icu::casemap::titlecase::TitlecaseOptions::default, FnInStruct)]
+ pub fn default_options() -> ICU4XTitlecaseOptionsV1 {
+ // named default_options to avoid keyword clashes
+ Self {
+ leading_adjustment: ICU4XLeadingAdjustment::Auto,
+ trailing_case: ICU4XTrailingCase::Lower,
+ }
+ }
+ }
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::casemap::CaseMapper, Struct)]
+ pub struct ICU4XCaseMapper(pub CaseMapper);
+
+ impl ICU4XCaseMapper {
+ /// Construct a new ICU4XCaseMapper instance
+ #[diplomat::rust_link(icu::casemap::CaseMapper::new, FnInStruct)]
+ pub fn create(provider: &ICU4XDataProvider) -> Result<Box<ICU4XCaseMapper>, ICU4XError> {
+ Ok(Box::new(ICU4XCaseMapper(call_constructor!(
+ CaseMapper::new [r => Ok(r)],
+ CaseMapper::try_new_with_any_provider,
+ CaseMapper::try_new_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Returns the full lowercase mapping of the given string
+ #[diplomat::rust_link(icu::casemap::CaseMapper::lowercase, FnInStruct)]
+ #[diplomat::rust_link(icu::casemap::CaseMapper::lowercase_to_string, FnInStruct, hidden)]
+ pub fn lowercase(
+ &self,
+ s: &str,
+ locale: &ICU4XLocale,
+ write: &mut DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ // #2520
+ // In the future we should be able to make assumptions based on backend
+ core::str::from_utf8(s.as_bytes())
+ .map_err(|e| ICU4XError::DataIoError.log_original(&e))?;
+ self.0.lowercase(s, &locale.0.id).write_to(write)?;
+
+ Ok(())
+ }
+
+ /// Returns the full uppercase mapping of the given string
+ #[diplomat::rust_link(icu::casemap::CaseMapper::uppercase, FnInStruct)]
+ #[diplomat::rust_link(icu::casemap::CaseMapper::uppercase_to_string, FnInStruct, hidden)]
+ pub fn uppercase(
+ &self,
+ s: &str,
+ locale: &ICU4XLocale,
+ write: &mut DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ // #2520
+ // In the future we should be able to make assumptions based on backend
+ core::str::from_utf8(s.as_bytes())
+ .map_err(|e| ICU4XError::DataIoError.log_original(&e))?;
+ self.0.uppercase(s, &locale.0.id).write_to(write)?;
+
+ Ok(())
+ }
+
+ /// Returns the full titlecase mapping of the given string, performing head adjustment without
+ /// loading additional data.
+ /// (if head adjustment is enabled in the options)
+ ///
+ /// The `v1` refers to the version of the options struct, which may change as we add more options
+ #[diplomat::rust_link(
+ icu::casemap::CaseMapper::titlecase_segment_with_only_case_data,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu::casemap::CaseMapper::titlecase_segment_with_only_case_data_to_string,
+ FnInStruct,
+ hidden
+ )]
+ pub fn titlecase_segment_with_only_case_data_v1(
+ &self,
+ s: &str,
+ locale: &ICU4XLocale,
+ options: ICU4XTitlecaseOptionsV1,
+ write: &mut DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ // #2520
+ // In the future we should be able to make assumptions based on backend
+ core::str::from_utf8(s.as_bytes())
+ .map_err(|e| ICU4XError::DataIoError.log_original(&e))?;
+ self.0
+ .titlecase_segment_with_only_case_data(s, &locale.0.id, options.into())
+ .write_to(write)?;
+
+ Ok(())
+ }
+
+ /// Case-folds the characters in the given string
+ #[diplomat::rust_link(icu::casemap::CaseMapper::fold, FnInStruct)]
+ #[diplomat::rust_link(icu::casemap::CaseMapper::fold_string, FnInStruct, hidden)]
+ pub fn fold(&self, s: &str, write: &mut DiplomatWriteable) -> Result<(), ICU4XError> {
+ // #2520
+ // In the future we should be able to make assumptions based on backend
+ core::str::from_utf8(s.as_bytes())
+ .map_err(|e| ICU4XError::DataIoError.log_original(&e))?;
+ self.0.fold(s).write_to(write)?;
+
+ Ok(())
+ }
+ /// Case-folds the characters in the given string
+ /// using Turkic (T) mappings for dotted/dotless I.
+ #[diplomat::rust_link(icu::casemap::CaseMapper::fold_turkic, FnInStruct)]
+ #[diplomat::rust_link(icu::casemap::CaseMapper::fold_turkic_string, FnInStruct, hidden)]
+ pub fn fold_turkic(
+ &self,
+ s: &str,
+ write: &mut DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ // #2520
+ // In the future we should be able to make assumptions based on backend
+ core::str::from_utf8(s.as_bytes())
+ .map_err(|e| ICU4XError::DataIoError.log_original(&e))?;
+ self.0.fold_turkic(s).write_to(write)?;
+
+ Ok(())
+ }
+
+ /// Adds all simple case mappings and the full case folding for `c` to `builder`.
+ /// Also adds special case closure mappings.
+ ///
+ /// In other words, this adds all characters that this casemaps to, as
+ /// well as all characters that may casemap to this one.
+ ///
+ /// Note that since ICU4XCodePointSetBuilder does not contain strings, this will
+ /// ignore string mappings.
+ ///
+ /// Identical to the similarly named method on `ICU4XCaseMapCloser`, use that if you
+ /// plan on using string case closure mappings too.
+ #[cfg(feature = "icu_properties")]
+ #[diplomat::rust_link(icu::casemap::CaseMapper::add_case_closure_to, FnInStruct)]
+ pub fn add_case_closure_to(
+ &self,
+ c: char,
+ builder: &mut crate::collections_sets::ffi::ICU4XCodePointSetBuilder,
+ ) {
+ self.0.add_case_closure_to(c, &mut builder.0)
+ }
+
+ /// Returns the simple lowercase mapping of the given character.
+ ///
+ /// This function only implements simple and common mappings.
+ /// Full mappings, which can map one char to a string, are not included.
+ /// For full mappings, use `ICU4XCaseMapper::lowercase`.
+ #[diplomat::rust_link(icu::casemap::CaseMapper::simple_lowercase, FnInStruct)]
+ pub fn simple_lowercase(&self, ch: char) -> char {
+ self.0.simple_lowercase(ch)
+ }
+
+ /// Returns the simple uppercase mapping of the given character.
+ ///
+ /// This function only implements simple and common mappings.
+ /// Full mappings, which can map one char to a string, are not included.
+ /// For full mappings, use `ICU4XCaseMapper::uppercase`.
+ #[diplomat::rust_link(icu::casemap::CaseMapper::simple_uppercase, FnInStruct)]
+ pub fn simple_uppercase(&self, ch: char) -> char {
+ self.0.simple_uppercase(ch)
+ }
+
+ /// Returns the simple titlecase mapping of the given character.
+ ///
+ /// This function only implements simple and common mappings.
+ /// Full mappings, which can map one char to a string, are not included.
+ /// For full mappings, use `ICU4XCaseMapper::titlecase_segment`.
+ #[diplomat::rust_link(icu::casemap::CaseMapper::simple_titlecase, FnInStruct)]
+ pub fn simple_titlecase(&self, ch: char) -> char {
+ self.0.simple_titlecase(ch)
+ }
+
+ /// Returns the simple casefolding of the given character.
+ ///
+ /// This function only implements simple folding.
+ /// For full folding, use `ICU4XCaseMapper::fold`.
+ #[diplomat::rust_link(icu::casemap::CaseMapper::simple_fold, FnInStruct)]
+ pub fn simple_fold(&self, ch: char) -> char {
+ self.0.simple_fold(ch)
+ }
+ /// Returns the simple casefolding of the given character in the Turkic locale
+ ///
+ /// This function only implements simple folding.
+ /// For full folding, use `ICU4XCaseMapper::fold_turkic`.
+ #[diplomat::rust_link(icu::casemap::CaseMapper::simple_fold_turkic, FnInStruct)]
+ pub fn simple_fold_turkic(&self, ch: char) -> char {
+ self.0.simple_fold_turkic(ch)
+ }
+ }
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::casemap::CaseMapCloser, Struct)]
+ pub struct ICU4XCaseMapCloser(pub CaseMapCloser<CaseMapper>);
+
+ impl ICU4XCaseMapCloser {
+ /// Construct a new ICU4XCaseMapper instance
+ #[diplomat::rust_link(icu::casemap::CaseMapCloser::new, FnInStruct)]
+ #[diplomat::rust_link(icu::casemap::CaseMapCloser::new_with_mapper, FnInStruct, hidden)]
+ pub fn create(provider: &ICU4XDataProvider) -> Result<Box<ICU4XCaseMapCloser>, ICU4XError> {
+ Ok(Box::new(ICU4XCaseMapCloser(call_constructor!(
+ CaseMapCloser::new [r => Ok(r)],
+ CaseMapCloser::try_new_with_any_provider,
+ CaseMapCloser::try_new_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Adds all simple case mappings and the full case folding for `c` to `builder`.
+ /// Also adds special case closure mappings.
+ #[cfg(feature = "icu_properties")]
+ #[diplomat::rust_link(icu::casemap::CaseMapCloser::add_case_closure_to, FnInStruct)]
+ pub fn add_case_closure_to(
+ &self,
+ c: char,
+ builder: &mut crate::collections_sets::ffi::ICU4XCodePointSetBuilder,
+ ) {
+ self.0.add_case_closure_to(c, &mut builder.0)
+ }
+
+ /// Finds all characters and strings which may casemap to `s` as their full case folding string
+ /// and adds them to the set.
+ ///
+ /// Returns true if the string was found
+ #[cfg(feature = "icu_properties")]
+ #[diplomat::rust_link(icu::casemap::CaseMapCloser::add_string_case_closure_to, FnInStruct)]
+ pub fn add_string_case_closure_to(
+ &self,
+ s: &str,
+ builder: &mut crate::collections_sets::ffi::ICU4XCodePointSetBuilder,
+ ) -> bool {
+ // #2520
+ // In the future we should be able to make assumptions based on backend
+ let s = core::str::from_utf8(s.as_bytes()).unwrap_or("");
+ self.0.add_string_case_closure_to(s, &mut builder.0)
+ }
+ }
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::casemap::TitlecaseMapper, Struct)]
+ pub struct ICU4XTitlecaseMapper(pub TitlecaseMapper<CaseMapper>);
+
+ impl ICU4XTitlecaseMapper {
+ /// Construct a new `ICU4XTitlecaseMapper` instance
+ #[diplomat::rust_link(icu::casemap::TitlecaseMapper::new, FnInStruct)]
+ #[diplomat::rust_link(icu::casemap::TitlecaseMapper::new_with_mapper, FnInStruct, hidden)]
+ pub fn create(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XTitlecaseMapper>, ICU4XError> {
+ Ok(Box::new(ICU4XTitlecaseMapper(call_constructor!(
+ TitlecaseMapper::new [r => Ok(r)],
+ TitlecaseMapper::try_new_with_any_provider,
+ TitlecaseMapper::try_new_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Returns the full titlecase mapping of the given string
+ ///
+ /// The `v1` refers to the version of the options struct, which may change as we add more options
+ #[diplomat::rust_link(icu::casemap::TitlecaseMapper::titlecase_segment, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::casemap::TitlecaseMapper::titlecase_segment_to_string,
+ FnInStruct,
+ hidden
+ )]
+ pub fn titlecase_segment_v1(
+ &self,
+ s: &str,
+ locale: &ICU4XLocale,
+ options: ICU4XTitlecaseOptionsV1,
+ write: &mut DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ // #2520
+ // In the future we should be able to make assumptions based on backend
+ core::str::from_utf8(s.as_bytes())
+ .map_err(|e| ICU4XError::DataIoError.log_original(&e))?;
+ self.0
+ .titlecase_segment(s, &locale.0.id, options.into())
+ .write_to(write)?;
+
+ Ok(())
+ }
+ }
+}
+
+impl From<ffi::ICU4XTitlecaseOptionsV1> for TitlecaseOptions {
+ fn from(other: ffi::ICU4XTitlecaseOptionsV1) -> Self {
+ let mut ret = Self::default();
+
+ ret.leading_adjustment = other.leading_adjustment.into();
+ ret.trailing_case = other.trailing_case.into();
+ ret
+ }
+}
diff --git a/intl/icu_capi/src/collator.rs b/intl/icu_capi/src/collator.rs
new file mode 100644
index 0000000000..bbf56786b7
--- /dev/null
+++ b/intl/icu_capi/src/collator.rs
@@ -0,0 +1,237 @@
+// 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]
+pub mod ffi {
+ use alloc::boxed::Box;
+ use icu_collator::{Collator, CollatorOptions};
+
+ use crate::{
+ common::ffi::ICU4XOrdering, errors::ffi::ICU4XError, locale::ffi::ICU4XLocale,
+ provider::ffi::ICU4XDataProvider,
+ };
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::collator::Collator, Struct)]
+ pub struct ICU4XCollator(pub Collator);
+
+ #[diplomat::rust_link(icu::collator::CollatorOptions, Struct)]
+ #[diplomat::rust_link(icu::collator::CollatorOptions::new, FnInStruct, hidden)]
+ pub struct ICU4XCollatorOptionsV1 {
+ pub strength: ICU4XCollatorStrength,
+ pub alternate_handling: ICU4XCollatorAlternateHandling,
+ pub case_first: ICU4XCollatorCaseFirst,
+ pub max_variable: ICU4XCollatorMaxVariable,
+ pub case_level: ICU4XCollatorCaseLevel,
+ pub numeric: ICU4XCollatorNumeric,
+ pub backward_second_level: ICU4XCollatorBackwardSecondLevel,
+ }
+
+ #[diplomat::rust_link(icu::collator::Strength, Enum)]
+ pub enum ICU4XCollatorStrength {
+ Auto = 0,
+ Primary = 1,
+ Secondary = 2,
+ Tertiary = 3,
+ Quaternary = 4,
+ Identical = 5,
+ }
+
+ #[diplomat::rust_link(icu::collator::AlternateHandling, Enum)]
+ pub enum ICU4XCollatorAlternateHandling {
+ Auto = 0,
+ NonIgnorable = 1,
+ Shifted = 2,
+ }
+
+ #[diplomat::rust_link(icu::collator::CaseFirst, Enum)]
+ pub enum ICU4XCollatorCaseFirst {
+ Auto = 0,
+ Off = 1,
+ LowerFirst = 2,
+ UpperFirst = 3,
+ }
+
+ #[diplomat::rust_link(icu::collator::MaxVariable, Enum)]
+ pub enum ICU4XCollatorMaxVariable {
+ Auto = 0,
+ Space = 1,
+ Punctuation = 2,
+ Symbol = 3,
+ Currency = 4,
+ }
+
+ #[diplomat::rust_link(icu::collator::CaseLevel, Enum)]
+ pub enum ICU4XCollatorCaseLevel {
+ Auto = 0,
+ Off = 1,
+ On = 2,
+ }
+
+ #[diplomat::rust_link(icu::collator::Numeric, Enum)]
+ pub enum ICU4XCollatorNumeric {
+ Auto = 0,
+ Off = 1,
+ On = 2,
+ }
+
+ #[diplomat::rust_link(icu::collator::BackwardSecondLevel, Enum)]
+ pub enum ICU4XCollatorBackwardSecondLevel {
+ Auto = 0,
+ Off = 1,
+ On = 2,
+ }
+
+ impl ICU4XCollator {
+ /// Construct a new Collator instance.
+ #[diplomat::rust_link(icu::collator::Collator::try_new, FnInStruct)]
+ pub fn create_v1(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ options: ICU4XCollatorOptionsV1,
+ ) -> Result<Box<ICU4XCollator>, ICU4XError> {
+ let locale = locale.to_datalocale();
+ let options = CollatorOptions::from(options);
+
+ Ok(Box::new(ICU4XCollator(call_constructor!(
+ Collator::try_new,
+ Collator::try_new_with_any_provider,
+ Collator::try_new_with_buffer_provider,
+ provider,
+ &locale,
+ options,
+ )?)))
+ }
+
+ /// Compare potentially ill-formed UTF-8 strings.
+ ///
+ /// Ill-formed input is compared
+ /// as if errors had been replaced with REPLACEMENT CHARACTERs according
+ /// to the WHATWG Encoding Standard.
+ #[diplomat::rust_link(icu::collator::Collator::compare_utf8, FnInStruct)]
+ #[diplomat::attr(dart, disable)]
+ pub fn compare(&self, left: &str, right: &str) -> ICU4XOrdering {
+ let left = left.as_bytes(); // #2520
+ let right = right.as_bytes(); // #2520
+ self.0.compare_utf8(left, right).into()
+ }
+
+ /// Compare guaranteed well-formed UTF-8 strings.
+ ///
+ /// Note: In C++, passing ill-formed UTF-8 strings is undefined behavior
+ /// (and may be memory-unsafe to do so, too).
+ #[diplomat::rust_link(icu::collator::Collator::compare, FnInStruct)]
+ #[diplomat::attr(dart, rename = "compare")]
+ pub fn compare_valid_utf8(&self, left: &str, right: &str) -> ICU4XOrdering {
+ self.0.compare(left, right).into()
+ }
+
+ /// Compare potentially ill-formed UTF-16 strings, with unpaired surrogates
+ /// compared as REPLACEMENT CHARACTER.
+ #[diplomat::rust_link(icu::collator::Collator::compare_utf16, FnInStruct)]
+ pub fn compare_utf16(&self, left: &[u16], right: &[u16]) -> ICU4XOrdering {
+ self.0.compare_utf16(left, right).into()
+ }
+ }
+}
+
+use icu_collator::{
+ AlternateHandling, BackwardSecondLevel, CaseFirst, CaseLevel, CollatorOptions, MaxVariable,
+ Numeric, Strength,
+};
+
+impl From<ffi::ICU4XCollatorStrength> for Option<Strength> {
+ fn from(strength: ffi::ICU4XCollatorStrength) -> Option<Strength> {
+ match strength {
+ ffi::ICU4XCollatorStrength::Auto => None,
+ ffi::ICU4XCollatorStrength::Primary => Some(Strength::Primary),
+ ffi::ICU4XCollatorStrength::Secondary => Some(Strength::Secondary),
+ ffi::ICU4XCollatorStrength::Tertiary => Some(Strength::Tertiary),
+ ffi::ICU4XCollatorStrength::Quaternary => Some(Strength::Quaternary),
+ ffi::ICU4XCollatorStrength::Identical => Some(Strength::Identical),
+ }
+ }
+}
+
+impl From<ffi::ICU4XCollatorAlternateHandling> for Option<AlternateHandling> {
+ fn from(alternate_handling: ffi::ICU4XCollatorAlternateHandling) -> Option<AlternateHandling> {
+ match alternate_handling {
+ ffi::ICU4XCollatorAlternateHandling::Auto => None,
+ ffi::ICU4XCollatorAlternateHandling::NonIgnorable => {
+ Some(AlternateHandling::NonIgnorable)
+ }
+ ffi::ICU4XCollatorAlternateHandling::Shifted => Some(AlternateHandling::Shifted),
+ }
+ }
+}
+
+impl From<ffi::ICU4XCollatorCaseFirst> for Option<CaseFirst> {
+ fn from(case_first: ffi::ICU4XCollatorCaseFirst) -> Option<CaseFirst> {
+ match case_first {
+ ffi::ICU4XCollatorCaseFirst::Auto => None,
+ ffi::ICU4XCollatorCaseFirst::Off => Some(CaseFirst::Off),
+ ffi::ICU4XCollatorCaseFirst::LowerFirst => Some(CaseFirst::LowerFirst),
+ ffi::ICU4XCollatorCaseFirst::UpperFirst => Some(CaseFirst::UpperFirst),
+ }
+ }
+}
+
+impl From<ffi::ICU4XCollatorMaxVariable> for Option<MaxVariable> {
+ fn from(max_variable: ffi::ICU4XCollatorMaxVariable) -> Option<MaxVariable> {
+ match max_variable {
+ ffi::ICU4XCollatorMaxVariable::Auto => None,
+ ffi::ICU4XCollatorMaxVariable::Space => Some(MaxVariable::Space),
+ ffi::ICU4XCollatorMaxVariable::Punctuation => Some(MaxVariable::Punctuation),
+ ffi::ICU4XCollatorMaxVariable::Symbol => Some(MaxVariable::Symbol),
+ ffi::ICU4XCollatorMaxVariable::Currency => Some(MaxVariable::Currency),
+ }
+ }
+}
+
+impl From<ffi::ICU4XCollatorCaseLevel> for Option<CaseLevel> {
+ fn from(case_level: ffi::ICU4XCollatorCaseLevel) -> Option<CaseLevel> {
+ match case_level {
+ ffi::ICU4XCollatorCaseLevel::Auto => None,
+ ffi::ICU4XCollatorCaseLevel::Off => Some(CaseLevel::Off),
+ ffi::ICU4XCollatorCaseLevel::On => Some(CaseLevel::On),
+ }
+ }
+}
+
+impl From<ffi::ICU4XCollatorNumeric> for Option<Numeric> {
+ fn from(numeric: ffi::ICU4XCollatorNumeric) -> Option<Numeric> {
+ match numeric {
+ ffi::ICU4XCollatorNumeric::Auto => None,
+ ffi::ICU4XCollatorNumeric::Off => Some(Numeric::Off),
+ ffi::ICU4XCollatorNumeric::On => Some(Numeric::On),
+ }
+ }
+}
+
+impl From<ffi::ICU4XCollatorBackwardSecondLevel> for Option<BackwardSecondLevel> {
+ fn from(
+ backward_second_level: ffi::ICU4XCollatorBackwardSecondLevel,
+ ) -> Option<BackwardSecondLevel> {
+ match backward_second_level {
+ ffi::ICU4XCollatorBackwardSecondLevel::Auto => None,
+ ffi::ICU4XCollatorBackwardSecondLevel::Off => Some(BackwardSecondLevel::Off),
+ ffi::ICU4XCollatorBackwardSecondLevel::On => Some(BackwardSecondLevel::On),
+ }
+ }
+}
+
+impl From<ffi::ICU4XCollatorOptionsV1> for CollatorOptions {
+ fn from(options: ffi::ICU4XCollatorOptionsV1) -> CollatorOptions {
+ let mut result = CollatorOptions::new();
+ result.strength = options.strength.into();
+ result.alternate_handling = options.alternate_handling.into();
+ result.case_first = options.case_first.into();
+ result.max_variable = options.max_variable.into();
+ result.case_level = options.case_level.into();
+ result.numeric = options.numeric.into();
+ result.backward_second_level = options.backward_second_level.into();
+
+ result
+ }
+}
diff --git a/intl/icu_capi/src/collections_sets.rs b/intl/icu_capi/src/collections_sets.rs
new file mode 100644
index 0000000000..68ce1dfd1a
--- /dev/null
+++ b/intl/icu_capi/src/collections_sets.rs
@@ -0,0 +1,223 @@
+// 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]
+pub mod ffi {
+ use crate::properties_sets::ffi::ICU4XCodePointSetData;
+ use alloc::boxed::Box;
+ use core::mem;
+ use icu_collections::codepointinvlist::CodePointInversionListBuilder;
+ use icu_properties::sets::CodePointSetData;
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(
+ icu::collections::codepointinvlist::CodePointInversionListBuilder,
+ Struct
+ )]
+ pub struct ICU4XCodePointSetBuilder(pub CodePointInversionListBuilder);
+
+ impl ICU4XCodePointSetBuilder {
+ /// Make a new set builder containing nothing
+ #[diplomat::rust_link(
+ icu::collections::codepointinvlist::CodePointInversionListBuilder::new,
+ FnInStruct
+ )]
+ pub fn create() -> Box<Self> {
+ Box::new(Self(CodePointInversionListBuilder::new()))
+ }
+
+ /// Build this into a set
+ ///
+ /// This object is repopulated with an empty builder
+ #[diplomat::rust_link(
+ icu::collections::codepointinvlist::CodePointInversionListBuilder::build,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu::properties::sets::CodePointSetData::from_code_point_inversion_list,
+ FnInStruct,
+ hidden
+ )]
+ pub fn build(&mut self) -> Box<ICU4XCodePointSetData> {
+ let inner = mem::take(&mut self.0);
+ let built = inner.build();
+ let set = CodePointSetData::from_code_point_inversion_list(built);
+ Box::new(ICU4XCodePointSetData(set))
+ }
+
+ /// Complements this set
+ ///
+ /// (Elements in this set are removed and vice versa)
+ #[diplomat::rust_link(
+ icu::collections::codepointinvlist::CodePointInversionListBuilder::complement,
+ FnInStruct
+ )]
+ pub fn complement(&mut self) {
+ self.0.complement()
+ }
+
+ /// Returns whether this set is empty
+ #[diplomat::rust_link(
+ icu::collections::codepointinvlist::CodePointInversionListBuilder::is_empty,
+ FnInStruct
+ )]
+ pub fn is_empty(&self) -> bool {
+ self.0.is_empty()
+ }
+
+ /// Add a single character to the set
+ #[diplomat::rust_link(
+ icu::collections::codepointinvlist::CodePointInversionListBuilder::add_char,
+ FnInStruct
+ )]
+ pub fn add_char(&mut self, ch: char) {
+ self.0.add_char(ch)
+ }
+
+ /// Add a single u32 value to the set
+ #[diplomat::rust_link(
+ icu::collections::codepointinvlist::CodePointInversionListBuilder::add_u32,
+ FnInStruct
+ )]
+ #[diplomat::attr(dart, disable)]
+ pub fn add_u32(&mut self, ch: u32) {
+ self.0.add_u32(ch)
+ }
+
+ /// Add an inclusive range of characters to the set
+ #[diplomat::rust_link(
+ icu::collections::codepointinvlist::CodePointInversionListBuilder::add_range,
+ FnInStruct
+ )]
+ pub fn add_inclusive_range(&mut self, start: char, end: char) {
+ self.0.add_range(&(start..=end))
+ }
+
+ /// Add an inclusive range of u32s to the set
+ #[diplomat::rust_link(
+ icu::collections::codepointinvlist::CodePointInversionListBuilder::add_range_u32,
+ FnInStruct
+ )]
+ #[diplomat::attr(dart, disable)]
+ pub fn add_inclusive_range_u32(&mut self, start: u32, end: u32) {
+ self.0.add_range_u32(&(start..=end))
+ }
+
+ /// Add all elements that belong to the provided set to the set
+ #[diplomat::rust_link(
+ icu::collections::codepointinvlist::CodePointInversionListBuilder::add_set,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu::properties::sets::CodePointSetData::as_code_point_inversion_list,
+ FnInStruct,
+ hidden
+ )]
+ #[diplomat::rust_link(
+ icu::properties::sets::CodePointSetData::to_code_point_inversion_list,
+ FnInStruct,
+ hidden
+ )]
+ pub fn add_set(&mut self, data: &ICU4XCodePointSetData) {
+ // This is a ZeroFrom and always cheap for a CPIL, may be expensive
+ // for other impls. In the future we can make this builder support multiple impls
+ // if we ever add them
+ let list = data.0.to_code_point_inversion_list();
+ self.0.add_set(&list);
+ }
+
+ /// Remove a single character to the set
+ #[diplomat::rust_link(
+ icu::collections::codepointinvlist::CodePointInversionListBuilder::remove_char,
+ FnInStruct
+ )]
+ pub fn remove_char(&mut self, ch: char) {
+ self.0.remove_char(ch)
+ }
+
+ /// Remove an inclusive range of characters from the set
+ #[diplomat::rust_link(
+ icu::collections::codepointinvlist::CodePointInversionListBuilder::remove_range,
+ FnInStruct
+ )]
+ pub fn remove_inclusive_range(&mut self, start: char, end: char) {
+ self.0.remove_range(&(start..=end))
+ }
+
+ /// Remove all elements that belong to the provided set from the set
+ #[diplomat::rust_link(
+ icu::collections::codepointinvlist::CodePointInversionListBuilder::remove_set,
+ FnInStruct
+ )]
+ pub fn remove_set(&mut self, data: &ICU4XCodePointSetData) {
+ // (see comment in add_set)
+ let list = data.0.to_code_point_inversion_list();
+ self.0.remove_set(&list);
+ }
+
+ /// Removes all elements from the set except a single character
+ #[diplomat::rust_link(
+ icu::collections::codepointinvlist::CodePointInversionListBuilder::retain_char,
+ FnInStruct
+ )]
+ pub fn retain_char(&mut self, ch: char) {
+ self.0.retain_char(ch)
+ }
+
+ /// Removes all elements from the set except an inclusive range of characters f
+ #[diplomat::rust_link(
+ icu::collections::codepointinvlist::CodePointInversionListBuilder::retain_range,
+ FnInStruct
+ )]
+ pub fn retain_inclusive_range(&mut self, start: char, end: char) {
+ self.0.retain_range(&(start..=end))
+ }
+
+ /// Removes all elements from the set except all elements in the provided set
+ #[diplomat::rust_link(
+ icu::collections::codepointinvlist::CodePointInversionListBuilder::retain_set,
+ FnInStruct
+ )]
+ pub fn retain_set(&mut self, data: &ICU4XCodePointSetData) {
+ // (see comment in add_set)
+ let list = data.0.to_code_point_inversion_list();
+ self.0.retain_set(&list);
+ }
+
+ /// Complement a single character to the set
+ ///
+ /// (Characters which are in this set are removed and vice versa)
+ #[diplomat::rust_link(
+ icu::collections::codepointinvlist::CodePointInversionListBuilder::complement_char,
+ FnInStruct
+ )]
+ pub fn complement_char(&mut self, ch: char) {
+ self.0.complement_char(ch)
+ }
+
+ /// Complement an inclusive range of characters from the set
+ ///
+ /// (Characters which are in this set are removed and vice versa)
+ #[diplomat::rust_link(
+ icu::collections::codepointinvlist::CodePointInversionListBuilder::complement_range,
+ FnInStruct
+ )]
+ pub fn complement_inclusive_range(&mut self, start: char, end: char) {
+ self.0.complement_range(&(start..=end))
+ }
+
+ /// Complement all elements that belong to the provided set from the set
+ ///
+ /// (Characters which are in this set are removed and vice versa)
+ #[diplomat::rust_link(
+ icu::collections::codepointinvlist::CodePointInversionListBuilder::complement_set,
+ FnInStruct
+ )]
+ pub fn complement_set(&mut self, data: &ICU4XCodePointSetData) {
+ // (see comment in add_set)
+ let list = data.0.to_code_point_inversion_list();
+ self.0.complement_set(&list);
+ }
+ }
+}
diff --git a/intl/icu_capi/src/common.rs b/intl/icu_capi/src/common.rs
new file mode 100644
index 0000000000..d11ca26044
--- /dev/null
+++ b/intl/icu_capi/src/common.rs
@@ -0,0 +1,16 @@
+// 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]
+pub mod ffi {
+ use alloc::boxed::Box;
+
+ #[diplomat::enum_convert(core::cmp::Ordering)]
+ #[diplomat::rust_link(core::cmp::Ordering, Enum)]
+ pub enum ICU4XOrdering {
+ Less = -1,
+ Equal = 0,
+ Greater = 1,
+ }
+}
diff --git a/intl/icu_capi/src/data_struct.rs b/intl/icu_capi/src/data_struct.rs
new file mode 100644
index 0000000000..60765cec24
--- /dev/null
+++ b/intl/icu_capi/src/data_struct.rs
@@ -0,0 +1,90 @@
+// 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 ).
+
+#[cfg(feature = "icu_decimal")]
+use alloc::borrow::{Cow, ToOwned};
+
+#[diplomat::bridge]
+pub mod ffi {
+
+ #[cfg(feature = "icu_decimal")]
+ use crate::errors::ffi::ICU4XError;
+ use alloc::boxed::Box;
+ use icu_provider::AnyPayload;
+ #[cfg(feature = "icu_decimal")]
+ use icu_provider::DataPayload;
+
+ #[diplomat::opaque]
+ /// A generic data struct to be used by ICU4X
+ ///
+ /// This can be used to construct a StructDataProvider.
+ #[diplomat::attr(dart, disable)]
+ pub struct ICU4XDataStruct(#[allow(dead_code)] AnyPayload);
+
+ impl ICU4XDataStruct {
+ /// Construct a new DecimalSymbolsV1 data struct.
+ ///
+ /// C++ users: All string arguments must be valid UTF8
+ #[diplomat::rust_link(icu::decimal::provider::DecimalSymbolsV1, Struct)]
+ #[allow(clippy::too_many_arguments)]
+ #[cfg(feature = "icu_decimal")]
+ pub fn create_decimal_symbols_v1(
+ plus_sign_prefix: &str,
+ plus_sign_suffix: &str,
+ minus_sign_prefix: &str,
+ minus_sign_suffix: &str,
+ decimal_separator: &str,
+ grouping_separator: &str,
+ primary_group_size: u8,
+ secondary_group_size: u8,
+ min_group_size: u8,
+ digits: &[char],
+ ) -> Result<Box<ICU4XDataStruct>, ICU4XError> {
+ use super::str_to_cow;
+ use icu_decimal::provider::{
+ AffixesV1, DecimalSymbolsV1, DecimalSymbolsV1Marker, GroupingSizesV1,
+ };
+ let digits = if digits.len() == 10 {
+ let mut new_digits = ['\0'; 10];
+ new_digits.copy_from_slice(digits);
+ new_digits
+ } else {
+ return Err(ICU4XError::DataStructValidityError);
+ };
+ let plus_sign_affixes = AffixesV1 {
+ prefix: str_to_cow(plus_sign_prefix),
+ suffix: str_to_cow(plus_sign_suffix),
+ };
+ let minus_sign_affixes = AffixesV1 {
+ prefix: str_to_cow(minus_sign_prefix),
+ suffix: str_to_cow(minus_sign_suffix),
+ };
+ let grouping_sizes = GroupingSizesV1 {
+ primary: primary_group_size,
+ secondary: secondary_group_size,
+ min_grouping: min_group_size,
+ };
+
+ let symbols = DecimalSymbolsV1 {
+ plus_sign_affixes,
+ minus_sign_affixes,
+ decimal_separator: str_to_cow(decimal_separator),
+ grouping_separator: str_to_cow(grouping_separator),
+ grouping_sizes,
+ digits,
+ };
+
+ let payload: DataPayload<DecimalSymbolsV1Marker> = DataPayload::from_owned(symbols);
+ Ok(Box::new(ICU4XDataStruct(payload.wrap_into_any_payload())))
+ }
+ }
+}
+#[cfg(feature = "icu_decimal")]
+fn str_to_cow(s: &str) -> Cow<'static, str> {
+ if s.is_empty() {
+ Cow::default()
+ } else {
+ Cow::from(s.to_owned())
+ }
+}
diff --git a/intl/icu_capi/src/date.rs b/intl/icu_capi/src/date.rs
new file mode 100644
index 0000000000..39fd03835c
--- /dev/null
+++ b/intl/icu_capi/src/date.rs
@@ -0,0 +1,300 @@
+// 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]
+pub mod ffi {
+ use alloc::boxed::Box;
+ use alloc::sync::Arc;
+ use core::fmt::Write;
+ use icu_calendar::types::IsoWeekday;
+ use icu_calendar::AnyCalendar;
+ use icu_calendar::{Date, Iso};
+ use tinystr::TinyAsciiStr;
+
+ use crate::calendar::ffi::ICU4XCalendar;
+ use crate::errors::ffi::ICU4XError;
+
+ #[cfg(feature = "icu_calendar")]
+ use crate::week::ffi::ICU4XWeekCalculator;
+
+ #[diplomat::enum_convert(IsoWeekday)]
+ pub enum ICU4XIsoWeekday {
+ Monday = 1,
+ Tuesday,
+ Wednesday,
+ Thursday,
+ Friday,
+ Saturday,
+ Sunday,
+ }
+ #[diplomat::opaque]
+ #[diplomat::transparent_convert]
+ /// An ICU4X Date object capable of containing a ISO-8601 date
+ #[diplomat::rust_link(icu::calendar::Date, Struct)]
+ pub struct ICU4XIsoDate(pub Date<Iso>);
+
+ impl ICU4XIsoDate {
+ /// Creates a new [`ICU4XIsoDate`] from the specified date and time.
+ #[diplomat::rust_link(icu::calendar::Date::try_new_iso_date, FnInStruct)]
+ pub fn create(year: i32, month: u8, day: u8) -> Result<Box<ICU4XIsoDate>, ICU4XError> {
+ Ok(Box::new(ICU4XIsoDate(Date::try_new_iso_date(
+ year, month, day,
+ )?)))
+ }
+
+ /// Creates a new [`ICU4XIsoDate`] representing January 1, 1970.
+ #[diplomat::rust_link(icu::calendar::Date::unix_epoch, FnInStruct)]
+ pub fn create_for_unix_epoch() -> Box<ICU4XIsoDate> {
+ Box::new(ICU4XIsoDate(Date::unix_epoch()))
+ }
+
+ /// Convert this date to one in a different calendar
+ #[diplomat::rust_link(icu::calendar::Date::to_calendar, FnInStruct)]
+ pub fn to_calendar(&self, calendar: &ICU4XCalendar) -> Box<ICU4XDate> {
+ Box::new(ICU4XDate(self.0.to_calendar(calendar.0.clone())))
+ }
+
+ #[diplomat::rust_link(icu::calendar::Date::to_any, FnInStruct)]
+ pub fn to_any(&self) -> Box<ICU4XDate> {
+ Box::new(ICU4XDate(self.0.to_any().wrap_calendar_in_arc()))
+ }
+
+ /// Returns the 1-indexed day in the month for this date
+ #[diplomat::rust_link(icu::calendar::Date::day_of_month, FnInStruct)]
+ pub fn day_of_month(&self) -> u32 {
+ self.0.day_of_month().0
+ }
+
+ /// Returns the day in the week for this day
+ #[diplomat::rust_link(icu::calendar::Date::day_of_week, FnInStruct)]
+ pub fn day_of_week(&self) -> ICU4XIsoWeekday {
+ self.0.day_of_week().into()
+ }
+
+ /// Returns the week number in this month, 1-indexed, based on what
+ /// is considered the first day of the week (often a locale preference).
+ ///
+ /// `first_weekday` can be obtained via `first_weekday()` on [`ICU4XWeekCalculator`]
+ #[diplomat::rust_link(icu::calendar::Date::week_of_month, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::calendar::week::WeekCalculator::week_of_month,
+ FnInStruct,
+ hidden
+ )]
+ pub fn week_of_month(&self, first_weekday: ICU4XIsoWeekday) -> u32 {
+ self.0.week_of_month(first_weekday.into()).0
+ }
+
+ /// Returns the week number in this year, using week data
+ #[diplomat::rust_link(icu::calendar::Date::week_of_year, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::calendar::week::WeekCalculator::week_of_year,
+ FnInStruct,
+ hidden
+ )]
+ #[cfg(feature = "icu_calendar")]
+ pub fn week_of_year(
+ &self,
+ calculator: &ICU4XWeekCalculator,
+ ) -> Result<crate::week::ffi::ICU4XWeekOf, ICU4XError> {
+ Ok(self.0.week_of_year(&calculator.0)?.into())
+ }
+
+ /// Returns 1-indexed number of the month of this date in its year
+ #[diplomat::rust_link(icu::calendar::Date::month, FnInStruct)]
+ pub fn month(&self) -> u32 {
+ self.0.month().ordinal
+ }
+
+ /// Returns the year number for this date
+ #[diplomat::rust_link(icu::calendar::Date::year, FnInStruct)]
+ pub fn year(&self) -> i32 {
+ self.0.year().number
+ }
+
+ /// Returns if the year is a leap year for this date
+ #[diplomat::rust_link(icu::calendar::Date::is_in_leap_year, FnInStruct)]
+ pub fn is_in_leap_year(&self) -> bool {
+ self.0.is_in_leap_year()
+ }
+
+ /// Returns the number of months in the year represented by this date
+ #[diplomat::rust_link(icu::calendar::Date::months_in_year, FnInStruct)]
+ pub fn months_in_year(&self) -> u8 {
+ self.0.months_in_year()
+ }
+
+ /// Returns the number of days in the month represented by this date
+ #[diplomat::rust_link(icu::calendar::Date::days_in_month, FnInStruct)]
+ pub fn days_in_month(&self) -> u8 {
+ self.0.days_in_month()
+ }
+
+ /// Returns the number of days in the year represented by this date
+ #[diplomat::rust_link(icu::calendar::Date::days_in_year, FnInStruct)]
+ pub fn days_in_year(&self) -> u16 {
+ self.0.days_in_year()
+ }
+ }
+
+ #[diplomat::opaque]
+ #[diplomat::transparent_convert]
+ /// An ICU4X Date object capable of containing a date and time for any calendar.
+ #[diplomat::rust_link(icu::calendar::Date, Struct)]
+ pub struct ICU4XDate(pub Date<Arc<AnyCalendar>>);
+
+ impl ICU4XDate {
+ /// Creates a new [`ICU4XDate`] representing the ISO date and time
+ /// given but in a given calendar
+ #[diplomat::rust_link(icu::calendar::Date::new_from_iso, FnInStruct)]
+ pub fn create_from_iso_in_calendar(
+ year: i32,
+ month: u8,
+ day: u8,
+ calendar: &ICU4XCalendar,
+ ) -> Result<Box<ICU4XDate>, ICU4XError> {
+ let cal = calendar.0.clone();
+ Ok(Box::new(ICU4XDate(
+ Date::try_new_iso_date(year, month, day)?.to_calendar(cal),
+ )))
+ }
+
+ /// Creates a new [`ICU4XDate`] from the given codes, which are interpreted in the given calendar system
+ #[diplomat::rust_link(icu::calendar::Date::try_new_from_codes, FnInStruct)]
+ pub fn create_from_codes_in_calendar(
+ era_code: &str,
+ year: i32,
+ month_code: &str,
+ day: u8,
+ calendar: &ICU4XCalendar,
+ ) -> Result<Box<ICU4XDate>, ICU4XError> {
+ let era_code = era_code.as_bytes(); // #2520
+ let month_code = month_code.as_bytes(); // #2520
+ let era = TinyAsciiStr::from_bytes(era_code)?.into();
+ let month = TinyAsciiStr::from_bytes(month_code)?.into();
+ let cal = calendar.0.clone();
+ Ok(Box::new(ICU4XDate(Date::try_new_from_codes(
+ era, year, month, day, cal,
+ )?)))
+ }
+
+ /// Convert this date to one in a different calendar
+ #[diplomat::rust_link(icu::calendar::Date::to_calendar, FnInStruct)]
+ pub fn to_calendar(&self, calendar: &ICU4XCalendar) -> Box<ICU4XDate> {
+ Box::new(ICU4XDate(self.0.to_calendar(calendar.0.clone())))
+ }
+
+ /// Converts this date to ISO
+ #[diplomat::rust_link(icu::calendar::Date::to_iso, FnInStruct)]
+ pub fn to_iso(&self) -> Box<ICU4XIsoDate> {
+ Box::new(ICU4XIsoDate(self.0.to_iso()))
+ }
+
+ /// Returns the 1-indexed day in the month for this date
+ #[diplomat::rust_link(icu::calendar::Date::day_of_month, FnInStruct)]
+ pub fn day_of_month(&self) -> u32 {
+ self.0.day_of_month().0
+ }
+
+ /// Returns the day in the week for this day
+ #[diplomat::rust_link(icu::calendar::Date::day_of_week, FnInStruct)]
+ pub fn day_of_week(&self) -> ICU4XIsoWeekday {
+ self.0.day_of_week().into()
+ }
+
+ /// Returns the week number in this month, 1-indexed, based on what
+ /// is considered the first day of the week (often a locale preference).
+ ///
+ /// `first_weekday` can be obtained via `first_weekday()` on [`ICU4XWeekCalculator`]
+ #[diplomat::rust_link(icu::calendar::Date::week_of_month, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::calendar::week::WeekCalculator::week_of_month,
+ FnInStruct,
+ hidden
+ )]
+ pub fn week_of_month(&self, first_weekday: ICU4XIsoWeekday) -> u32 {
+ self.0.week_of_month(first_weekday.into()).0
+ }
+
+ /// Returns the week number in this year, using week data
+ #[diplomat::rust_link(icu::calendar::Date::week_of_year, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::calendar::week::WeekCalculator::week_of_year,
+ FnInStruct,
+ hidden
+ )]
+ #[cfg(feature = "icu_calendar")]
+ pub fn week_of_year(
+ &self,
+ calculator: &ICU4XWeekCalculator,
+ ) -> Result<crate::week::ffi::ICU4XWeekOf, ICU4XError> {
+ Ok(self.0.week_of_year(&calculator.0)?.into())
+ }
+
+ /// Returns 1-indexed number of the month of this date in its year
+ ///
+ /// Note that for lunar calendars this may not lead to the same month
+ /// having the same ordinal month across years; use month_code if you care
+ /// about month identity.
+ #[diplomat::rust_link(icu::calendar::Date::month, FnInStruct)]
+ pub fn ordinal_month(&self) -> u32 {
+ self.0.month().ordinal
+ }
+
+ /// Returns the month code for this date. Typically something
+ /// like "M01", "M02", but can be more complicated for lunar calendars.
+ #[diplomat::rust_link(icu::calendar::Date::month, FnInStruct)]
+ pub fn month_code(
+ &self,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ let code = self.0.month().code;
+ write.write_str(&code.0)?;
+ Ok(())
+ }
+
+ /// Returns the year number in the current era for this date
+ #[diplomat::rust_link(icu::calendar::Date::year, FnInStruct)]
+ pub fn year_in_era(&self) -> i32 {
+ self.0.year().number
+ }
+
+ /// Returns the era for this date,
+ #[diplomat::rust_link(icu::Date::year, FnInStruct)]
+ #[diplomat::rust_link(icu::types::Era, Struct, compact)]
+ pub fn era(
+ &self,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ let era = self.0.year().era;
+ write.write_str(&era.0)?;
+ Ok(())
+ }
+
+ /// Returns the number of months in the year represented by this date
+ #[diplomat::rust_link(icu::calendar::Date::months_in_year, FnInStruct)]
+ pub fn months_in_year(&self) -> u8 {
+ self.0.months_in_year()
+ }
+
+ /// Returns the number of days in the month represented by this date
+ #[diplomat::rust_link(icu::calendar::Date::days_in_month, FnInStruct)]
+ pub fn days_in_month(&self) -> u8 {
+ self.0.days_in_month()
+ }
+
+ /// Returns the number of days in the year represented by this date
+ #[diplomat::rust_link(icu::calendar::Date::days_in_year, FnInStruct)]
+ pub fn days_in_year(&self) -> u16 {
+ self.0.days_in_year()
+ }
+
+ /// Returns the [`ICU4XCalendar`] object backing this date
+ #[diplomat::rust_link(icu::calendar::Date::calendar, FnInStruct)]
+ #[diplomat::rust_link(icu::calendar::Date::calendar_wrapper, FnInStruct, hidden)]
+ pub fn calendar(&self) -> Box<ICU4XCalendar> {
+ Box::new(ICU4XCalendar(self.0.calendar_wrapper().clone()))
+ }
+ }
+}
diff --git a/intl/icu_capi/src/datetime.rs b/intl/icu_capi/src/datetime.rs
new file mode 100644
index 0000000000..627b9c16a3
--- /dev/null
+++ b/intl/icu_capi/src/datetime.rs
@@ -0,0 +1,415 @@
+// 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]
+pub mod ffi {
+ use alloc::boxed::Box;
+ use alloc::sync::Arc;
+ use core::convert::TryInto;
+ use core::fmt::Write;
+
+ use icu_calendar::types::Time;
+ use icu_calendar::AnyCalendar;
+ use icu_calendar::{DateTime, Iso};
+ use tinystr::TinyAsciiStr;
+
+ use crate::calendar::ffi::ICU4XCalendar;
+ use crate::date::ffi::{ICU4XDate, ICU4XIsoDate, ICU4XIsoWeekday};
+ use crate::errors::ffi::ICU4XError;
+ use crate::time::ffi::ICU4XTime;
+
+ #[cfg(feature = "icu_calendar")]
+ use crate::week::ffi::ICU4XWeekCalculator;
+
+ #[diplomat::opaque]
+ /// An ICU4X DateTime object capable of containing a ISO-8601 date and time.
+ #[diplomat::rust_link(icu::calendar::DateTime, Struct)]
+ pub struct ICU4XIsoDateTime(pub DateTime<Iso>);
+
+ impl ICU4XIsoDateTime {
+ /// Creates a new [`ICU4XIsoDateTime`] from the specified date and time.
+ #[diplomat::rust_link(icu::calendar::DateTime::try_new_iso_datetime, FnInStruct)]
+ pub fn create(
+ year: i32,
+ month: u8,
+ day: u8,
+ hour: u8,
+ minute: u8,
+ second: u8,
+ nanosecond: u32,
+ ) -> Result<Box<ICU4XIsoDateTime>, ICU4XError> {
+ let mut dt = DateTime::try_new_iso_datetime(year, month, day, hour, minute, second)?;
+ dt.time.nanosecond = nanosecond.try_into()?;
+ Ok(Box::new(ICU4XIsoDateTime(dt)))
+ }
+
+ /// Creates a new [`ICU4XIsoDateTime`] from an [`ICU4XIsoDate`] and [`ICU4XTime`] object
+ #[diplomat::rust_link(icu::calendar::DateTime::new, FnInStruct)]
+ #[diplomat::attr(dart, rename = "create_from_date_and_time")]
+ pub fn crate_from_date_and_time(
+ date: &ICU4XIsoDate,
+ time: &ICU4XTime,
+ ) -> Box<ICU4XIsoDateTime> {
+ let dt = DateTime::new(date.0, time.0);
+ Box::new(ICU4XIsoDateTime(dt))
+ }
+
+ /// Construct from the minutes since the local unix epoch for this date (Jan 1 1970, 00:00)
+ #[diplomat::rust_link(
+ icu::calendar::DateTime::from_minutes_since_local_unix_epoch,
+ FnInStruct
+ )]
+ pub fn create_from_minutes_since_local_unix_epoch(minutes: i32) -> Box<ICU4XIsoDateTime> {
+ Box::new(ICU4XIsoDateTime(
+ DateTime::from_minutes_since_local_unix_epoch(minutes),
+ ))
+ }
+
+ /// Gets the date contained in this object
+ #[diplomat::rust_link(icu::calendar::DateTime::date, StructField)]
+ pub fn date(&self) -> Box<ICU4XIsoDate> {
+ Box::new(ICU4XIsoDate(self.0.date))
+ }
+
+ /// Gets the time contained in this object
+ #[diplomat::rust_link(icu::calendar::DateTime::time, StructField)]
+ pub fn time(&self) -> Box<ICU4XTime> {
+ Box::new(ICU4XTime(self.0.time))
+ }
+
+ /// Converts this to an [`ICU4XDateTime`] capable of being mixed with dates of
+ /// other calendars
+ #[diplomat::rust_link(icu::calendar::DateTime::to_any, FnInStruct)]
+ #[diplomat::rust_link(icu::calendar::DateTime::new_from_iso, FnInStruct, hidden)]
+ pub fn to_any(&self) -> Box<ICU4XDateTime> {
+ Box::new(ICU4XDateTime(self.0.to_any().wrap_calendar_in_arc()))
+ }
+
+ /// Gets the minutes since the local unix epoch for this date (Jan 1 1970, 00:00)
+ #[diplomat::rust_link(icu::calendar::DateTime::minutes_since_local_unix_epoch, FnInStruct)]
+ pub fn minutes_since_local_unix_epoch(&self) -> i32 {
+ self.0.minutes_since_local_unix_epoch()
+ }
+
+ /// Convert this datetime to one in a different calendar
+ #[diplomat::rust_link(icu::calendar::DateTime::to_calendar, FnInStruct)]
+ pub fn to_calendar(&self, calendar: &ICU4XCalendar) -> Box<ICU4XDateTime> {
+ Box::new(ICU4XDateTime(self.0.to_calendar(calendar.0.clone())))
+ }
+
+ /// Returns the hour in this time
+ #[diplomat::rust_link(icu::calendar::types::Time::hour, StructField)]
+ pub fn hour(&self) -> u8 {
+ self.0.time.hour.into()
+ }
+ /// Returns the minute in this time
+ #[diplomat::rust_link(icu::calendar::types::Time::minute, StructField)]
+ pub fn minute(&self) -> u8 {
+ self.0.time.minute.into()
+ }
+ /// Returns the second in this time
+ #[diplomat::rust_link(icu::calendar::types::Time::second, StructField)]
+ pub fn second(&self) -> u8 {
+ self.0.time.second.into()
+ }
+ /// Returns the nanosecond in this time
+ #[diplomat::rust_link(icu::calendar::types::Time::nanosecond, StructField)]
+ pub fn nanosecond(&self) -> u32 {
+ self.0.time.nanosecond.into()
+ }
+
+ /// Returns the 1-indexed day in the month for this date
+ #[diplomat::rust_link(icu::calendar::Date::day_of_month, FnInStruct)]
+ pub fn day_of_month(&self) -> u32 {
+ self.0.date.day_of_month().0
+ }
+
+ /// Returns the day in the week for this day
+ #[diplomat::rust_link(icu::calendar::Date::day_of_week, FnInStruct)]
+ pub fn day_of_week(&self) -> ICU4XIsoWeekday {
+ self.0.date.day_of_week().into()
+ }
+
+ /// Returns the week number in this month, 1-indexed, based on what
+ /// is considered the first day of the week (often a locale preference).
+ ///
+ /// `first_weekday` can be obtained via `first_weekday()` on [`ICU4XWeekCalculator`]
+ #[diplomat::rust_link(icu::calendar::Date::week_of_month, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::calendar::week::WeekCalculator::week_of_month,
+ FnInStruct,
+ hidden
+ )]
+ pub fn week_of_month(&self, first_weekday: ICU4XIsoWeekday) -> u32 {
+ self.0.date.week_of_month(first_weekday.into()).0
+ }
+
+ /// Returns the week number in this year, using week data
+ #[diplomat::rust_link(icu::calendar::Date::week_of_year, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::calendar::week::WeekCalculator::week_of_year,
+ FnInStruct,
+ hidden
+ )]
+ #[cfg(feature = "icu_calendar")]
+ pub fn week_of_year(
+ &self,
+ calculator: &ICU4XWeekCalculator,
+ ) -> Result<crate::week::ffi::ICU4XWeekOf, ICU4XError> {
+ Ok(self.0.date.week_of_year(&calculator.0)?.into())
+ }
+
+ /// Returns 1-indexed number of the month of this date in its year
+ #[diplomat::rust_link(icu::calendar::Date::month, FnInStruct)]
+ pub fn month(&self) -> u32 {
+ self.0.date.month().ordinal
+ }
+
+ /// Returns the year number for this date
+ #[diplomat::rust_link(icu::calendar::Date::year, FnInStruct)]
+ pub fn year(&self) -> i32 {
+ self.0.date.year().number
+ }
+
+ /// Returns whether this date is in a leap year
+ #[diplomat::rust_link(icu::calendar::Date::is_in_leap_year, FnInStruct)]
+ pub fn is_in_leap_year(&self) -> bool {
+ self.0.date.is_in_leap_year()
+ }
+
+ /// Returns the number of months in the year represented by this date
+ #[diplomat::rust_link(icu::calendar::Date::months_in_year, FnInStruct)]
+ pub fn months_in_year(&self) -> u8 {
+ self.0.date.months_in_year()
+ }
+
+ /// Returns the number of days in the month represented by this date
+ #[diplomat::rust_link(icu::calendar::Date::days_in_month, FnInStruct)]
+ pub fn days_in_month(&self) -> u8 {
+ self.0.date.days_in_month()
+ }
+
+ /// Returns the number of days in the year represented by this date
+ #[diplomat::rust_link(icu::calendar::Date::days_in_year, FnInStruct)]
+ pub fn days_in_year(&self) -> u16 {
+ self.0.date.days_in_year()
+ }
+ }
+
+ #[diplomat::opaque]
+ /// An ICU4X DateTime object capable of containing a date and time for any calendar.
+ #[diplomat::rust_link(icu::calendar::DateTime, Struct)]
+ pub struct ICU4XDateTime(pub DateTime<Arc<AnyCalendar>>);
+
+ impl ICU4XDateTime {
+ /// Creates a new [`ICU4XDateTime`] representing the ISO date and time
+ /// given but in a given calendar
+ #[diplomat::rust_link(icu::DateTime::new_from_iso, FnInStruct)]
+ #[allow(clippy::too_many_arguments)]
+ pub fn create_from_iso_in_calendar(
+ year: i32,
+ month: u8,
+ day: u8,
+ hour: u8,
+ minute: u8,
+ second: u8,
+ nanosecond: u32,
+ calendar: &ICU4XCalendar,
+ ) -> Result<Box<ICU4XDateTime>, ICU4XError> {
+ let cal = calendar.0.clone();
+ let mut dt = DateTime::try_new_iso_datetime(year, month, day, hour, minute, second)?;
+ dt.time.nanosecond = nanosecond.try_into()?;
+ Ok(Box::new(ICU4XDateTime(dt.to_calendar(cal))))
+ }
+ /// Creates a new [`ICU4XDateTime`] from the given codes, which are interpreted in the given calendar system
+ #[diplomat::rust_link(icu::calendar::DateTime::try_new_from_codes, FnInStruct)]
+ #[allow(clippy::too_many_arguments)]
+ pub fn create_from_codes_in_calendar(
+ era_code: &str,
+ year: i32,
+ month_code: &str,
+ day: u8,
+ hour: u8,
+ minute: u8,
+ second: u8,
+ nanosecond: u32,
+ calendar: &ICU4XCalendar,
+ ) -> Result<Box<ICU4XDateTime>, ICU4XError> {
+ let era_code = era_code.as_bytes(); // #2520
+ let month_code = month_code.as_bytes(); // #2520
+ let era = TinyAsciiStr::from_bytes(era_code)?.into();
+ let month = TinyAsciiStr::from_bytes(month_code)?.into();
+ let cal = calendar.0.clone();
+ let hour = hour.try_into()?;
+ let minute = minute.try_into()?;
+ let second = second.try_into()?;
+ let nanosecond = nanosecond.try_into()?;
+ let time = Time {
+ hour,
+ minute,
+ second,
+ nanosecond,
+ };
+ Ok(Box::new(ICU4XDateTime(DateTime::try_new_from_codes(
+ era, year, month, day, time, cal,
+ )?)))
+ }
+ /// Creates a new [`ICU4XDateTime`] from an [`ICU4XDate`] and [`ICU4XTime`] object
+ #[diplomat::rust_link(icu::calendar::DateTime::new, FnInStruct)]
+ pub fn create_from_date_and_time(date: &ICU4XDate, time: &ICU4XTime) -> Box<ICU4XDateTime> {
+ let dt = DateTime::new(date.0.clone(), time.0);
+ Box::new(ICU4XDateTime(dt))
+ }
+
+ /// Gets a copy of the date contained in this object
+ #[diplomat::rust_link(icu::calendar::DateTime::date, StructField)]
+ pub fn date(&self) -> Box<ICU4XDate> {
+ Box::new(ICU4XDate(self.0.date.clone()))
+ }
+
+ /// Gets the time contained in this object
+ #[diplomat::rust_link(icu::calendar::DateTime::time, StructField)]
+ pub fn time(&self) -> Box<ICU4XTime> {
+ Box::new(ICU4XTime(self.0.time))
+ }
+
+ /// Converts this date to ISO
+ #[diplomat::rust_link(icu::calendar::DateTime::to_iso, FnInStruct)]
+ pub fn to_iso(&self) -> Box<ICU4XIsoDateTime> {
+ Box::new(ICU4XIsoDateTime(self.0.to_iso()))
+ }
+
+ /// Convert this datetime to one in a different calendar
+ #[diplomat::rust_link(icu::calendar::DateTime::to_calendar, FnInStruct)]
+ pub fn to_calendar(&self, calendar: &ICU4XCalendar) -> Box<ICU4XDateTime> {
+ Box::new(ICU4XDateTime(self.0.to_calendar(calendar.0.clone())))
+ }
+
+ /// Returns the hour in this time
+ #[diplomat::rust_link(icu::calendar::types::Time::hour, StructField)]
+ pub fn hour(&self) -> u8 {
+ self.0.time.hour.into()
+ }
+ /// Returns the minute in this time
+ #[diplomat::rust_link(icu::calendar::types::Time::minute, StructField)]
+ pub fn minute(&self) -> u8 {
+ self.0.time.minute.into()
+ }
+ /// Returns the second in this time
+ #[diplomat::rust_link(icu::calendar::types::Time::second, StructField)]
+ pub fn second(&self) -> u8 {
+ self.0.time.second.into()
+ }
+ /// Returns the nanosecond in this time
+ #[diplomat::rust_link(icu::calendar::types::Time::nanosecond, StructField)]
+ pub fn nanosecond(&self) -> u32 {
+ self.0.time.nanosecond.into()
+ }
+
+ /// Returns the 1-indexed day in the month for this date
+ #[diplomat::rust_link(icu::calendar::Date::day_of_month, FnInStruct)]
+ pub fn day_of_month(&self) -> u32 {
+ self.0.date.day_of_month().0
+ }
+
+ /// Returns the day in the week for this day
+ #[diplomat::rust_link(icu::calendar::Date::day_of_week, FnInStruct)]
+ pub fn day_of_week(&self) -> ICU4XIsoWeekday {
+ self.0.date.day_of_week().into()
+ }
+
+ /// Returns the week number in this month, 1-indexed, based on what
+ /// is considered the first day of the week (often a locale preference).
+ ///
+ /// `first_weekday` can be obtained via `first_weekday()` on [`ICU4XWeekCalculator`]
+ #[diplomat::rust_link(icu::calendar::Date::week_of_month, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::calendar::week::WeekCalculator::week_of_month,
+ FnInStruct,
+ hidden
+ )]
+ pub fn week_of_month(&self, first_weekday: ICU4XIsoWeekday) -> u32 {
+ self.0.date.week_of_month(first_weekday.into()).0
+ }
+
+ /// Returns the week number in this year, using week data
+ #[diplomat::rust_link(icu::calendar::Date::week_of_year, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::calendar::week::WeekCalculator::week_of_year,
+ FnInStruct,
+ hidden
+ )]
+ #[cfg(feature = "icu_calendar")]
+ pub fn week_of_year(
+ &self,
+ calculator: &ICU4XWeekCalculator,
+ ) -> Result<crate::week::ffi::ICU4XWeekOf, ICU4XError> {
+ Ok(self.0.date.week_of_year(&calculator.0)?.into())
+ }
+
+ /// Returns 1-indexed number of the month of this date in its year
+ ///
+ /// Note that for lunar calendars this may not lead to the same month
+ /// having the same ordinal month across years; use month_code if you care
+ /// about month identity.
+ #[diplomat::rust_link(icu::calendar::Date::month, FnInStruct)]
+ pub fn ordinal_month(&self) -> u32 {
+ self.0.date.month().ordinal
+ }
+
+ /// Returns the month code for this date. Typically something
+ /// like "M01", "M02", but can be more complicated for lunar calendars.
+ #[diplomat::rust_link(icu::calendar::Date::month, FnInStruct)]
+ pub fn month_code(
+ &self,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ let code = self.0.date.month().code;
+ write.write_str(&code.0)?;
+ Ok(())
+ }
+
+ /// Returns the year number in the current era for this date
+ #[diplomat::rust_link(icu::calendar::Date::year, FnInStruct)]
+ pub fn year_in_era(&self) -> i32 {
+ self.0.date.year().number
+ }
+
+ /// Returns the era for this date,
+ #[diplomat::rust_link(icu::calendar::Date::year, FnInStruct)]
+ pub fn era(
+ &self,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ let era = self.0.date.year().era;
+ write.write_str(&era.0)?;
+ Ok(())
+ }
+
+ /// Returns the number of months in the year represented by this date
+ #[diplomat::rust_link(icu::calendar::Date::months_in_year, FnInStruct)]
+ pub fn months_in_year(&self) -> u8 {
+ self.0.date.months_in_year()
+ }
+
+ /// Returns the number of days in the month represented by this date
+ #[diplomat::rust_link(icu::calendar::Date::days_in_month, FnInStruct)]
+ pub fn days_in_month(&self) -> u8 {
+ self.0.date.days_in_month()
+ }
+
+ /// Returns the number of days in the year represented by this date
+ #[diplomat::rust_link(icu::calendar::Date::days_in_year, FnInStruct)]
+ pub fn days_in_year(&self) -> u16 {
+ self.0.date.days_in_year()
+ }
+
+ /// Returns the [`ICU4XCalendar`] object backing this date
+ #[diplomat::rust_link(icu::calendar::Date::calendar, FnInStruct)]
+ #[diplomat::rust_link(icu::calendar::Date::calendar_wrapper, FnInStruct, hidden)]
+ pub fn calendar(&self) -> Box<ICU4XCalendar> {
+ Box::new(ICU4XCalendar(self.0.date.calendar_wrapper().clone()))
+ }
+ }
+}
diff --git a/intl/icu_capi/src/datetime_formatter.rs b/intl/icu_capi/src/datetime_formatter.rs
new file mode 100644
index 0000000000..c68b3381e9
--- /dev/null
+++ b/intl/icu_capi/src/datetime_formatter.rs
@@ -0,0 +1,358 @@
+// 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]
+pub mod ffi {
+ use alloc::boxed::Box;
+ use icu_calendar::{Date, DateTime, Gregorian};
+ use icu_datetime::{
+ options::length, DateFormatter, DateTimeFormatter, TimeFormatter, TypedDateFormatter,
+ TypedDateTimeFormatter,
+ };
+
+ use crate::{
+ date::ffi::{ICU4XDate, ICU4XIsoDate},
+ datetime::ffi::ICU4XDateTime,
+ datetime::ffi::ICU4XIsoDateTime,
+ errors::ffi::ICU4XError,
+ locale::ffi::ICU4XLocale,
+ provider::ffi::ICU4XDataProvider,
+ time::ffi::ICU4XTime,
+ };
+ use writeable::Writeable;
+
+ #[diplomat::opaque]
+ /// An ICU4X TimeFormatter object capable of formatting an [`ICU4XTime`] type (and others) as a string
+ #[diplomat::rust_link(icu::datetime::TimeFormatter, Struct)]
+ pub struct ICU4XTimeFormatter(pub TimeFormatter);
+
+ #[diplomat::enum_convert(length::Time, needs_wildcard)]
+ #[diplomat::rust_link(icu::datetime::options::length::Time, Enum)]
+ pub enum ICU4XTimeLength {
+ Full,
+ Long,
+ Medium,
+ Short,
+ }
+
+ impl ICU4XTimeFormatter {
+ /// Creates a new [`ICU4XTimeFormatter`] from locale data.
+ #[diplomat::rust_link(icu::datetime::TimeFormatter::try_new_with_length, FnInStruct)]
+ pub fn create_with_length(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ length: ICU4XTimeLength,
+ ) -> Result<Box<ICU4XTimeFormatter>, ICU4XError> {
+ let locale = locale.to_datalocale();
+
+ Ok(Box::new(ICU4XTimeFormatter(call_constructor!(
+ TimeFormatter::try_new_with_length,
+ TimeFormatter::try_new_with_length_with_any_provider,
+ TimeFormatter::try_new_with_length_with_buffer_provider,
+ provider,
+ &locale,
+ length.into()
+ )?)))
+ }
+
+ /// Formats a [`ICU4XTime`] to a string.
+ #[diplomat::rust_link(icu::datetime::TimeFormatter::format, FnInStruct)]
+ #[diplomat::rust_link(icu::datetime::TimeFormatter::format_to_string, FnInStruct, hidden)]
+ pub fn format_time(
+ &self,
+ value: &ICU4XTime,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ self.0.format(&value.0).write_to(write)?;
+ Ok(())
+ }
+
+ /// Formats a [`ICU4XDateTime`] to a string.
+ #[diplomat::rust_link(icu::datetime::TimeFormatter::format, FnInStruct)]
+ #[diplomat::rust_link(icu::datetime::TimeFormatter::format_to_string, FnInStruct, hidden)]
+ pub fn format_datetime(
+ &self,
+ value: &ICU4XDateTime,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ self.0.format(&value.0).write_to(write)?;
+ Ok(())
+ }
+
+ /// Formats a [`ICU4XIsoDateTime`] to a string.
+ #[diplomat::rust_link(icu::datetime::TimeFormatter::format, FnInStruct)]
+ #[diplomat::rust_link(icu::datetime::TimeFormatter::format_to_string, FnInStruct, hidden)]
+ pub fn format_iso_datetime(
+ &self,
+ value: &ICU4XIsoDateTime,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ self.0.format(&value.0).write_to(write)?;
+ Ok(())
+ }
+ }
+
+ #[diplomat::opaque]
+ /// An ICU4X TypedDateFormatter object capable of formatting a [`ICU4XIsoDateTime`] as a string,
+ /// using the Gregorian Calendar.
+ #[diplomat::rust_link(icu::datetime::TypedDateFormatter, Struct)]
+ pub struct ICU4XGregorianDateFormatter(pub TypedDateFormatter<Gregorian>);
+
+ #[diplomat::enum_convert(length::Date, needs_wildcard)]
+ #[diplomat::rust_link(icu::datetime::options::length::Date, Enum)]
+ pub enum ICU4XDateLength {
+ Full,
+ Long,
+ Medium,
+ Short,
+ }
+
+ impl ICU4XGregorianDateFormatter {
+ /// Creates a new [`ICU4XGregorianDateFormatter`] from locale data.
+ #[diplomat::rust_link(icu::datetime::TypedDateFormatter::try_new_with_length, FnInStruct)]
+ pub fn create_with_length(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ length: ICU4XDateLength,
+ ) -> Result<Box<ICU4XGregorianDateFormatter>, ICU4XError> {
+ let locale = locale.to_datalocale();
+
+ Ok(Box::new(ICU4XGregorianDateFormatter(call_constructor!(
+ TypedDateFormatter::try_new_with_length,
+ TypedDateFormatter::try_new_with_length_with_any_provider,
+ TypedDateFormatter::try_new_with_length_with_buffer_provider,
+ provider,
+ &locale,
+ length.into()
+ )?)))
+ }
+
+ /// Formats a [`ICU4XIsoDate`] to a string.
+ #[diplomat::rust_link(icu::datetime::TypedDateFormatter::format, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::datetime::TypedDateFormatter::format_to_string,
+ FnInStruct,
+ hidden
+ )]
+ pub fn format_iso_date(
+ &self,
+ value: &ICU4XIsoDate,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ let greg = Date::new_from_iso(value.0, Gregorian);
+ self.0.format(&greg).write_to(write)?;
+ Ok(())
+ }
+ /// Formats a [`ICU4XIsoDateTime`] to a string.
+ #[diplomat::rust_link(icu::datetime::TypedDateFormatter::format, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::datetime::TypedDateFormatter::format_to_string,
+ FnInStruct,
+ hidden
+ )]
+ pub fn format_iso_datetime(
+ &self,
+ value: &ICU4XIsoDateTime,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ let greg = DateTime::new_from_iso(value.0, Gregorian);
+ self.0.format(&greg).write_to(write)?;
+ Ok(())
+ }
+ }
+
+ #[diplomat::opaque]
+ /// An ICU4X TypedDateTimeFormatter object capable of formatting a [`ICU4XIsoDateTime`] as a string,
+ /// using the Gregorian Calendar.
+ #[diplomat::rust_link(icu::datetime::TypedDateTimeFormatter, Struct)]
+ pub struct ICU4XGregorianDateTimeFormatter(pub TypedDateTimeFormatter<Gregorian>);
+
+ impl ICU4XGregorianDateTimeFormatter {
+ /// Creates a new [`ICU4XGregorianDateFormatter`] from locale data.
+ #[diplomat::rust_link(icu::datetime::TypedDateTimeFormatter::try_new, FnInStruct)]
+ pub fn create_with_lengths(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ date_length: ICU4XDateLength,
+ time_length: ICU4XTimeLength,
+ ) -> Result<Box<ICU4XGregorianDateTimeFormatter>, ICU4XError> {
+ let locale = locale.to_datalocale();
+
+ let options = length::Bag::from_date_time_style(date_length.into(), time_length.into());
+
+ Ok(Box::new(ICU4XGregorianDateTimeFormatter(
+ call_constructor!(
+ TypedDateTimeFormatter::try_new,
+ TypedDateTimeFormatter::try_new_with_any_provider,
+ TypedDateTimeFormatter::try_new_with_buffer_provider,
+ provider,
+ &locale,
+ options.into()
+ )?,
+ )))
+ }
+
+ /// Formats a [`ICU4XIsoDateTime`] to a string.
+ #[diplomat::rust_link(icu::datetime::TypedDateTimeFormatter::format, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::datetime::TypedDateTimeFormatter::format_to_string,
+ FnInStruct,
+ hidden
+ )]
+ pub fn format_iso_datetime(
+ &self,
+ value: &ICU4XIsoDateTime,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ let greg = DateTime::new_from_iso(value.0, Gregorian);
+ self.0.format(&greg).write_to(write)?;
+ Ok(())
+ }
+ }
+
+ #[diplomat::opaque]
+ /// An ICU4X DateFormatter object capable of formatting a [`ICU4XDate`] as a string,
+ /// using some calendar specified at runtime in the locale.
+ #[diplomat::rust_link(icu::datetime::DateFormatter, Struct)]
+ pub struct ICU4XDateFormatter(pub DateFormatter);
+
+ impl ICU4XDateFormatter {
+ /// Creates a new [`ICU4XDateFormatter`] from locale data.
+ #[diplomat::rust_link(icu::datetime::DateFormatter::try_new_with_length, FnInStruct)]
+ pub fn create_with_length(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ date_length: ICU4XDateLength,
+ ) -> Result<Box<ICU4XDateFormatter>, ICU4XError> {
+ let locale = locale.to_datalocale();
+
+ Ok(Box::new(ICU4XDateFormatter(call_constructor!(
+ DateFormatter::try_new_with_length,
+ DateFormatter::try_new_with_length_with_any_provider,
+ DateFormatter::try_new_with_length_with_buffer_provider,
+ provider,
+ &locale,
+ date_length.into()
+ )?)))
+ }
+
+ /// Formats a [`ICU4XDate`] to a string.
+ #[diplomat::rust_link(icu::datetime::DateFormatter::format, FnInStruct)]
+ #[diplomat::rust_link(icu::datetime::DateFormatter::format_to_string, FnInStruct, hidden)]
+ pub fn format_date(
+ &self,
+ value: &ICU4XDate,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ self.0.format(&value.0)?.write_to(write)?;
+ Ok(())
+ }
+
+ /// Formats a [`ICU4XIsoDate`] to a string.
+ ///
+ /// Will convert to this formatter's calendar first
+ #[diplomat::rust_link(icu::datetime::DateFormatter::format, FnInStruct)]
+ #[diplomat::rust_link(icu::datetime::DateFormatter::format_to_string, FnInStruct, hidden)]
+ pub fn format_iso_date(
+ &self,
+ value: &ICU4XIsoDate,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ let any = value.0.to_any();
+ self.0.format(&any)?.write_to(write)?;
+ Ok(())
+ }
+
+ /// Formats a [`ICU4XDateTime`] to a string.
+ #[diplomat::rust_link(icu::datetime::DateFormatter::format, FnInStruct)]
+ #[diplomat::rust_link(icu::datetime::DateFormatter::format_to_string, FnInStruct, hidden)]
+ pub fn format_datetime(
+ &self,
+ value: &ICU4XDateTime,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ self.0.format(&value.0)?.write_to(write)?;
+ Ok(())
+ }
+
+ /// Formats a [`ICU4XIsoDateTime`] to a string.
+ ///
+ /// Will convert to this formatter's calendar first
+ #[diplomat::rust_link(icu::datetime::DateFormatter::format, FnInStruct)]
+ #[diplomat::rust_link(icu::datetime::DateFormatter::format_to_string, FnInStruct, hidden)]
+ pub fn format_iso_datetime(
+ &self,
+ value: &ICU4XIsoDateTime,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ let any = value.0.to_any();
+ self.0.format(&any)?.write_to(write)?;
+ Ok(())
+ }
+ }
+
+ #[diplomat::opaque]
+ /// An ICU4X DateFormatter object capable of formatting a [`ICU4XDateTime`] as a string,
+ /// using some calendar specified at runtime in the locale.
+ #[diplomat::rust_link(icu::datetime::DateTimeFormatter, Struct)]
+ pub struct ICU4XDateTimeFormatter(pub DateTimeFormatter);
+
+ impl ICU4XDateTimeFormatter {
+ /// Creates a new [`ICU4XDateTimeFormatter`] from locale data.
+ #[diplomat::rust_link(icu::datetime::DateTimeFormatter::try_new, FnInStruct)]
+ pub fn create_with_lengths(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ date_length: ICU4XDateLength,
+ time_length: ICU4XTimeLength,
+ ) -> Result<Box<ICU4XDateTimeFormatter>, ICU4XError> {
+ let locale = locale.to_datalocale();
+ let options = length::Bag::from_date_time_style(date_length.into(), time_length.into());
+
+ Ok(Box::new(ICU4XDateTimeFormatter(call_constructor!(
+ DateTimeFormatter::try_new,
+ DateTimeFormatter::try_new_with_any_provider,
+ DateTimeFormatter::try_new_with_buffer_provider,
+ provider,
+ &locale,
+ options.into()
+ )?)))
+ }
+
+ /// Formats a [`ICU4XDateTime`] to a string.
+ #[diplomat::rust_link(icu::datetime::DateTimeFormatter::format, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::datetime::DateTimeFormatter::format_to_string,
+ FnInStruct,
+ hidden
+ )]
+ pub fn format_datetime(
+ &self,
+ value: &ICU4XDateTime,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ self.0.format(&value.0)?.write_to(write)?;
+ Ok(())
+ }
+
+ /// Formats a [`ICU4XIsoDateTime`] to a string.
+ ///
+ /// Will convert to this formatter's calendar first
+ #[diplomat::rust_link(icu::datetime::DateTimeFormatter::format, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::datetime::DateTimeFormatter::format_to_string,
+ FnInStruct,
+ hidden
+ )]
+ pub fn format_iso_datetime(
+ &self,
+ value: &ICU4XIsoDateTime,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ let any = value.0.to_any();
+ self.0.format(&any)?.write_to(write)?;
+ Ok(())
+ }
+ }
+}
diff --git a/intl/icu_capi/src/decimal.rs b/intl/icu_capi/src/decimal.rs
new file mode 100644
index 0000000000..290f1c5a63
--- /dev/null
+++ b/intl/icu_capi/src/decimal.rs
@@ -0,0 +1,109 @@
+// 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]
+pub mod ffi {
+ use alloc::boxed::Box;
+ use icu_decimal::{
+ options::{FixedDecimalFormatterOptions, GroupingStrategy},
+ provider::DecimalSymbolsV1Marker,
+ FixedDecimalFormatter,
+ };
+ use icu_provider_adapters::any_payload::AnyPayloadProvider;
+ use writeable::Writeable;
+
+ use crate::{
+ data_struct::ffi::ICU4XDataStruct, errors::ffi::ICU4XError,
+ fixed_decimal::ffi::ICU4XFixedDecimal, locale::ffi::ICU4XLocale,
+ provider::ffi::ICU4XDataProvider,
+ };
+
+ #[diplomat::opaque]
+ /// An ICU4X Fixed Decimal Format object, capable of formatting a [`ICU4XFixedDecimal`] as a string.
+ #[diplomat::rust_link(icu::decimal::FixedDecimalFormatter, Struct)]
+ pub struct ICU4XFixedDecimalFormatter(pub FixedDecimalFormatter);
+
+ #[diplomat::rust_link(icu::decimal::options::GroupingStrategy, Enum)]
+ pub enum ICU4XFixedDecimalGroupingStrategy {
+ Auto,
+ Never,
+ Always,
+ Min2,
+ }
+
+ impl ICU4XFixedDecimalFormatter {
+ /// Creates a new [`ICU4XFixedDecimalFormatter`] from locale data.
+ #[diplomat::rust_link(icu::decimal::FixedDecimalFormatter::try_new, FnInStruct)]
+ pub fn create_with_grouping_strategy(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ grouping_strategy: ICU4XFixedDecimalGroupingStrategy,
+ ) -> Result<Box<ICU4XFixedDecimalFormatter>, ICU4XError> {
+ let locale = locale.to_datalocale();
+
+ let grouping_strategy = match grouping_strategy {
+ ICU4XFixedDecimalGroupingStrategy::Auto => GroupingStrategy::Auto,
+ ICU4XFixedDecimalGroupingStrategy::Never => GroupingStrategy::Never,
+ ICU4XFixedDecimalGroupingStrategy::Always => GroupingStrategy::Always,
+ ICU4XFixedDecimalGroupingStrategy::Min2 => GroupingStrategy::Min2,
+ };
+ let mut options = FixedDecimalFormatterOptions::default();
+ options.grouping_strategy = grouping_strategy;
+ Ok(Box::new(ICU4XFixedDecimalFormatter(call_constructor!(
+ FixedDecimalFormatter::try_new,
+ FixedDecimalFormatter::try_new_with_any_provider,
+ FixedDecimalFormatter::try_new_with_buffer_provider,
+ provider,
+ &locale,
+ options,
+ )?)))
+ }
+
+ /// Creates a new [`ICU4XFixedDecimalFormatter`] from preconstructed locale data in the form of an [`ICU4XDataStruct`]
+ /// constructed from `ICU4XDataStruct::create_decimal_symbols()`.
+ ///
+ /// The contents of the data struct will be consumed: if you wish to use the struct again it will have to be reconstructed.
+ /// Passing a consumed struct to this method will return an error.
+ #[diplomat::attr(dart, disable)]
+ pub fn create_with_decimal_symbols_v1(
+ data_struct: &ICU4XDataStruct,
+ grouping_strategy: ICU4XFixedDecimalGroupingStrategy,
+ ) -> Result<Box<ICU4XFixedDecimalFormatter>, ICU4XError> {
+ let grouping_strategy = match grouping_strategy {
+ ICU4XFixedDecimalGroupingStrategy::Auto => GroupingStrategy::Auto,
+ ICU4XFixedDecimalGroupingStrategy::Never => GroupingStrategy::Never,
+ ICU4XFixedDecimalGroupingStrategy::Always => GroupingStrategy::Always,
+ ICU4XFixedDecimalGroupingStrategy::Min2 => GroupingStrategy::Min2,
+ };
+ let mut options = FixedDecimalFormatterOptions::default();
+ options.grouping_strategy = grouping_strategy;
+ Ok(Box::new(ICU4XFixedDecimalFormatter(
+ FixedDecimalFormatter::try_new_with_any_provider(
+ &AnyPayloadProvider::from_any_payload::<DecimalSymbolsV1Marker>(
+ // Note: This clone is free, since cloning AnyPayload is free.
+ data_struct.0.clone(),
+ ),
+ &Default::default(),
+ options,
+ )?,
+ )))
+ }
+
+ /// Formats a [`ICU4XFixedDecimal`] to a string.
+ #[diplomat::rust_link(icu::decimal::FixedDecimalFormatter::format, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::decimal::FixedDecimalFormatter::format_to_string,
+ FnInStruct,
+ hidden
+ )]
+ pub fn format(
+ &self,
+ value: &ICU4XFixedDecimal,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ self.0.format(&value.0).write_to(write)?;
+ Ok(())
+ }
+ }
+}
diff --git a/intl/icu_capi/src/displaynames.rs b/intl/icu_capi/src/displaynames.rs
new file mode 100644
index 0000000000..eec14ed839
--- /dev/null
+++ b/intl/icu_capi/src/displaynames.rs
@@ -0,0 +1,156 @@
+// 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]
+pub mod ffi {
+ use crate::errors::ffi::ICU4XError;
+ use crate::locale::ffi::ICU4XLocale;
+ use crate::provider::ffi::ICU4XDataProvider;
+ use alloc::boxed::Box;
+ use diplomat_runtime::DiplomatWriteable;
+ #[allow(unused_imports)] // feature-specific
+ use icu_displaynames::{DisplayNamesOptions, Fallback, LanguageDisplay};
+ use icu_displaynames::{LocaleDisplayNamesFormatter, RegionDisplayNames};
+ use icu_locid::subtags::Region;
+ use writeable::Writeable;
+
+ // FFI version of `LocaleDisplayNamesFormatter`.
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::displaynames::LocaleDisplayNamesFormatter, Struct)]
+ pub struct ICU4XLocaleDisplayNamesFormatter(pub LocaleDisplayNamesFormatter);
+
+ // FFI version of `RegionDisplayNames`.
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::displaynames::RegionDisplayNames, Struct)]
+ pub struct ICU4XRegionDisplayNames(pub RegionDisplayNames);
+
+ // FFI version of `DisplayNamesOptions`.
+ #[diplomat::rust_link(icu::displaynames::options::DisplayNamesOptions, Struct)]
+ pub struct ICU4XDisplayNamesOptionsV1 {
+ /// The optional formatting style to use for display name.
+ pub style: ICU4XDisplayNamesStyle,
+ /// The fallback return when the system does not have the
+ /// requested display name, defaults to "code".
+ pub fallback: ICU4XDisplayNamesFallback,
+ /// The language display kind, defaults to "dialect".
+ pub language_display: ICU4XLanguageDisplay,
+ }
+
+ // FFI version of `Style` option for the display names.
+ #[diplomat::rust_link(icu::displaynames::options::Style, Enum)]
+ pub enum ICU4XDisplayNamesStyle {
+ Auto,
+ Narrow,
+ Short,
+ Long,
+ Menu,
+ }
+
+ // FFI version of `Fallback` option for the display names.
+ #[diplomat::rust_link(icu::displaynames::options::Fallback, Enum)]
+ #[diplomat::enum_convert(Fallback, needs_wildcard)]
+ pub enum ICU4XDisplayNamesFallback {
+ Code,
+ None,
+ }
+
+ // FFI version of `LanguageDisplay`.
+ #[diplomat::rust_link(icu::displaynames::options::LanguageDisplay, Enum)]
+ #[diplomat::enum_convert(LanguageDisplay, needs_wildcard)]
+ pub enum ICU4XLanguageDisplay {
+ Dialect,
+ Standard,
+ }
+
+ impl ICU4XLocaleDisplayNamesFormatter {
+ /// Creates a new `LocaleDisplayNamesFormatter` from locale data and an options bag.
+ #[diplomat::rust_link(icu::displaynames::LocaleDisplayNamesFormatter::try_new, FnInStruct)]
+ pub fn create(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ options: ICU4XDisplayNamesOptionsV1,
+ ) -> Result<Box<ICU4XLocaleDisplayNamesFormatter>, ICU4XError> {
+ let locale = locale.to_datalocale();
+ let options = DisplayNamesOptions::from(options);
+
+ Ok(Box::new(ICU4XLocaleDisplayNamesFormatter(
+ call_constructor!(
+ LocaleDisplayNamesFormatter::try_new,
+ LocaleDisplayNamesFormatter::try_new_with_any_provider,
+ LocaleDisplayNamesFormatter::try_new_with_buffer_provider,
+ provider,
+ &locale,
+ options,
+ )?,
+ )))
+ }
+
+ /// Returns the locale-specific display name of a locale.
+ #[diplomat::rust_link(icu::displaynames::LocaleDisplayNamesFormatter::of, FnInStruct)]
+ pub fn of(
+ &self,
+ locale: &ICU4XLocale,
+ write: &mut DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ self.0.of(&locale.0).write_to(write)?;
+ Ok(())
+ }
+ }
+
+ impl ICU4XRegionDisplayNames {
+ /// Creates a new `RegionDisplayNames` from locale data and an options bag.
+ #[diplomat::rust_link(icu::displaynames::RegionDisplayNames::try_new, FnInStruct)]
+ pub fn create(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ ) -> Result<Box<ICU4XRegionDisplayNames>, ICU4XError> {
+ let locale = locale.to_datalocale();
+ Ok(Box::new(ICU4XRegionDisplayNames(call_constructor!(
+ RegionDisplayNames::try_new,
+ RegionDisplayNames::try_new_with_any_provider,
+ RegionDisplayNames::try_new_with_buffer_provider,
+ provider,
+ &locale,
+ Default::default()
+ )?)))
+ }
+
+ /// Returns the locale specific display name of a region.
+ /// Note that the funtion returns an empty string in case the display name for a given
+ /// region code is not found.
+ #[diplomat::rust_link(icu::displaynames::RegionDisplayNames::of, FnInStruct)]
+ pub fn of(&self, region: &str, write: &mut DiplomatWriteable) -> Result<(), ICU4XError> {
+ self.0
+ .of(region.parse::<Region>()?)
+ .unwrap_or("")
+ .write_to(write)?;
+ Ok(())
+ }
+ }
+}
+
+#[allow(unused_imports)] // feature-specific
+use icu_displaynames::{DisplayNamesOptions, Fallback, LanguageDisplay, Style};
+
+impl From<ffi::ICU4XDisplayNamesStyle> for Option<Style> {
+ fn from(style: ffi::ICU4XDisplayNamesStyle) -> Option<Style> {
+ match style {
+ ffi::ICU4XDisplayNamesStyle::Auto => None,
+ ffi::ICU4XDisplayNamesStyle::Narrow => Some(Style::Narrow),
+ ffi::ICU4XDisplayNamesStyle::Short => Some(Style::Short),
+ ffi::ICU4XDisplayNamesStyle::Long => Some(Style::Long),
+ ffi::ICU4XDisplayNamesStyle::Menu => Some(Style::Menu),
+ }
+ }
+}
+
+impl From<ffi::ICU4XDisplayNamesOptionsV1> for DisplayNamesOptions {
+ fn from(other: ffi::ICU4XDisplayNamesOptionsV1) -> DisplayNamesOptions {
+ let mut options = DisplayNamesOptions::default();
+ options.style = other.style.into();
+ options.fallback = other.fallback.into();
+ options.language_display = other.language_display.into();
+ options
+ }
+}
diff --git a/intl/icu_capi/src/errors.rs b/intl/icu_capi/src/errors.rs
new file mode 100644
index 0000000000..bf17a650f3
--- /dev/null
+++ b/intl/icu_capi/src/errors.rs
@@ -0,0 +1,407 @@
+// 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 self::ffi::ICU4XError;
+use core::fmt;
+#[cfg(feature = "icu_decimal")]
+use fixed_decimal::FixedDecimalError;
+#[cfg(any(
+ feature = "icu_datetime",
+ feature = "icu_timezone",
+ feature = "icu_calendar"
+))]
+use icu_calendar::CalendarError;
+#[cfg(feature = "icu_collator")]
+use icu_collator::CollatorError;
+#[cfg(feature = "icu_datetime")]
+use icu_datetime::DateTimeError;
+#[cfg(any(feature = "icu_decimal", feature = "icu_datetime"))]
+use icu_decimal::DecimalError;
+#[cfg(feature = "icu_list")]
+use icu_list::ListError;
+use icu_locid::ParserError;
+#[cfg(feature = "icu_locid_transform")]
+use icu_locid_transform::LocaleTransformError;
+#[cfg(feature = "icu_normalizer")]
+use icu_normalizer::NormalizerError;
+#[cfg(any(feature = "icu_plurals", feature = "icu_datetime"))]
+use icu_plurals::PluralsError;
+#[cfg(feature = "icu_properties")]
+use icu_properties::PropertiesError;
+use icu_provider::{DataError, DataErrorKind};
+#[cfg(feature = "icu_segmenter")]
+use icu_segmenter::SegmenterError;
+#[cfg(any(feature = "icu_timezone", feature = "icu_datetime"))]
+use icu_timezone::TimeZoneError;
+use tinystr::TinyStrError;
+
+#[diplomat::bridge]
+pub mod ffi {
+ use alloc::boxed::Box;
+
+ #[derive(Debug, Copy, Clone, PartialEq, Eq)]
+ #[repr(C)]
+ /// A common enum for errors that ICU4X may return, organized by API
+ ///
+ /// The error names are stable and can be checked against as strings in the JS API
+ #[diplomat::rust_link(fixed_decimal::FixedDecimalError, Enum, compact)]
+ #[diplomat::rust_link(icu::calendar::CalendarError, Enum, compact)]
+ #[diplomat::rust_link(icu::collator::CollatorError, Enum, compact)]
+ #[diplomat::rust_link(icu::datetime::DateTimeError, Enum, compact)]
+ #[diplomat::rust_link(icu::decimal::DecimalError, Enum, compact)]
+ #[diplomat::rust_link(icu::list::ListError, Enum, compact)]
+ #[diplomat::rust_link(icu::locid::ParserError, Enum, compact)]
+ #[diplomat::rust_link(icu::locid_transform::LocaleTransformError, Enum, compact)]
+ #[diplomat::rust_link(icu::normalizer::NormalizerError, Enum, compact)]
+ #[diplomat::rust_link(icu::plurals::PluralsError, Enum, compact)]
+ #[diplomat::rust_link(icu::properties::PropertiesError, Enum, compact)]
+ #[diplomat::rust_link(icu::provider::DataError, Struct, compact)]
+ #[diplomat::rust_link(icu::provider::DataErrorKind, Enum, compact)]
+ #[diplomat::rust_link(icu::segmenter::SegmenterError, Enum, compact)]
+ #[diplomat::rust_link(icu::timezone::TimeZoneError, Enum, compact)]
+ pub enum ICU4XError {
+ // general errors
+ /// The error is not currently categorized as ICU4XError.
+ /// Please file a bug
+ UnknownError = 0x00,
+ /// An error arising from writing to a string
+ /// Typically found when not enough space is allocated
+ /// Most APIs that return a string may return this error
+ WriteableError = 0x01,
+ // Some input was out of bounds
+ OutOfBoundsError = 0x02,
+
+ // general data errors
+ // See DataError
+ DataMissingDataKeyError = 0x1_00,
+ DataMissingVariantError = 0x1_01,
+ DataMissingLocaleError = 0x1_02,
+ DataNeedsVariantError = 0x1_03,
+ DataNeedsLocaleError = 0x1_04,
+ DataExtraneousLocaleError = 0x1_05,
+ DataFilteredResourceError = 0x1_06,
+ DataMismatchedTypeError = 0x1_07,
+ DataMissingPayloadError = 0x1_08,
+ DataInvalidStateError = 0x1_09,
+ DataCustomError = 0x1_0A,
+ DataIoError = 0x1_0B,
+ DataUnavailableBufferFormatError = 0x1_0C,
+ DataMismatchedAnyBufferError = 0x1_0D,
+
+ // locale errors
+ /// The subtag being requested was not set
+ LocaleUndefinedSubtagError = 0x2_00,
+ /// The locale or subtag string failed to parse
+ LocaleParserLanguageError = 0x2_01,
+ LocaleParserSubtagError = 0x2_02,
+ LocaleParserExtensionError = 0x2_03,
+
+ // data struct errors
+ /// Attempted to construct an invalid data struct
+ DataStructValidityError = 0x3_00,
+
+ // property errors
+ PropertyUnknownScriptIdError = 0x4_00,
+ PropertyUnknownGeneralCategoryGroupError = 0x4_01,
+ PropertyUnexpectedPropertyNameError = 0x4_02,
+
+ // fixed_decimal errors
+ FixedDecimalLimitError = 0x5_00,
+ FixedDecimalSyntaxError = 0x5_01,
+
+ // plural errors
+ PluralsParserError = 0x6_00,
+
+ // datetime errors
+ CalendarParseError = 0x7_00,
+ CalendarOverflowError = 0x7_01,
+ CalendarUnderflowError = 0x7_02,
+ CalendarOutOfRangeError = 0x7_03,
+ CalendarUnknownEraError = 0x7_04,
+ CalendarUnknownMonthCodeError = 0x7_05,
+ CalendarMissingInputError = 0x7_06,
+ CalendarUnknownKindError = 0x7_07,
+ CalendarMissingError = 0x7_08,
+
+ // datetime format errors
+ DateTimePatternError = 0x8_00,
+ DateTimeMissingInputFieldError = 0x8_01,
+ DateTimeSkeletonError = 0x8_02,
+ DateTimeUnsupportedFieldError = 0x8_03,
+ DateTimeUnsupportedOptionsError = 0x8_04,
+ DateTimeMissingWeekdaySymbolError = 0x8_05,
+ DateTimeMissingMonthSymbolError = 0x8_06,
+ DateTimeFixedDecimalError = 0x8_07,
+ DateTimeMismatchedCalendarError = 0x8_08,
+
+ // tinystr errors
+ TinyStrTooLargeError = 0x9_00,
+ TinyStrContainsNullError = 0x9_01,
+ TinyStrNonAsciiError = 0x9_02,
+
+ // timezone errors
+ TimeZoneOffsetOutOfBoundsError = 0xA_00,
+ TimeZoneInvalidOffsetError = 0xA_01,
+ TimeZoneMissingInputError = 0xA_02,
+ TimeZoneInvalidIdError = 0xA_03,
+
+ // normalizer errors
+ NormalizerFutureExtensionError = 0xB_00,
+ NormalizerValidationError = 0xB_01,
+ }
+}
+
+impl ICU4XError {
+ #[cfg(feature = "logging")]
+ #[inline]
+ pub(crate) fn log_original<T: core::fmt::Display + ?Sized>(self, e: &T) -> Self {
+ use core::any;
+ log::warn!(
+ "Returning ICU4XError::{:?} based on original {}: {}",
+ self,
+ any::type_name::<T>(),
+ e
+ );
+ self
+ }
+
+ #[cfg(not(feature = "logging"))]
+ #[inline]
+ pub(crate) fn log_original<T: core::fmt::Display + ?Sized>(self, _e: &T) -> Self {
+ self
+ }
+}
+
+impl From<fmt::Error> for ICU4XError {
+ fn from(e: fmt::Error) -> Self {
+ ICU4XError::WriteableError.log_original(&e)
+ }
+}
+
+impl From<DataError> for ICU4XError {
+ fn from(e: DataError) -> Self {
+ match e.kind {
+ DataErrorKind::MissingDataKey => ICU4XError::DataMissingDataKeyError,
+ DataErrorKind::MissingLocale => ICU4XError::DataMissingLocaleError,
+ DataErrorKind::NeedsLocale => ICU4XError::DataNeedsLocaleError,
+ DataErrorKind::ExtraneousLocale => ICU4XError::DataExtraneousLocaleError,
+ DataErrorKind::FilteredResource => ICU4XError::DataFilteredResourceError,
+ DataErrorKind::MismatchedType(..) => ICU4XError::DataMismatchedTypeError,
+ DataErrorKind::MissingPayload => ICU4XError::DataMissingPayloadError,
+ DataErrorKind::InvalidState => ICU4XError::DataInvalidStateError,
+ DataErrorKind::Custom => ICU4XError::DataCustomError,
+ #[cfg(all(
+ feature = "provider_fs",
+ not(any(target_arch = "wasm32", target_os = "none"))
+ ))]
+ DataErrorKind::Io(..) => ICU4XError::DataIoError,
+ // datagen only
+ // DataErrorKind::MissingSourceData(..) => ..,
+ DataErrorKind::UnavailableBufferFormat(..) => {
+ ICU4XError::DataUnavailableBufferFormatError
+ }
+ _ => ICU4XError::UnknownError,
+ }
+ .log_original(&e)
+ }
+}
+
+#[cfg(feature = "icu_collator")]
+impl From<CollatorError> for ICU4XError {
+ fn from(e: CollatorError) -> Self {
+ match e {
+ CollatorError::NotFound => ICU4XError::DataMissingPayloadError,
+ CollatorError::MalformedData => ICU4XError::DataInvalidStateError,
+ CollatorError::Data(_) => ICU4XError::DataIoError,
+ _ => ICU4XError::DataIoError,
+ }
+ .log_original(&e)
+ }
+}
+
+#[cfg(feature = "icu_properties")]
+impl From<PropertiesError> for ICU4XError {
+ fn from(e: PropertiesError) -> Self {
+ match e {
+ PropertiesError::PropDataLoad(e) => e.into(),
+ PropertiesError::UnknownScriptId(..) => ICU4XError::PropertyUnknownScriptIdError,
+ PropertiesError::UnknownGeneralCategoryGroup(..) => {
+ ICU4XError::PropertyUnknownGeneralCategoryGroupError
+ }
+ PropertiesError::UnexpectedPropertyName => {
+ ICU4XError::PropertyUnexpectedPropertyNameError
+ }
+ _ => ICU4XError::UnknownError,
+ }
+ .log_original(&e)
+ }
+}
+
+#[cfg(any(
+ feature = "icu_datetime",
+ feature = "icu_timezone",
+ feature = "icu_calendar"
+))]
+impl From<CalendarError> for ICU4XError {
+ fn from(e: CalendarError) -> Self {
+ match e {
+ CalendarError::Parse => ICU4XError::CalendarParseError,
+ CalendarError::Overflow { field: _, max: _ } => ICU4XError::CalendarOverflowError,
+ CalendarError::Underflow { field: _, min: _ } => ICU4XError::CalendarUnderflowError,
+ CalendarError::OutOfRange => ICU4XError::CalendarOutOfRangeError,
+ CalendarError::UnknownEra(..) => ICU4XError::CalendarUnknownEraError,
+ CalendarError::UnknownMonthCode(..) => ICU4XError::CalendarUnknownMonthCodeError,
+ CalendarError::MissingInput(_) => ICU4XError::CalendarMissingInputError,
+ CalendarError::UnknownAnyCalendarKind(_) => ICU4XError::CalendarUnknownKindError,
+ CalendarError::MissingCalendar => ICU4XError::CalendarMissingError,
+ CalendarError::Data(e) => e.into(),
+ _ => ICU4XError::UnknownError,
+ }
+ .log_original(&e)
+ }
+}
+
+#[cfg(feature = "icu_datetime")]
+impl From<DateTimeError> for ICU4XError {
+ fn from(e: DateTimeError) -> Self {
+ match e {
+ DateTimeError::Pattern(_) => ICU4XError::DateTimePatternError,
+ DateTimeError::Format(err) => err.into(),
+ DateTimeError::Data(err) => err.into(),
+ DateTimeError::MissingInputField(_) => ICU4XError::DateTimeMissingInputFieldError,
+ // TODO(#1324): Add back skeleton errors
+ // DateTimeFormatterError::Skeleton(_) => ICU4XError::DateTimeFormatSkeletonError,
+ DateTimeError::UnsupportedField(_) => ICU4XError::DateTimeUnsupportedFieldError,
+ DateTimeError::UnsupportedOptions => ICU4XError::DateTimeUnsupportedOptionsError,
+ DateTimeError::PluralRules(err) => err.into(),
+ DateTimeError::DateTimeInput(err) => err.into(),
+ DateTimeError::MissingWeekdaySymbol(_) => ICU4XError::DateTimeMissingWeekdaySymbolError,
+ DateTimeError::MissingMonthSymbol(_) => ICU4XError::DateTimeMissingMonthSymbolError,
+ DateTimeError::FixedDecimal => ICU4XError::DateTimeFixedDecimalError,
+ DateTimeError::FixedDecimalFormatter(err) => err.into(),
+ DateTimeError::MismatchedAnyCalendar(_, _) => {
+ ICU4XError::DateTimeMismatchedCalendarError
+ }
+ _ => ICU4XError::UnknownError,
+ }
+ .log_original(&e)
+ }
+}
+
+#[cfg(feature = "icu_decimal")]
+impl From<FixedDecimalError> for ICU4XError {
+ fn from(e: FixedDecimalError) -> Self {
+ match e {
+ FixedDecimalError::Limit => ICU4XError::FixedDecimalLimitError,
+ FixedDecimalError::Syntax => ICU4XError::FixedDecimalSyntaxError,
+ _ => ICU4XError::UnknownError,
+ }
+ .log_original(&e)
+ }
+}
+
+#[cfg(any(feature = "icu_plurals", feature = "icu_datetime"))]
+impl From<PluralsError> for ICU4XError {
+ fn from(e: PluralsError) -> Self {
+ match e {
+ PluralsError::Data(e) => e.into(),
+ _ => ICU4XError::UnknownError,
+ }
+ .log_original(&e)
+ }
+}
+
+#[cfg(any(feature = "icu_decimal", feature = "icu_datetime"))]
+impl From<DecimalError> for ICU4XError {
+ fn from(e: DecimalError) -> Self {
+ match e {
+ DecimalError::Data(e) => e.into(),
+ _ => ICU4XError::UnknownError,
+ }
+ .log_original(&e)
+ }
+}
+
+#[cfg(feature = "icu_locid_transform")]
+impl From<LocaleTransformError> for ICU4XError {
+ fn from(e: LocaleTransformError) -> Self {
+ match e {
+ LocaleTransformError::Data(e) => e.into(),
+ _ => ICU4XError::UnknownError,
+ }
+ .log_original(&e)
+ }
+}
+
+#[cfg(feature = "icu_segmenter")]
+impl From<SegmenterError> for ICU4XError {
+ fn from(e: SegmenterError) -> Self {
+ match e {
+ SegmenterError::Data(e) => e.into(),
+ _ => ICU4XError::UnknownError,
+ }
+ .log_original(&e)
+ }
+}
+
+#[cfg(feature = "icu_list")]
+impl From<ListError> for ICU4XError {
+ fn from(e: ListError) -> Self {
+ match e {
+ ListError::Data(e) => e.into(),
+ _ => ICU4XError::UnknownError,
+ }
+ .log_original(&e)
+ }
+}
+
+impl From<ParserError> for ICU4XError {
+ fn from(e: ParserError) -> Self {
+ match e {
+ ParserError::InvalidLanguage => ICU4XError::LocaleParserLanguageError,
+ ParserError::InvalidSubtag => ICU4XError::LocaleParserSubtagError,
+ ParserError::InvalidExtension => ICU4XError::LocaleParserExtensionError,
+ _ => ICU4XError::UnknownError,
+ }
+ .log_original(&e)
+ }
+}
+
+impl From<TinyStrError> for ICU4XError {
+ fn from(e: TinyStrError) -> Self {
+ match e {
+ TinyStrError::TooLarge { .. } => ICU4XError::TinyStrTooLargeError,
+ TinyStrError::ContainsNull => ICU4XError::TinyStrContainsNullError,
+ TinyStrError::NonAscii => ICU4XError::TinyStrNonAsciiError,
+ _ => ICU4XError::UnknownError,
+ }
+ .log_original(&e)
+ }
+}
+
+#[cfg(any(feature = "icu_timezone", feature = "icu_datetime"))]
+impl From<TimeZoneError> for ICU4XError {
+ fn from(e: TimeZoneError) -> Self {
+ match e {
+ TimeZoneError::OffsetOutOfBounds => ICU4XError::TimeZoneOffsetOutOfBoundsError,
+ TimeZoneError::InvalidOffset => ICU4XError::TimeZoneInvalidOffsetError,
+ TimeZoneError::Data(err) => err.into(),
+ _ => ICU4XError::UnknownError,
+ }
+ .log_original(&e)
+ }
+}
+
+#[cfg(feature = "icu_normalizer")]
+impl From<NormalizerError> for ICU4XError {
+ fn from(e: NormalizerError) -> Self {
+ match e {
+ NormalizerError::FutureExtension => ICU4XError::NormalizerFutureExtensionError,
+ NormalizerError::ValidationError => ICU4XError::NormalizerValidationError,
+ NormalizerError::Data(err) => err.into(),
+ _ => ICU4XError::UnknownError,
+ }
+ .log_original(&e)
+ }
+}
diff --git a/intl/icu_capi/src/fallbacker.rs b/intl/icu_capi/src/fallbacker.rs
new file mode 100644
index 0000000000..b3f21b7530
--- /dev/null
+++ b/intl/icu_capi/src/fallbacker.rs
@@ -0,0 +1,195 @@
+// 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]
+pub mod ffi {
+ use alloc::boxed::Box;
+ use icu_locid_transform::fallback::LocaleFallbackConfig;
+ use icu_locid_transform::fallback::LocaleFallbackIterator;
+ use icu_locid_transform::fallback::LocaleFallbackPriority;
+ use icu_locid_transform::fallback::LocaleFallbackerWithConfig;
+ use icu_locid_transform::LocaleFallbacker;
+
+ use crate::{
+ errors::ffi::ICU4XError, locale::ffi::ICU4XLocale, provider::ffi::ICU4XDataProvider,
+ };
+
+ /// An object that runs the ICU4X locale fallback algorithm.
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::locid_transform::fallback::LocaleFallbacker, Struct)]
+ #[diplomat::rust_link(
+ icu::locid_transform::fallback::LocaleFallbackerBorrowed,
+ Struct,
+ hidden
+ )]
+ pub struct ICU4XLocaleFallbacker(pub LocaleFallbacker);
+
+ /// Priority mode for the ICU4X fallback algorithm.
+ #[diplomat::enum_convert(LocaleFallbackPriority, needs_wildcard)]
+ #[diplomat::rust_link(icu::locid_transform::fallback::LocaleFallbackPriority, Enum)]
+ #[diplomat::rust_link(
+ icu::locid_transform::fallback::LocaleFallbackPriority::const_default,
+ FnInEnum,
+ hidden
+ )]
+ pub enum ICU4XLocaleFallbackPriority {
+ Language = 0,
+ Region = 1,
+ Collation = 2,
+ }
+
+ /// What additional data is required to load when performing fallback.
+ #[diplomat::rust_link(icu::locid_transform::fallback::LocaleFallbackSupplement, Enum)]
+ #[diplomat::rust_link(
+ icu::locid_transform::fallback::LocaleFallbackSupplement::const_default,
+ FnInEnum,
+ hidden
+ )]
+ pub enum ICU4XLocaleFallbackSupplement {
+ None = 0,
+ Collation = 1,
+ }
+
+ /// Collection of configurations for the ICU4X fallback algorithm.
+ #[diplomat::rust_link(icu::locid_transform::fallback::LocaleFallbackConfig, Struct)]
+ #[diplomat::rust_link(
+ icu::locid_transform::fallback::LocaleFallbackConfig::const_default,
+ FnInStruct,
+ hidden
+ )]
+ pub struct ICU4XLocaleFallbackConfig<'a> {
+ /// Choice of priority mode.
+ pub priority: ICU4XLocaleFallbackPriority,
+ /// An empty string is considered `None`.
+ pub extension_key: &'a str,
+ /// Fallback supplement data key to customize fallback rules.
+ pub fallback_supplement: ICU4XLocaleFallbackSupplement,
+ }
+
+ /// An object that runs the ICU4X locale fallback algorithm with specific configurations.
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::locid_transform::fallback::LocaleFallbacker, Struct)]
+ #[diplomat::rust_link(icu::locid_transform::fallback::LocaleFallbackerWithConfig, Struct)]
+ pub struct ICU4XLocaleFallbackerWithConfig<'a>(pub LocaleFallbackerWithConfig<'a>);
+
+ /// An iterator over the locale under fallback.
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::locid_transform::fallback::LocaleFallbackIterator, Struct)]
+ pub struct ICU4XLocaleFallbackIterator<'a>(pub LocaleFallbackIterator<'a, 'a>);
+
+ impl ICU4XLocaleFallbacker {
+ /// Creates a new `ICU4XLocaleFallbacker` from a data provider.
+ #[diplomat::rust_link(icu::locid_transform::fallback::LocaleFallbacker::new, FnInStruct)]
+ pub fn create(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XLocaleFallbacker>, ICU4XError> {
+ Ok(Box::new(ICU4XLocaleFallbacker(call_constructor!(
+ LocaleFallbacker::new [r => Ok(r.static_to_owned())],
+ LocaleFallbacker::try_new_with_any_provider,
+ LocaleFallbacker::try_new_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Creates a new `ICU4XLocaleFallbacker` without data for limited functionality.
+ #[diplomat::rust_link(
+ icu::locid_transform::fallback::LocaleFallbacker::new_without_data,
+ FnInStruct
+ )]
+ pub fn create_without_data() -> Box<ICU4XLocaleFallbacker> {
+ Box::new(ICU4XLocaleFallbacker(LocaleFallbacker::new_without_data()))
+ }
+
+ /// Associates this `ICU4XLocaleFallbacker` with configuration options.
+ #[diplomat::rust_link(
+ icu::locid_transform::fallback::LocaleFallbacker::for_config,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu::locid_transform::fallback::LocaleFallbackerBorrowed::for_config,
+ FnInStruct,
+ hidden
+ )]
+ pub fn for_config<'a, 'temp>(
+ &'a self,
+ config: ICU4XLocaleFallbackConfig<'temp>,
+ ) -> Result<Box<ICU4XLocaleFallbackerWithConfig<'a>>, ICU4XError> {
+ Ok(Box::new(ICU4XLocaleFallbackerWithConfig(
+ self.0.for_config(LocaleFallbackConfig::try_from(config)?),
+ )))
+ }
+ }
+
+ impl<'a> ICU4XLocaleFallbackerWithConfig<'a> {
+ /// Creates an iterator from a locale with each step of fallback.
+ #[diplomat::rust_link(
+ icu::locid_transform::fallback::LocaleFallbacker::fallback_for,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu::locid_transform::fallback::LocaleFallbackerBorrowed::fallback_for,
+ FnInStruct,
+ hidden
+ )]
+ #[diplomat::rust_link(
+ icu::locid_transform::fallback::LocaleFallbackerWithConfig::fallback_for,
+ FnInStruct,
+ hidden
+ )]
+ pub fn fallback_for_locale<'b: 'a, 'temp>(
+ &'b self,
+ locale: &'temp ICU4XLocale,
+ ) -> Box<ICU4XLocaleFallbackIterator<'a>> {
+ Box::new(ICU4XLocaleFallbackIterator(
+ self.0.fallback_for((&locale.0).into()),
+ ))
+ }
+ }
+
+ impl<'a> ICU4XLocaleFallbackIterator<'a> {
+ /// Gets a snapshot of the current state of the locale.
+ #[diplomat::rust_link(
+ icu::locid_transform::fallback::LocaleFallbackIterator::get,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu::locid_transform::fallback::LocaleFallbackIterator::take,
+ FnInStruct,
+ hidden
+ )]
+ pub fn get(&self) -> Box<ICU4XLocale> {
+ Box::new(ICU4XLocale(self.0.get().clone().into_locale()))
+ }
+
+ /// Performs one step of the fallback algorithm, mutating the locale.
+ #[diplomat::rust_link(
+ icu::locid_transform::fallback::LocaleFallbackIterator::step,
+ FnInStruct
+ )]
+ pub fn step(&mut self) {
+ self.0.step();
+ }
+ }
+}
+
+impl TryFrom<ffi::ICU4XLocaleFallbackConfig<'_>>
+ for icu_locid_transform::fallback::LocaleFallbackConfig
+{
+ type Error = crate::errors::ffi::ICU4XError;
+ fn try_from(other: ffi::ICU4XLocaleFallbackConfig) -> Result<Self, Self::Error> {
+ let mut result = Self::default();
+ result.priority = other.priority.into();
+ result.extension_key = match other.extension_key {
+ "" => None,
+ s => Some(s.parse()?),
+ };
+ result.fallback_supplement = match other.fallback_supplement {
+ ffi::ICU4XLocaleFallbackSupplement::None => None,
+ ffi::ICU4XLocaleFallbackSupplement::Collation => {
+ Some(icu_locid_transform::fallback::LocaleFallbackSupplement::Collation)
+ }
+ };
+ Ok(result)
+ }
+}
diff --git a/intl/icu_capi/src/fixed_decimal.rs b/intl/icu_capi/src/fixed_decimal.rs
new file mode 100644
index 0000000000..6439698323
--- /dev/null
+++ b/intl/icu_capi/src/fixed_decimal.rs
@@ -0,0 +1,341 @@
+// 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 fixed_decimal::Sign;
+use fixed_decimal::SignDisplay;
+
+#[diplomat::bridge]
+pub mod ffi {
+ use alloc::boxed::Box;
+ use fixed_decimal::{DoublePrecision, FixedDecimal};
+ use writeable::Writeable;
+
+ use crate::errors::ffi::ICU4XError;
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal, Struct)]
+ pub struct ICU4XFixedDecimal(pub FixedDecimal);
+
+ /// The sign of a FixedDecimal, as shown in formatting.
+ #[diplomat::rust_link(fixed_decimal::Sign, Enum)]
+ pub enum ICU4XFixedDecimalSign {
+ /// No sign (implicitly positive, e.g., 1729).
+ 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)]
+ pub enum ICU4XFixedDecimalSignDisplay {
+ Auto,
+ Never,
+ Always,
+ ExceptZero,
+ Negative,
+ }
+
+ /// Increment used in a rounding operation.
+ #[diplomat::rust_link(fixed_decimal::RoundingIncrement, Enum)]
+ pub enum ICU4XRoundingIncrement {
+ MultiplesOf1,
+ MultiplesOf2,
+ MultiplesOf5,
+ MultiplesOf25,
+ }
+
+ impl ICU4XFixedDecimal {
+ /// Construct an [`ICU4XFixedDecimal`] from an integer.
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal, Struct)]
+ #[diplomat::attr(dart, disable)]
+ pub fn create_from_i32(v: i32) -> Box<ICU4XFixedDecimal> {
+ Box::new(ICU4XFixedDecimal(FixedDecimal::from(v)))
+ }
+
+ /// Construct an [`ICU4XFixedDecimal`] from an integer.
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal, Struct)]
+ #[diplomat::attr(dart, disable)]
+ pub fn create_from_u32(v: u32) -> Box<ICU4XFixedDecimal> {
+ Box::new(ICU4XFixedDecimal(FixedDecimal::from(v)))
+ }
+
+ /// Construct an [`ICU4XFixedDecimal`] from an integer.
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal, Struct)]
+ #[diplomat::attr(dart, rename = "create_from_int")]
+ pub fn create_from_i64(v: i64) -> Box<ICU4XFixedDecimal> {
+ Box::new(ICU4XFixedDecimal(FixedDecimal::from(v)))
+ }
+
+ /// Construct an [`ICU4XFixedDecimal`] from an integer.
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal, Struct)]
+ #[diplomat::attr(dart, disable)]
+ pub fn create_from_u64(v: u64) -> Box<ICU4XFixedDecimal> {
+ Box::new(ICU4XFixedDecimal(FixedDecimal::from(v)))
+ }
+
+ /// Construct an [`ICU4XFixedDecimal`] from an integer-valued float
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::try_from_f64, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FloatPrecision, Enum)]
+ #[diplomat::rust_link(fixed_decimal::DoublePrecision, Enum, hidden)]
+ #[diplomat::attr(dart, disable)]
+ #[diplomat::attr(dart, rename = "create_from_double_with_integer_precision")]
+ pub fn create_from_f64_with_integer_precision(
+ f: f64,
+ ) -> Result<Box<ICU4XFixedDecimal>, ICU4XError> {
+ let precision = DoublePrecision::Integer;
+ Ok(Box::new(ICU4XFixedDecimal(FixedDecimal::try_from_f64(
+ f, precision,
+ )?)))
+ }
+
+ /// Construct an [`ICU4XFixedDecimal`] from an float, with a given power of 10 for the lower magnitude
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::try_from_f64, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FloatPrecision, Enum)]
+ #[diplomat::rust_link(fixed_decimal::DoublePrecision, Enum, hidden)]
+ #[diplomat::attr(dart, rename = "create_from_double_with_lower_magnitude")]
+ pub fn create_from_f64_with_lower_magnitude(
+ f: f64,
+ magnitude: i16,
+ ) -> Result<Box<ICU4XFixedDecimal>, ICU4XError> {
+ let precision = DoublePrecision::Magnitude(magnitude);
+ Ok(Box::new(ICU4XFixedDecimal(FixedDecimal::try_from_f64(
+ f, precision,
+ )?)))
+ }
+
+ /// Construct an [`ICU4XFixedDecimal`] from an float, for a given number of significant digits
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::try_from_f64, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FloatPrecision, Enum)]
+ #[diplomat::rust_link(fixed_decimal::DoublePrecision, Enum, hidden)]
+ #[diplomat::attr(dart, rename = "create_from_double_with_significant_digits")]
+ pub fn create_from_f64_with_significant_digits(
+ f: f64,
+ digits: u8,
+ ) -> Result<Box<ICU4XFixedDecimal>, ICU4XError> {
+ let precision = DoublePrecision::SignificantDigits(digits);
+ Ok(Box::new(ICU4XFixedDecimal(FixedDecimal::try_from_f64(
+ f, precision,
+ )?)))
+ }
+
+ /// Construct an [`ICU4XFixedDecimal`] from an float, with enough digits to recover
+ /// the original floating point in IEEE 754 without needing trailing zeros
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::try_from_f64, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FloatPrecision, Enum)]
+ #[diplomat::rust_link(fixed_decimal::DoublePrecision, Enum, hidden)]
+ #[diplomat::attr(dart, rename = "create_from_double_with_double_precision")]
+ pub fn create_from_f64_with_floating_precision(
+ f: f64,
+ ) -> Result<Box<ICU4XFixedDecimal>, ICU4XError> {
+ let precision = DoublePrecision::Floating;
+ Ok(Box::new(ICU4XFixedDecimal(FixedDecimal::try_from_f64(
+ f, precision,
+ )?)))
+ }
+
+ /// Construct an [`ICU4XFixedDecimal`] from a string.
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::from_str, FnInStruct)]
+ pub fn create_from_string(v: &str) -> Result<Box<ICU4XFixedDecimal>, ICU4XError> {
+ let v = v.as_bytes(); // #2520
+ Ok(Box::new(ICU4XFixedDecimal(FixedDecimal::try_from(v)?)))
+ }
+
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::digit_at, FnInStruct)]
+ pub fn digit_at(&self, magnitude: i16) -> u8 {
+ self.0.digit_at(magnitude)
+ }
+
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::magnitude_range, FnInStruct)]
+ pub fn magnitude_start(&self) -> i16 {
+ *self.0.magnitude_range().start()
+ }
+
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::magnitude_range, FnInStruct)]
+ pub fn magnitude_end(&self) -> i16 {
+ *self.0.magnitude_range().end()
+ }
+
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::nonzero_magnitude_start, FnInStruct)]
+ pub fn nonzero_magnitude_start(&self) -> i16 {
+ self.0.nonzero_magnitude_start()
+ }
+
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::nonzero_magnitude_end, FnInStruct)]
+ pub fn nonzero_magnitude_end(&self) -> i16 {
+ self.0.nonzero_magnitude_end()
+ }
+
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::is_zero, FnInStruct)]
+ pub fn is_zero(&self) -> bool {
+ self.0.is_zero()
+ }
+
+ /// Multiply the [`ICU4XFixedDecimal`] by a given power of ten.
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::multiply_pow10, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::multiplied_pow10, FnInStruct, hidden)]
+ pub fn multiply_pow10(&mut self, power: i16) {
+ self.0.multiply_pow10(power)
+ }
+
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::sign, FnInStruct)]
+ pub fn sign(&self) -> ICU4XFixedDecimalSign {
+ self.0.sign().into()
+ }
+
+ /// Set the sign of the [`ICU4XFixedDecimal`].
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::set_sign, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::with_sign, FnInStruct, hidden)]
+ pub fn set_sign(&mut self, sign: ICU4XFixedDecimalSign) {
+ self.0.set_sign(sign.into())
+ }
+
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::apply_sign_display, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::with_sign_display, FnInStruct, hidden)]
+ pub fn apply_sign_display(&mut self, sign_display: ICU4XFixedDecimalSignDisplay) {
+ self.0.apply_sign_display(sign_display.into())
+ }
+
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::trim_start, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::trimmed_start, FnInStruct, hidden)]
+ pub fn trim_start(&mut self) {
+ self.0.trim_start()
+ }
+
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::trim_end, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::trimmed_end, FnInStruct, hidden)]
+ pub fn trim_end(&mut self) {
+ self.0.trim_end()
+ }
+
+ /// Zero-pad the [`ICU4XFixedDecimal`] on the left to a particular position
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::pad_start, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::padded_start, FnInStruct, hidden)]
+ pub fn pad_start(&mut self, position: i16) {
+ self.0.pad_start(position)
+ }
+
+ /// Zero-pad the [`ICU4XFixedDecimal`] on the right to a particular position
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::pad_end, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::padded_end, FnInStruct, hidden)]
+ pub fn pad_end(&mut self, position: i16) {
+ self.0.pad_end(position)
+ }
+
+ /// Truncate the [`ICU4XFixedDecimal`] 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::FixedDecimal::set_max_position, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::with_max_position, FnInStruct, hidden)]
+ pub fn set_max_position(&mut self, position: i16) {
+ self.0.set_max_position(position)
+ }
+
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::trunc, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::trunced, FnInStruct, hidden)]
+ pub fn trunc(&mut self, position: i16) {
+ self.0.trunc(position)
+ }
+
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_trunc, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_trunced, FnInStruct, hidden)]
+ pub fn half_trunc(&mut self, position: i16) {
+ self.0.half_trunc(position)
+ }
+
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::expand, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::expanded, FnInStruct, hidden)]
+ pub fn expand(&mut self, position: i16) {
+ self.0.expand(position)
+ }
+
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_expand, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_expanded, FnInStruct, hidden)]
+ pub fn half_expand(&mut self, position: i16) {
+ self.0.half_expand(position)
+ }
+
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::ceil, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::ceiled, FnInStruct, hidden)]
+ pub fn ceil(&mut self, position: i16) {
+ self.0.ceil(position)
+ }
+
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_ceil, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_ceiled, FnInStruct, hidden)]
+ pub fn half_ceil(&mut self, position: i16) {
+ self.0.half_ceil(position)
+ }
+
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::floor, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::floored, FnInStruct, hidden)]
+ pub fn floor(&mut self, position: i16) {
+ self.0.floor(position)
+ }
+
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_floor, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_floored, FnInStruct, hidden)]
+ pub fn half_floor(&mut self, position: i16) {
+ self.0.half_floor(position)
+ }
+
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_even, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::half_evened, FnInStruct, hidden)]
+ pub fn half_even(&mut self, position: i16) {
+ self.0.half_even(position)
+ }
+
+ /// 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::FixedDecimal::concatenate_end, FnInStruct)]
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::concatenated_end, FnInStruct, hidden)]
+ pub fn concatenate_end(&mut self, other: &mut ICU4XFixedDecimal) -> Result<(), ()> {
+ let x = core::mem::take(&mut other.0);
+ self.0.concatenate_end(x).map_err(|y| {
+ other.0 = y;
+ })
+ }
+
+ /// Format the [`ICU4XFixedDecimal`] as a string.
+ #[diplomat::rust_link(fixed_decimal::FixedDecimal::write_to, FnInStruct)]
+ pub fn to_string(&self, to: &mut diplomat_runtime::DiplomatWriteable) {
+ let _ = self.0.write_to(to);
+ }
+ }
+}
+
+impl From<ffi::ICU4XFixedDecimalSign> for Sign {
+ fn from(other: ffi::ICU4XFixedDecimalSign) -> Self {
+ match other {
+ ffi::ICU4XFixedDecimalSign::None => Self::None,
+ ffi::ICU4XFixedDecimalSign::Negative => Self::Negative,
+ ffi::ICU4XFixedDecimalSign::Positive => Self::Positive,
+ }
+ }
+}
+
+impl From<Sign> for ffi::ICU4XFixedDecimalSign {
+ fn from(other: Sign) -> Self {
+ match other {
+ Sign::None => Self::None,
+ Sign::Negative => Self::Negative,
+ Sign::Positive => Self::Positive,
+ }
+ }
+}
+
+impl From<ffi::ICU4XFixedDecimalSignDisplay> for SignDisplay {
+ fn from(other: ffi::ICU4XFixedDecimalSignDisplay) -> Self {
+ match other {
+ ffi::ICU4XFixedDecimalSignDisplay::Auto => Self::Auto,
+ ffi::ICU4XFixedDecimalSignDisplay::Never => Self::Never,
+ ffi::ICU4XFixedDecimalSignDisplay::Always => Self::Always,
+ ffi::ICU4XFixedDecimalSignDisplay::ExceptZero => Self::ExceptZero,
+ ffi::ICU4XFixedDecimalSignDisplay::Negative => Self::Negative,
+ }
+ }
+}
diff --git a/intl/icu_capi/src/iana_bcp47_mapper.rs b/intl/icu_capi/src/iana_bcp47_mapper.rs
new file mode 100644
index 0000000000..c77730410d
--- /dev/null
+++ b/intl/icu_capi/src/iana_bcp47_mapper.rs
@@ -0,0 +1,82 @@
+// 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]
+pub mod ffi {
+ use crate::errors::ffi::ICU4XError;
+ use crate::provider::ffi::ICU4XDataProvider;
+ use alloc::boxed::Box;
+ use icu_timezone::IanaBcp47RoundTripMapper;
+ use icu_timezone::IanaToBcp47Mapper;
+ use icu_timezone::TimeZoneBcp47Id;
+
+ /// An object capable of mapping from an IANA time zone ID to a BCP-47 ID.
+ ///
+ /// This can be used via `try_set_iana_time_zone_id()` on [`ICU4XCustomTimeZone`].
+ ///
+ /// [`ICU4XCustomTimeZone`]: crate::timezone::ffi::ICU4XCustomTimeZone
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::timezone::IanaToBcp47Mapper, Struct)]
+ #[diplomat::rust_link(icu::timezone::IanaToBcp47Mapper::as_borrowed, FnInStruct, hidden)]
+ pub struct ICU4XIanaToBcp47Mapper(pub IanaToBcp47Mapper);
+
+ impl ICU4XIanaToBcp47Mapper {
+ #[diplomat::rust_link(icu::timezone::IanaToBcp47Mapper::new, FnInStruct)]
+ pub fn create(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XIanaToBcp47Mapper>, ICU4XError> {
+ Ok(Box::new(ICU4XIanaToBcp47Mapper(call_constructor!(
+ IanaToBcp47Mapper::new [r => Ok(r)],
+ IanaToBcp47Mapper::try_new_with_any_provider,
+ IanaToBcp47Mapper::try_new_with_buffer_provider,
+ provider,
+ )?)))
+ }
+ }
+
+ /// An object capable of mapping from a BCP-47 time zone ID to an IANA ID.
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::timezone::IanaBcp47RoundTripMapper, Struct)]
+ #[diplomat::rust_link(
+ icu::timezone::IanaBcp47RoundTripMapper::as_borrowed,
+ FnInStruct,
+ hidden
+ )]
+ pub struct ICU4XBcp47ToIanaMapper(pub IanaBcp47RoundTripMapper);
+
+ impl ICU4XBcp47ToIanaMapper {
+ #[diplomat::rust_link(icu::timezone::IanaBcp47RoundTripMapper::new, FnInStruct)]
+ pub fn create(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XBcp47ToIanaMapper>, ICU4XError> {
+ Ok(Box::new(ICU4XBcp47ToIanaMapper(call_constructor!(
+ IanaBcp47RoundTripMapper::new [r => Ok(r)],
+ IanaBcp47RoundTripMapper::try_new_with_any_provider,
+ IanaBcp47RoundTripMapper::try_new_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Writes out the canonical IANA time zone ID corresponding to the given BCP-47 ID.
+ #[diplomat::rust_link(
+ icu::datetime::time_zone::IanaBcp47RoundTripMapper::bcp47_to_iana,
+ FnInStruct
+ )]
+ pub fn get(
+ &self,
+ value: &str,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ use core::str::FromStr;
+ use writeable::Writeable;
+ let handle = self.0.as_borrowed();
+ TimeZoneBcp47Id::from_str(value)
+ .ok()
+ .and_then(|bcp47_id| handle.bcp47_to_iana(bcp47_id))
+ .ok_or(ICU4XError::TimeZoneInvalidIdError)?
+ .write_to(write)?;
+ Ok(())
+ }
+ }
+}
diff --git a/intl/icu_capi/src/lib.rs b/intl/icu_capi/src/lib.rs
new file mode 100644
index 0000000000..74060998bc
--- /dev/null
+++ b/intl/icu_capi/src/lib.rs
@@ -0,0 +1,145 @@
+// 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 ).
+
+// https://github.com/unicode-org/icu4x/blob/main/docs/process/boilerplate.md#library-annotations
+#![no_std]
+#![cfg_attr(
+ not(test),
+ deny(
+ clippy::indexing_slicing,
+ clippy::unwrap_used,
+ clippy::expect_used,
+ clippy::panic,
+ // Exhaustiveness and Debug is not required for Diplomat types
+ )
+)]
+#![allow(clippy::upper_case_acronyms)]
+#![allow(clippy::needless_lifetimes)]
+#![allow(clippy::result_unit_err)]
+
+//! This crate contains the source of truth for the [Diplomat](https://github.com/rust-diplomat/diplomat)-generated
+//! FFI bindings. This generates the C, C++, JavaScript, and TypeScript bindings. This crate also contains the `extern "C"`
+//! FFI for ICU4X.
+//!
+//! While the types in this crate are public, APIs from this crate are *not intended to be used from Rust*
+//! and as such this crate may unpredictably change its Rust API across compatible semver versions. The `extern "C"` APIs exposed
+//! by this crate, while not directly documented, are stable within the same major semver version, as are the bindings exposed under
+//! the `cpp/` and `js/` folders.
+//!
+//! This crate may still be explored for documentation on docs.rs, and there are generated language-specific docs available as well.
+//! C++ has sphinx docs in `cpp/docs/`, and the header files also contain documentation comments. The JS version has sphinx docs under
+//! `js/docs`, and the TypeScript sources in `js/include` are compatible with `tsdoc`.
+//!
+//! This crate is `no_std` and will not typically build as a staticlib on its own. If you wish to link to it you should prefer
+//! using `icu_capi_staticlib`, or for more esoteric platforms you may write a shim crate depending on this crate that hooks in
+//! an allocator and panic hook.
+//!
+//! More information on using ICU4X from C++ can be found in [our tutorial].
+//!
+//! [our tutorial]: https://github.com/unicode-org/icu4x/blob/main/docs/tutorials/cpp.md
+// Renamed so you can't accidentally use it
+#[cfg(target_arch = "wasm32")]
+extern crate std as rust_std;
+
+extern crate alloc;
+
+// Common modules
+
+pub mod common;
+pub mod data_struct;
+pub mod errors;
+pub mod locale;
+#[cfg(feature = "logging")]
+pub mod logging;
+#[macro_use]
+pub mod provider;
+
+// Components
+
+#[cfg(feature = "icu_properties")]
+pub mod bidi;
+#[cfg(any(
+ feature = "icu_datetime",
+ feature = "icu_timezone",
+ feature = "icu_calendar"
+))]
+pub mod calendar;
+#[cfg(feature = "icu_casemap")]
+pub mod casemap;
+#[cfg(feature = "icu_collator")]
+pub mod collator;
+#[cfg(feature = "icu_properties")]
+pub mod collections_sets;
+#[cfg(any(
+ feature = "icu_datetime",
+ feature = "icu_timezone",
+ feature = "icu_calendar"
+))]
+pub mod date;
+#[cfg(any(
+ feature = "icu_datetime",
+ feature = "icu_timezone",
+ feature = "icu_calendar"
+))]
+pub mod datetime;
+#[cfg(feature = "icu_datetime")]
+pub mod datetime_formatter;
+#[cfg(feature = "icu_decimal")]
+pub mod decimal;
+#[cfg(feature = "icu_displaynames")]
+pub mod displaynames;
+#[cfg(feature = "icu_locid_transform")]
+pub mod fallbacker;
+#[cfg(feature = "icu_decimal")]
+pub mod fixed_decimal;
+#[cfg(any(feature = "icu_datetime", feature = "icu_timezone"))]
+pub mod iana_bcp47_mapper;
+#[cfg(feature = "icu_list")]
+pub mod list;
+#[cfg(feature = "icu_locid_transform")]
+pub mod locale_directionality;
+#[cfg(feature = "icu_locid_transform")]
+pub mod locid_transform;
+#[cfg(feature = "icu_timezone")]
+pub mod metazone_calculator;
+#[cfg(feature = "icu_normalizer")]
+pub mod normalizer;
+#[cfg(feature = "icu_normalizer")]
+pub mod normalizer_properties;
+#[cfg(feature = "icu_plurals")]
+pub mod pluralrules;
+#[cfg(feature = "icu_properties")]
+pub mod properties_iter;
+#[cfg(feature = "icu_properties")]
+pub mod properties_maps;
+#[cfg(feature = "icu_properties")]
+pub mod properties_names;
+#[cfg(feature = "icu_properties")]
+pub mod properties_sets;
+#[cfg(feature = "icu_properties")]
+pub mod properties_unisets;
+#[cfg(feature = "icu_properties")]
+pub mod script;
+#[cfg(feature = "icu_segmenter")]
+pub mod segmenter_grapheme;
+#[cfg(feature = "icu_segmenter")]
+pub mod segmenter_line;
+#[cfg(feature = "icu_segmenter")]
+pub mod segmenter_sentence;
+#[cfg(feature = "icu_segmenter")]
+pub mod segmenter_word;
+#[cfg(any(
+ feature = "icu_datetime",
+ feature = "icu_timezone",
+ feature = "icu_calendar"
+))]
+pub mod time;
+#[cfg(any(feature = "icu_datetime", feature = "icu_timezone"))]
+pub mod timezone;
+#[cfg(feature = "icu_datetime")]
+pub mod timezone_formatter;
+#[cfg(feature = "icu_calendar")]
+pub mod week;
+#[cfg(feature = "icu_datetime")]
+pub mod zoned_formatter;
diff --git a/intl/icu_capi/src/list.rs b/intl/icu_capi/src/list.rs
new file mode 100644
index 0000000000..278b12ffb5
--- /dev/null
+++ b/intl/icu_capi/src/list.rs
@@ -0,0 +1,125 @@
+// 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]
+pub mod ffi {
+ use crate::{
+ errors::ffi::ICU4XError, locale::ffi::ICU4XLocale, provider::ffi::ICU4XDataProvider,
+ };
+ use alloc::boxed::Box;
+ use alloc::string::String;
+ use alloc::vec::Vec;
+ use diplomat_runtime::DiplomatWriteable;
+ use icu_list::{ListFormatter, ListLength};
+ use writeable::Writeable;
+
+ /// A list of strings
+ #[diplomat::opaque]
+ pub struct ICU4XList(pub Vec<String>);
+
+ impl ICU4XList {
+ /// Create a new list of strings
+ pub fn create() -> Box<ICU4XList> {
+ Box::new(ICU4XList(Vec::new()))
+ }
+
+ /// Create a new list of strings with preallocated space to hold
+ /// at least `capacity` elements
+ pub fn create_with_capacity(capacity: usize) -> Box<ICU4XList> {
+ Box::new(ICU4XList(Vec::with_capacity(capacity)))
+ }
+
+ /// Push a string to the list
+ ///
+ /// For C++ users, potentially invalid UTF8 will be handled via
+ /// REPLACEMENT CHARACTERs
+ pub fn push(&mut self, val: &str) {
+ let val = val.as_bytes(); // #2520
+ self.0.push(String::from_utf8_lossy(val).into_owned());
+ }
+
+ /// The number of elements in this list
+ #[allow(clippy::len_without_is_empty)] // don't need to follow Rust conventions over FFI
+ #[diplomat::attr(dart, rename = "length")]
+ pub fn len(&self) -> usize {
+ self.0.len()
+ }
+ }
+
+ #[diplomat::rust_link(icu::list::ListLength, Enum)]
+ #[diplomat::enum_convert(ListLength, needs_wildcard)]
+ pub enum ICU4XListLength {
+ Wide,
+ Short,
+ Narrow,
+ }
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::list::ListFormatter, Struct)]
+ pub struct ICU4XListFormatter(pub ListFormatter);
+
+ impl ICU4XListFormatter {
+ /// Construct a new ICU4XListFormatter instance for And patterns
+ #[diplomat::rust_link(icu::list::ListFormatter::try_new_and_with_length, FnInStruct)]
+ pub fn create_and_with_length(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ length: ICU4XListLength,
+ ) -> Result<Box<ICU4XListFormatter>, ICU4XError> {
+ let locale = locale.to_datalocale();
+ Ok(Box::new(ICU4XListFormatter(call_constructor!(
+ ListFormatter::try_new_and_with_length,
+ ListFormatter::try_new_and_with_length_with_any_provider,
+ ListFormatter::try_new_and_with_length_with_buffer_provider,
+ provider,
+ &locale,
+ length.into()
+ )?)))
+ }
+ /// Construct a new ICU4XListFormatter instance for And patterns
+ #[diplomat::rust_link(icu::list::ListFormatter::try_new_or_with_length, FnInStruct)]
+ pub fn create_or_with_length(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ length: ICU4XListLength,
+ ) -> Result<Box<ICU4XListFormatter>, ICU4XError> {
+ let locale = locale.to_datalocale();
+ Ok(Box::new(ICU4XListFormatter(call_constructor!(
+ ListFormatter::try_new_or_with_length,
+ ListFormatter::try_new_or_with_length_with_any_provider,
+ ListFormatter::try_new_or_with_length_with_buffer_provider,
+ provider,
+ &locale,
+ length.into()
+ )?)))
+ }
+ /// Construct a new ICU4XListFormatter instance for And patterns
+ #[diplomat::rust_link(icu::list::ListFormatter::try_new_unit_with_length, FnInStruct)]
+ pub fn create_unit_with_length(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ length: ICU4XListLength,
+ ) -> Result<Box<ICU4XListFormatter>, ICU4XError> {
+ let locale = locale.to_datalocale();
+ Ok(Box::new(ICU4XListFormatter(call_constructor!(
+ ListFormatter::try_new_unit_with_length,
+ ListFormatter::try_new_unit_with_length_with_any_provider,
+ ListFormatter::try_new_unit_with_length_with_buffer_provider,
+ provider,
+ &locale,
+ length.into()
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::list::ListFormatter::format, FnInStruct)]
+ #[diplomat::rust_link(icu::list::ListFormatter::format_to_string, FnInStruct, hidden)]
+ pub fn format(
+ &self,
+ list: &ICU4XList,
+ write: &mut DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ self.0.format(list.0.iter()).write_to(write)?;
+ Ok(())
+ }
+ }
+}
diff --git a/intl/icu_capi/src/locale.rs b/intl/icu_capi/src/locale.rs
new file mode 100644
index 0000000000..07089cae65
--- /dev/null
+++ b/intl/icu_capi/src/locale.rs
@@ -0,0 +1,212 @@
+// 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]
+pub mod ffi {
+ use crate::errors::ffi::ICU4XError;
+ use alloc::boxed::Box;
+ use core::str;
+ use diplomat_runtime::DiplomatWriteable;
+ use icu_locid::extensions::unicode::Key;
+ use icu_locid::subtags::{Language, Region, Script};
+ use icu_locid::Locale;
+ use writeable::Writeable;
+
+ use crate::common::ffi::ICU4XOrdering;
+
+ #[diplomat::opaque]
+ /// An ICU4X Locale, capable of representing strings like `"en-US"`.
+ #[diplomat::rust_link(icu::locid::Locale, Struct)]
+ pub struct ICU4XLocale(pub Locale);
+
+ impl ICU4XLocale {
+ /// Construct an [`ICU4XLocale`] from an locale identifier.
+ ///
+ /// This will run the complete locale parsing algorithm. If code size and
+ /// performance are critical and the locale is of a known shape (such as
+ /// `aa-BB`) use `create_und`, `set_language`, `set_script`, and `set_region`.
+ #[diplomat::rust_link(icu::locid::Locale::try_from_bytes, FnInStruct)]
+ #[diplomat::rust_link(icu::locid::Locale::from_str, FnInStruct, hidden)]
+ pub fn create_from_string(name: &str) -> Result<Box<ICU4XLocale>, ICU4XError> {
+ let name = name.as_bytes(); // #2520
+ Ok(Box::new(ICU4XLocale(Locale::try_from_bytes(name)?)))
+ }
+
+ /// Construct a default undefined [`ICU4XLocale`] "und".
+ #[diplomat::rust_link(icu::locid::Locale::UND, AssociatedConstantInStruct)]
+ pub fn create_und() -> Box<ICU4XLocale> {
+ Box::new(ICU4XLocale(Locale::UND))
+ }
+
+ /// Clones the [`ICU4XLocale`].
+ #[diplomat::rust_link(icu::locid::Locale, Struct)]
+ #[allow(clippy::should_implement_trait)]
+ pub fn clone(&self) -> Box<ICU4XLocale> {
+ Box::new(ICU4XLocale(self.0.clone()))
+ }
+
+ /// Write a string representation of the `LanguageIdentifier` part of
+ /// [`ICU4XLocale`] to `write`.
+ #[diplomat::rust_link(icu::locid::Locale::id, StructField)]
+ pub fn basename(
+ &self,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ self.0.id.write_to(write)?;
+ Ok(())
+ }
+
+ /// Write a string representation of the unicode extension to `write`
+ #[diplomat::rust_link(icu::locid::Locale::extensions, StructField)]
+ pub fn get_unicode_extension(
+ &self,
+ bytes: &str,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ let bytes = bytes.as_bytes(); // #2520
+ self.0
+ .extensions
+ .unicode
+ .keywords
+ .get(&Key::try_from_bytes(bytes)?)
+ .ok_or(ICU4XError::LocaleUndefinedSubtagError)?
+ .write_to(write)?;
+ Ok(())
+ }
+
+ /// Write a string representation of [`ICU4XLocale`] language to `write`
+ #[diplomat::rust_link(icu::locid::Locale::id, StructField)]
+ pub fn language(
+ &self,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ self.0.id.language.write_to(write)?;
+ Ok(())
+ }
+
+ /// Set the language part of the [`ICU4XLocale`].
+ #[diplomat::rust_link(icu::locid::Locale::try_from_bytes, FnInStruct)]
+ pub fn set_language(&mut self, bytes: &str) -> Result<(), ICU4XError> {
+ let bytes = bytes.as_bytes(); // #2520
+ self.0.id.language = if bytes.is_empty() {
+ Language::UND
+ } else {
+ Language::try_from_bytes(bytes)?
+ };
+ Ok(())
+ }
+
+ /// Write a string representation of [`ICU4XLocale`] region to `write`
+ #[diplomat::rust_link(icu::locid::Locale::id, StructField)]
+ pub fn region(
+ &self,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ if let Some(region) = self.0.id.region {
+ region.write_to(write)?;
+ Ok(())
+ } else {
+ Err(ICU4XError::LocaleUndefinedSubtagError)
+ }
+ }
+
+ /// Set the region part of the [`ICU4XLocale`].
+ #[diplomat::rust_link(icu::locid::Locale::try_from_bytes, FnInStruct)]
+ pub fn set_region(&mut self, bytes: &str) -> Result<(), ICU4XError> {
+ let bytes = bytes.as_bytes(); // #2520
+ self.0.id.region = if bytes.is_empty() {
+ None
+ } else {
+ Some(Region::try_from_bytes(bytes)?)
+ };
+ Ok(())
+ }
+
+ /// Write a string representation of [`ICU4XLocale`] script to `write`
+ #[diplomat::rust_link(icu::locid::Locale::id, StructField)]
+ pub fn script(
+ &self,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ if let Some(script) = self.0.id.script {
+ script.write_to(write)?;
+ Ok(())
+ } else {
+ Err(ICU4XError::LocaleUndefinedSubtagError)
+ }
+ }
+
+ /// Set the script part of the [`ICU4XLocale`]. Pass an empty string to remove the script.
+ #[diplomat::rust_link(icu::locid::Locale::try_from_bytes, FnInStruct)]
+ pub fn set_script(&mut self, bytes: &str) -> Result<(), ICU4XError> {
+ let bytes = bytes.as_bytes(); // #2520
+ self.0.id.script = if bytes.is_empty() {
+ None
+ } else {
+ Some(Script::try_from_bytes(bytes)?)
+ };
+ Ok(())
+ }
+
+ /// Best effort locale canonicalizer that doesn't need any data
+ ///
+ /// Use ICU4XLocaleCanonicalizer for better control and functionality
+ #[diplomat::rust_link(icu::locid::Locale::canonicalize, FnInStruct)]
+ pub fn canonicalize(bytes: &str, write: &mut DiplomatWriteable) -> Result<(), ICU4XError> {
+ let bytes = bytes.as_bytes(); // #2520
+ Locale::canonicalize(bytes)?.write_to(write)?;
+ Ok(())
+ }
+ /// Write a string representation of [`ICU4XLocale`] to `write`
+ #[diplomat::rust_link(icu::locid::Locale::write_to, FnInStruct)]
+ pub fn to_string(
+ &self,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ self.0.write_to(write)?;
+ Ok(())
+ }
+
+ #[diplomat::rust_link(icu::locid::Locale::normalizing_eq, FnInStruct)]
+ pub fn normalizing_eq(&self, other: &str) -> bool {
+ let other = other.as_bytes(); // #2520
+ if let Ok(other) = str::from_utf8(other) {
+ self.0.normalizing_eq(other)
+ } else {
+ // invalid UTF8 won't be allowed in locales anyway
+ false
+ }
+ }
+
+ #[diplomat::rust_link(icu::locid::Locale::strict_cmp, FnInStruct)]
+ pub fn strict_cmp(&self, other: &str) -> ICU4XOrdering {
+ let other = other.as_bytes(); // #2520
+ self.0.strict_cmp(other).into()
+ }
+
+ /// Deprecated
+ ///
+ /// Use `create_from_string("en").
+ #[cfg(feature = "provider_test")]
+ #[diplomat::attr(dart, disable)]
+ pub fn create_en() -> Box<ICU4XLocale> {
+ Box::new(ICU4XLocale(icu_locid::locale!("en")))
+ }
+
+ /// Deprecated
+ ///
+ /// Use `create_from_string("bn").
+ #[cfg(feature = "provider_test")]
+ #[diplomat::attr(dart, disable)]
+ pub fn create_bn() -> Box<ICU4XLocale> {
+ Box::new(ICU4XLocale(icu_locid::locale!("bn")))
+ }
+ }
+}
+
+impl ffi::ICU4XLocale {
+ pub fn to_datalocale(&self) -> icu_provider::DataLocale {
+ (&self.0).into()
+ }
+}
diff --git a/intl/icu_capi/src/locale_directionality.rs b/intl/icu_capi/src/locale_directionality.rs
new file mode 100644
index 0000000000..2ec83caff2
--- /dev/null
+++ b/intl/icu_capi/src/locale_directionality.rs
@@ -0,0 +1,101 @@
+// 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]
+pub mod ffi {
+ use crate::{
+ errors::ffi::ICU4XError,
+ locale::ffi::ICU4XLocale,
+ locid_transform::ffi::ICU4XLocaleExpander,
+ provider::{ffi::ICU4XDataProvider, ICU4XDataProviderInner},
+ };
+ use alloc::boxed::Box;
+ use icu_locid_transform::{Direction, LocaleDirectionality};
+
+ #[diplomat::rust_link(icu::locid_transform::Direction, Enum)]
+ pub enum ICU4XLocaleDirection {
+ LeftToRight,
+ RightToLeft,
+ Unknown,
+ }
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::locid_transform::LocaleDirectionality, Struct)]
+ pub struct ICU4XLocaleDirectionality(pub LocaleDirectionality);
+
+ impl ICU4XLocaleDirectionality {
+ /// Construct a new ICU4XLocaleDirectionality instance
+ #[diplomat::rust_link(icu::locid_transform::LocaleDirectionality::new, FnInStruct)]
+ pub fn create(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XLocaleDirectionality>, ICU4XError> {
+ Ok(Box::new(ICU4XLocaleDirectionality(call_constructor!(
+ LocaleDirectionality::new [r => Ok(r)],
+ LocaleDirectionality::try_new_with_any_provider,
+ LocaleDirectionality::try_new_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Construct a new ICU4XLocaleDirectionality instance with a custom expander
+ #[diplomat::rust_link(
+ icu::locid_transform::LocaleDirectionality::new_with_expander,
+ FnInStruct
+ )]
+ pub fn create_with_expander(
+ provider: &ICU4XDataProvider,
+ expander: &ICU4XLocaleExpander,
+ ) -> Result<Box<ICU4XLocaleDirectionality>, ICU4XError> {
+ #[allow(unused_imports)]
+ use icu_provider::prelude::*;
+ Ok(Box::new(ICU4XLocaleDirectionality(match &provider.0 {
+ ICU4XDataProviderInner::Destroyed => Err(icu_provider::DataError::custom(
+ "This provider has been destroyed",
+ ))?,
+ ICU4XDataProviderInner::Empty => {
+ LocaleDirectionality::try_new_with_expander_unstable(
+ &icu_provider_adapters::empty::EmptyDataProvider::new(),
+ expander.0.clone(),
+ )?
+ }
+ #[cfg(feature = "buffer_provider")]
+ ICU4XDataProviderInner::Buffer(buffer_provider) => {
+ LocaleDirectionality::try_new_with_expander_unstable(
+ &buffer_provider.as_deserializing(),
+ expander.0.clone(),
+ )?
+ }
+ #[cfg(feature = "compiled_data")]
+ ICU4XDataProviderInner::Compiled => {
+ LocaleDirectionality::new_with_expander(expander.0.clone())
+ }
+ })))
+ }
+
+ #[diplomat::rust_link(icu::locid_transform::LocaleDirectionality::get, FnInStruct)]
+ pub fn get(&self, locale: &ICU4XLocale) -> ICU4XLocaleDirection {
+ match self.0.get(&locale.0) {
+ Some(Direction::LeftToRight) => ICU4XLocaleDirection::LeftToRight,
+ Some(Direction::RightToLeft) => ICU4XLocaleDirection::RightToLeft,
+ _ => ICU4XLocaleDirection::Unknown,
+ }
+ }
+
+ #[diplomat::rust_link(
+ icu::locid_transform::LocaleDirectionality::is_left_to_right,
+ FnInStruct
+ )]
+ pub fn is_left_to_right(&self, locale: &ICU4XLocale) -> bool {
+ self.0.is_left_to_right(&locale.0)
+ }
+
+ #[diplomat::rust_link(
+ icu::locid_transform::LocaleDirectionality::is_right_to_left,
+ FnInStruct
+ )]
+ pub fn is_right_to_left(&self, locale: &ICU4XLocale) -> bool {
+ self.0.is_right_to_left(&locale.0)
+ }
+ }
+}
diff --git a/intl/icu_capi/src/locid_transform.rs b/intl/icu_capi/src/locid_transform.rs
new file mode 100644
index 0000000000..d42583b072
--- /dev/null
+++ b/intl/icu_capi/src/locid_transform.rs
@@ -0,0 +1,115 @@
+// 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]
+pub mod ffi {
+ use alloc::boxed::Box;
+ use icu_locid_transform::{LocaleCanonicalizer, LocaleExpander, TransformResult};
+
+ use crate::{locale::ffi::ICU4XLocale, provider::ffi::ICU4XDataProvider};
+
+ use crate::errors::ffi::ICU4XError;
+
+ /// FFI version of `TransformResult`.
+ #[diplomat::rust_link(icu::locid_transform::TransformResult, Enum)]
+ #[diplomat::enum_convert(TransformResult)]
+ pub enum ICU4XTransformResult {
+ Modified,
+ Unmodified,
+ }
+
+ /// A locale canonicalizer.
+ #[diplomat::rust_link(icu::locid_transform::LocaleCanonicalizer, Struct)]
+ #[diplomat::opaque]
+ pub struct ICU4XLocaleCanonicalizer(LocaleCanonicalizer);
+
+ impl ICU4XLocaleCanonicalizer {
+ /// Create a new [`ICU4XLocaleCanonicalizer`].
+ #[diplomat::rust_link(icu::locid_transform::LocaleCanonicalizer::new, FnInStruct)]
+ pub fn create(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XLocaleCanonicalizer>, ICU4XError> {
+ Ok(Box::new(ICU4XLocaleCanonicalizer(call_constructor!(
+ LocaleCanonicalizer::new [r => Ok(r)],
+ LocaleCanonicalizer::try_new_with_any_provider,
+ LocaleCanonicalizer::try_new_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Create a new [`ICU4XLocaleCanonicalizer`] with extended data.
+ #[diplomat::rust_link(
+ icu::locid_transform::LocaleCanonicalizer::new_with_expander,
+ FnInStruct
+ )]
+ pub fn create_extended(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XLocaleCanonicalizer>, ICU4XError> {
+ let expander = call_constructor!(
+ LocaleExpander::new_extended [r => Ok(r)],
+ LocaleExpander::try_new_with_any_provider,
+ LocaleExpander::try_new_with_buffer_provider,
+ provider,
+ )?;
+ Ok(Box::new(ICU4XLocaleCanonicalizer(call_constructor!(
+ LocaleCanonicalizer::new_with_expander [r => Ok(r)],
+ LocaleCanonicalizer::try_new_with_expander_with_any_provider,
+ LocaleCanonicalizer::try_new_with_expander_with_buffer_provider,
+ provider,
+ expander
+ )?)))
+ }
+
+ /// FFI version of `LocaleCanonicalizer::canonicalize()`.
+ #[diplomat::rust_link(icu::locid_transform::LocaleCanonicalizer::canonicalize, FnInStruct)]
+ pub fn canonicalize(&self, locale: &mut ICU4XLocale) -> ICU4XTransformResult {
+ self.0.canonicalize(&mut locale.0).into()
+ }
+ }
+
+ /// A locale expander.
+ #[diplomat::rust_link(icu::locid_transform::LocaleExpander, Struct)]
+ #[diplomat::opaque]
+ pub struct ICU4XLocaleExpander(pub LocaleExpander);
+
+ impl ICU4XLocaleExpander {
+ /// Create a new [`ICU4XLocaleExpander`].
+ #[diplomat::rust_link(icu::locid_transform::LocaleExpander::new, FnInStruct)]
+ pub fn create(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XLocaleExpander>, ICU4XError> {
+ Ok(Box::new(ICU4XLocaleExpander(call_constructor!(
+ LocaleExpander::new [r => Ok(r)],
+ LocaleExpander::try_new_with_any_provider,
+ LocaleExpander::try_new_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Create a new [`ICU4XLocaleExpander`] with extended data.
+ #[diplomat::rust_link(icu::locid_transform::LocaleExpander::new_extended, FnInStruct)]
+ pub fn create_extended(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XLocaleExpander>, ICU4XError> {
+ Ok(Box::new(ICU4XLocaleExpander(call_constructor!(
+ LocaleExpander::new_extended [r => Ok(r)],
+ LocaleExpander::try_new_with_any_provider,
+ LocaleExpander::try_new_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// FFI version of `LocaleExpander::maximize()`.
+ #[diplomat::rust_link(icu::locid_transform::LocaleExpander::maximize, FnInStruct)]
+ pub fn maximize(&self, locale: &mut ICU4XLocale) -> ICU4XTransformResult {
+ self.0.maximize(&mut locale.0).into()
+ }
+
+ /// FFI version of `LocaleExpander::minimize()`.
+ #[diplomat::rust_link(icu::locid_transform::LocaleExpander::minimize, FnInStruct)]
+ pub fn minimize(&self, locale: &mut ICU4XLocale) -> ICU4XTransformResult {
+ self.0.minimize(&mut locale.0).into()
+ }
+ }
+}
diff --git a/intl/icu_capi/src/logging.rs b/intl/icu_capi/src/logging.rs
new file mode 100644
index 0000000000..c84205bec5
--- /dev/null
+++ b/intl/icu_capi/src/logging.rs
@@ -0,0 +1,36 @@
+// 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]
+pub mod ffi {
+ use alloc::boxed::Box;
+
+ #[diplomat::opaque]
+ /// An object allowing control over the logging used
+ #[diplomat::attr(dart, disable)]
+ pub struct ICU4XLogger;
+
+ impl ICU4XLogger {
+ /// Initialize the logger using `simple_logger`
+ ///
+ /// Requires the `simple_logger` Cargo feature.
+ ///
+ /// Returns `false` if there was already a logger set.
+ #[cfg(all(not(target_arch = "wasm32"), feature = "simple_logger"))]
+ pub fn init_simple_logger() -> bool {
+ simple_logger::init().is_ok()
+ }
+
+ /// Deprecated: since ICU4X 1.4, this now happens automatically if the `log` feature is enabled.
+ #[cfg(target_arch = "wasm32")]
+ pub fn init_console_logger() -> bool {
+ false
+ }
+ }
+}
+
+// semver?
+#[no_mangle]
+#[cfg(target_arch = "wasm32")]
+pub unsafe extern "C" fn icu4x_init() {}
diff --git a/intl/icu_capi/src/metazone_calculator.rs b/intl/icu_capi/src/metazone_calculator.rs
new file mode 100644
index 0000000000..e55ede26bd
--- /dev/null
+++ b/intl/icu_capi/src/metazone_calculator.rs
@@ -0,0 +1,34 @@
+// 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]
+pub mod ffi {
+ use crate::errors::ffi::ICU4XError;
+ use crate::provider::ffi::ICU4XDataProvider;
+ use alloc::boxed::Box;
+ use icu_timezone::MetazoneCalculator;
+
+ /// An object capable of computing the metazone from a timezone.
+ ///
+ /// This can be used via `maybe_calculate_metazone()` on [`ICU4XCustomTimeZone`].
+ ///
+ /// [`ICU4XCustomTimeZone`]: crate::timezone::ffi::ICU4XCustomTimeZone
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::timezone::MetazoneCalculator, Struct)]
+ pub struct ICU4XMetazoneCalculator(pub MetazoneCalculator);
+
+ impl ICU4XMetazoneCalculator {
+ #[diplomat::rust_link(icu::timezone::MetazoneCalculator::new, FnInStruct)]
+ pub fn create(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XMetazoneCalculator>, ICU4XError> {
+ Ok(Box::new(ICU4XMetazoneCalculator(call_constructor!(
+ MetazoneCalculator::new [r => Ok(r)],
+ MetazoneCalculator::try_new_with_any_provider,
+ MetazoneCalculator::try_new_with_buffer_provider,
+ provider,
+ )?)))
+ }
+ }
+}
diff --git a/intl/icu_capi/src/normalizer.rs b/intl/icu_capi/src/normalizer.rs
new file mode 100644
index 0000000000..13a1cf0292
--- /dev/null
+++ b/intl/icu_capi/src/normalizer.rs
@@ -0,0 +1,152 @@
+// 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]
+pub mod ffi {
+ use crate::{errors::ffi::ICU4XError, provider::ffi::ICU4XDataProvider};
+ use alloc::boxed::Box;
+ use diplomat_runtime::DiplomatWriteable;
+ use icu_normalizer::{ComposingNormalizer, DecomposingNormalizer};
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::normalizer::ComposingNormalizer, Struct)]
+ pub struct ICU4XComposingNormalizer(pub ComposingNormalizer);
+
+ impl ICU4XComposingNormalizer {
+ /// Construct a new ICU4XComposingNormalizer instance for NFC
+ #[diplomat::rust_link(icu::normalizer::ComposingNormalizer::new_nfc, FnInStruct)]
+ pub fn create_nfc(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XComposingNormalizer>, ICU4XError> {
+ Ok(Box::new(ICU4XComposingNormalizer(call_constructor!(
+ ComposingNormalizer::new_nfc [r => Ok(r)],
+ ComposingNormalizer::try_new_nfc_with_any_provider,
+ ComposingNormalizer::try_new_nfc_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Construct a new ICU4XComposingNormalizer instance for NFKC
+ #[diplomat::rust_link(icu::normalizer::ComposingNormalizer::new_nfkc, FnInStruct)]
+ pub fn create_nfkc(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XComposingNormalizer>, ICU4XError> {
+ Ok(Box::new(ICU4XComposingNormalizer(call_constructor!(
+ ComposingNormalizer::new_nfkc [r => Ok(r)],
+ ComposingNormalizer::try_new_nfkc_with_any_provider,
+ ComposingNormalizer::try_new_nfkc_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Normalize a (potentially ill-formed) UTF8 string
+ ///
+ /// Errors are mapped to REPLACEMENT CHARACTER
+ #[diplomat::rust_link(icu::normalizer::ComposingNormalizer::normalize_utf8, FnInStruct)]
+ #[diplomat::rust_link(icu::normalizer::ComposingNormalizer::normalize, FnInStruct, hidden)]
+ #[diplomat::rust_link(
+ icu::normalizer::ComposingNormalizer::normalize_to,
+ FnInStruct,
+ hidden
+ )]
+ #[diplomat::rust_link(
+ icu::normalizer::ComposingNormalizer::normalize_utf8_to,
+ FnInStruct,
+ hidden
+ )]
+ pub fn normalize(&self, s: &str, write: &mut DiplomatWriteable) -> Result<(), ICU4XError> {
+ let s = s.as_bytes(); // #2520
+ self.0.normalize_utf8_to(s, write)?;
+ Ok(())
+ }
+
+ /// Check if a (potentially ill-formed) UTF8 string is normalized
+ ///
+ /// Errors are mapped to REPLACEMENT CHARACTER
+ #[diplomat::rust_link(icu::normalizer::ComposingNormalizer::is_normalized_utf8, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::normalizer::ComposingNormalizer::is_normalized,
+ FnInStruct,
+ hidden
+ )]
+ pub fn is_normalized(&self, s: &str) -> bool {
+ let s = s.as_bytes(); // #2520
+ self.0.is_normalized_utf8(s)
+ }
+ }
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::normalizer::DecomposingNormalizer, Struct)]
+ pub struct ICU4XDecomposingNormalizer(pub DecomposingNormalizer);
+
+ impl ICU4XDecomposingNormalizer {
+ /// Construct a new ICU4XDecomposingNormalizer instance for NFC
+ #[diplomat::rust_link(icu::normalizer::DecomposingNormalizer::new_nfd, FnInStruct)]
+ pub fn create_nfd(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XDecomposingNormalizer>, ICU4XError> {
+ Ok(Box::new(ICU4XDecomposingNormalizer(call_constructor!(
+ DecomposingNormalizer::new_nfd [r => Ok(r)],
+ DecomposingNormalizer::try_new_nfd_with_any_provider,
+ DecomposingNormalizer::try_new_nfd_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Construct a new ICU4XDecomposingNormalizer instance for NFKC
+ #[diplomat::rust_link(icu::normalizer::DecomposingNormalizer::new_nfkd, FnInStruct)]
+ pub fn create_nfkd(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XDecomposingNormalizer>, ICU4XError> {
+ Ok(Box::new(ICU4XDecomposingNormalizer(call_constructor!(
+ DecomposingNormalizer::new_nfkd [r => Ok(r)],
+ DecomposingNormalizer::try_new_nfkd_with_any_provider,
+ DecomposingNormalizer::try_new_nfkd_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Normalize a (potentially ill-formed) UTF8 string
+ ///
+ /// Errors are mapped to REPLACEMENT CHARACTER
+ #[diplomat::rust_link(icu::normalizer::DecomposingNormalizer::normalize_utf8, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::normalizer::DecomposingNormalizer::normalize,
+ FnInStruct,
+ hidden
+ )]
+ #[diplomat::rust_link(
+ icu::normalizer::DecomposingNormalizer::normalize_to,
+ FnInStruct,
+ hidden
+ )]
+ #[diplomat::rust_link(
+ icu::normalizer::DecomposingNormalizer::normalize_utf8_to,
+ FnInStruct,
+ hidden
+ )]
+ pub fn normalize(&self, s: &str, write: &mut DiplomatWriteable) -> Result<(), ICU4XError> {
+ let s = s.as_bytes(); // #2520
+ self.0.normalize_utf8_to(s, write)?;
+ Ok(())
+ }
+
+ /// Check if a (potentially ill-formed) UTF8 string is normalized
+ ///
+ /// Errors are mapped to REPLACEMENT CHARACTER
+ #[diplomat::rust_link(
+ icu::normalizer::DecomposingNormalizer::is_normalized_utf8,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu::normalizer::DecomposingNormalizer::is_normalized,
+ FnInStruct,
+ hidden
+ )]
+ pub fn is_normalized(&self, s: &str) -> bool {
+ let s = s.as_bytes(); // #2520
+ self.0.is_normalized_utf8(s)
+ }
+ }
+}
diff --git a/intl/icu_capi/src/normalizer_properties.rs b/intl/icu_capi/src/normalizer_properties.rs
new file mode 100644
index 0000000000..dbb1dfbdbc
--- /dev/null
+++ b/intl/icu_capi/src/normalizer_properties.rs
@@ -0,0 +1,144 @@
+// 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]
+pub mod ffi {
+ use crate::{errors::ffi::ICU4XError, provider::ffi::ICU4XDataProvider};
+ use alloc::boxed::Box;
+ use icu_normalizer::properties::{
+ CanonicalCombiningClassMap, CanonicalComposition, CanonicalDecomposition, Decomposed,
+ };
+
+ /// Lookup of the Canonical_Combining_Class Unicode property
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::normalizer::properties::CanonicalCombiningClassMap, Struct)]
+ pub struct ICU4XCanonicalCombiningClassMap(pub CanonicalCombiningClassMap);
+
+ impl ICU4XCanonicalCombiningClassMap {
+ /// Construct a new ICU4XCanonicalCombiningClassMap instance for NFC
+ #[diplomat::rust_link(
+ icu::normalizer::properties::CanonicalCombiningClassMap::new,
+ FnInStruct
+ )]
+ pub fn create(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCanonicalCombiningClassMap>, ICU4XError> {
+ Ok(Box::new(ICU4XCanonicalCombiningClassMap(
+ call_constructor!(
+ CanonicalCombiningClassMap::new [r => Ok(r)],
+ CanonicalCombiningClassMap::try_new_with_any_provider,
+ CanonicalCombiningClassMap::try_new_with_buffer_provider,
+ provider
+ )?,
+ )))
+ }
+
+ #[diplomat::rust_link(
+ icu::normalizer::properties::CanonicalCombiningClassMap::get,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu::properties::properties::CanonicalCombiningClass,
+ Struct,
+ compact
+ )]
+ pub fn get(&self, ch: char) -> u8 {
+ self.0.get(ch).0
+ }
+ #[diplomat::rust_link(
+ icu::normalizer::properties::CanonicalCombiningClassMap::get32,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu::properties::properties::CanonicalCombiningClass,
+ Struct,
+ compact
+ )]
+ pub fn get32(&self, ch: u32) -> u8 {
+ self.0.get32(ch).0
+ }
+ }
+
+ /// The raw canonical composition operation.
+ ///
+ /// Callers should generally use ICU4XComposingNormalizer unless they specifically need raw composition operations
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::normalizer::properties::CanonicalComposition, Struct)]
+ pub struct ICU4XCanonicalComposition(pub CanonicalComposition);
+
+ impl ICU4XCanonicalComposition {
+ /// Construct a new ICU4XCanonicalComposition instance for NFC
+ #[diplomat::rust_link(icu::normalizer::properties::CanonicalComposition::new, FnInStruct)]
+ pub fn create(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCanonicalComposition>, ICU4XError> {
+ Ok(Box::new(ICU4XCanonicalComposition(call_constructor!(
+ CanonicalComposition::new [r => Ok(r)],
+ CanonicalComposition::try_new_with_any_provider,
+ CanonicalComposition::try_new_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Performs canonical composition (including Hangul) on a pair of characters
+ /// or returns NUL if these characters don’t compose. Composition exclusions are taken into account.
+ #[diplomat::rust_link(
+ icu::normalizer::properties::CanonicalComposition::compose,
+ FnInStruct
+ )]
+ pub fn compose(&self, starter: char, second: char) -> char {
+ self.0.compose(starter, second).unwrap_or('\0')
+ }
+ }
+
+ /// The outcome of non-recursive canonical decomposition of a character.
+ /// `second` will be NUL when the decomposition expands to a single character
+ /// (which may or may not be the original one)
+ #[diplomat::rust_link(icu::normalizer::properties::Decomposed, Enum)]
+ pub struct ICU4XDecomposed {
+ first: char,
+ second: char,
+ }
+
+ /// The raw (non-recursive) canonical decomposition operation.
+ ///
+ /// Callers should generally use ICU4XDecomposingNormalizer unless they specifically need raw composition operations
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::normalizer::properties::CanonicalDecomposition, Struct)]
+ pub struct ICU4XCanonicalDecomposition(pub CanonicalDecomposition);
+
+ impl ICU4XCanonicalDecomposition {
+ /// Construct a new ICU4XCanonicalDecomposition instance for NFC
+ #[diplomat::rust_link(icu::normalizer::properties::CanonicalDecomposition::new, FnInStruct)]
+ pub fn create(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCanonicalDecomposition>, ICU4XError> {
+ Ok(Box::new(ICU4XCanonicalDecomposition(call_constructor!(
+ CanonicalDecomposition::new [r => Ok(r)],
+ CanonicalDecomposition::try_new_with_any_provider,
+ CanonicalDecomposition::try_new_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Performs non-recursive canonical decomposition (including for Hangul).
+ #[diplomat::rust_link(
+ icu::normalizer::properties::CanonicalDecomposition::decompose,
+ FnInStruct
+ )]
+ pub fn decompose(&self, c: char) -> ICU4XDecomposed {
+ match self.0.decompose(c) {
+ Decomposed::Default => ICU4XDecomposed {
+ first: c,
+ second: '\0',
+ },
+ Decomposed::Singleton(s) => ICU4XDecomposed {
+ first: s,
+ second: '\0',
+ },
+ Decomposed::Expansion(first, second) => ICU4XDecomposed { first, second },
+ }
+ }
+ }
+}
diff --git a/intl/icu_capi/src/pluralrules.rs b/intl/icu_capi/src/pluralrules.rs
new file mode 100644
index 0000000000..78720f536e
--- /dev/null
+++ b/intl/icu_capi/src/pluralrules.rs
@@ -0,0 +1,150 @@
+// 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]
+pub mod ffi {
+ use core::str::{self};
+
+ use alloc::boxed::Box;
+
+ use fixed_decimal::FixedDecimal;
+ use icu_plurals::{PluralCategory, PluralOperands, PluralRules};
+
+ use crate::{locale::ffi::ICU4XLocale, provider::ffi::ICU4XDataProvider};
+
+ use crate::errors::ffi::ICU4XError;
+
+ /// FFI version of `PluralCategory`.
+ #[diplomat::rust_link(icu::plurals::PluralCategory, Enum)]
+ #[diplomat::enum_convert(PluralCategory)]
+ pub enum ICU4XPluralCategory {
+ Zero,
+ One,
+ Two,
+ Few,
+ Many,
+ Other,
+ }
+
+ impl ICU4XPluralCategory {
+ /// Construct from a string in the format
+ /// [specified in TR35](https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules)
+ #[diplomat::rust_link(icu::plurals::PluralCategory::get_for_cldr_string, FnInEnum)]
+ #[diplomat::rust_link(icu::plurals::PluralCategory::get_for_cldr_bytes, FnInEnum)]
+ pub fn get_for_cldr_string(s: &str) -> Result<ICU4XPluralCategory, ()> {
+ let s = s.as_bytes(); // #2520
+ PluralCategory::get_for_cldr_bytes(s)
+ .ok_or(())
+ .map(Into::into)
+ }
+ }
+
+ /// FFI version of `PluralRules`.
+ #[diplomat::rust_link(icu::plurals::PluralRules, Struct)]
+ #[diplomat::opaque]
+ pub struct ICU4XPluralRules(PluralRules);
+
+ impl ICU4XPluralRules {
+ /// Construct an [`ICU4XPluralRules`] for the given locale, for cardinal numbers
+ #[diplomat::rust_link(icu::plurals::PluralRules::try_new_cardinal, FnInStruct)]
+ #[diplomat::rust_link(icu::plurals::PluralRules::try_new, FnInStruct, hidden)]
+ #[diplomat::rust_link(icu::plurals::PluralRuleType, Enum, hidden)]
+ pub fn create_cardinal(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ ) -> Result<Box<ICU4XPluralRules>, ICU4XError> {
+ let locale = locale.to_datalocale();
+ Ok(Box::new(ICU4XPluralRules(call_constructor!(
+ PluralRules::try_new_cardinal,
+ PluralRules::try_new_cardinal_with_any_provider,
+ PluralRules::try_new_cardinal_with_buffer_provider,
+ provider,
+ &locale
+ )?)))
+ }
+
+ /// Construct an [`ICU4XPluralRules`] for the given locale, for ordinal numbers
+ #[diplomat::rust_link(icu::plurals::PluralRules::try_new_ordinal, FnInStruct)]
+ #[diplomat::rust_link(icu::plurals::PluralRules::try_new, FnInStruct, hidden)]
+ #[diplomat::rust_link(icu::plurals::PluralRuleType, Enum, hidden)]
+ pub fn create_ordinal(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ ) -> Result<Box<ICU4XPluralRules>, ICU4XError> {
+ let locale = locale.to_datalocale();
+ Ok(Box::new(ICU4XPluralRules(call_constructor!(
+ PluralRules::try_new_ordinal,
+ PluralRules::try_new_ordinal_with_any_provider,
+ PluralRules::try_new_ordinal_with_buffer_provider,
+ provider,
+ &locale
+ )?)))
+ }
+
+ /// Get the category for a given number represented as operands
+ #[diplomat::rust_link(icu::plurals::PluralRules::category_for, FnInStruct)]
+ pub fn category_for(&self, op: &ICU4XPluralOperands) -> ICU4XPluralCategory {
+ self.0.category_for(op.0).into()
+ }
+
+ /// Get all of the categories needed in the current locale
+ #[diplomat::rust_link(icu::plurals::PluralRules::categories, FnInStruct)]
+ pub fn categories(&self) -> ICU4XPluralCategories {
+ ICU4XPluralCategories::from_iter(self.0.categories())
+ }
+ }
+
+ /// FFI version of `PluralOperands`.
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::plurals::PluralOperands, Struct)]
+ pub struct ICU4XPluralOperands(pub icu_plurals::PluralOperands);
+
+ impl ICU4XPluralOperands {
+ /// Construct for a given string representing a number
+ #[diplomat::rust_link(icu::plurals::PluralOperands::from_str, FnInStruct)]
+ pub fn create_from_string(s: &str) -> Result<Box<ICU4XPluralOperands>, ICU4XError> {
+ let s = s.as_bytes(); // #2520
+ Ok(Box::new(ICU4XPluralOperands(PluralOperands::from(
+ // XXX should this have its own errors?
+ &FixedDecimal::try_from(s).map_err(|_| ICU4XError::PluralsParserError)?,
+ ))))
+ }
+ }
+
+ /// FFI version of `PluralRules::categories()` data.
+ pub struct ICU4XPluralCategories {
+ pub zero: bool,
+ pub one: bool,
+ pub two: bool,
+ pub few: bool,
+ pub many: bool,
+ pub other: bool,
+ }
+
+ impl ICU4XPluralCategories {
+ fn from_iter(i: impl Iterator<Item = PluralCategory>) -> Self {
+ i.fold(
+ ICU4XPluralCategories {
+ zero: false,
+ one: false,
+ two: false,
+ few: false,
+ many: false,
+ other: false,
+ },
+ |mut categories, category| {
+ match category {
+ PluralCategory::Zero => categories.zero = true,
+ PluralCategory::One => categories.one = true,
+ PluralCategory::Two => categories.two = true,
+ PluralCategory::Few => categories.few = true,
+ PluralCategory::Many => categories.many = true,
+ PluralCategory::Other => categories.other = true,
+ };
+ categories
+ },
+ )
+ }
+ }
+}
diff --git a/intl/icu_capi/src/properties_iter.rs b/intl/icu_capi/src/properties_iter.rs
new file mode 100644
index 0000000000..e0095789f5
--- /dev/null
+++ b/intl/icu_capi/src/properties_iter.rs
@@ -0,0 +1,48 @@
+// 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]
+pub mod ffi {
+ use alloc::boxed::Box;
+ use core::ops::RangeInclusive;
+
+ /// Result of a single iteration of [`CodePointRangeIterator`].
+ /// Logically can be considered to be an `Option<RangeInclusive<u32>>`,
+ ///
+ /// `start` and `end` represent an inclusive range of code points [start, end],
+ /// and `done` will be true if the iterator has already finished. The last contentful
+ /// iteration will NOT produce a range done=true, in other words `start` and `end` are useful
+ /// values if and only if `done=false`.
+ pub struct CodePointRangeIteratorResult {
+ pub start: u32,
+ pub end: u32,
+ pub done: bool,
+ }
+
+ /// An iterator over code point ranges, produced by `ICU4XCodePointSetData` or
+ /// one of the `ICU4XCodePointMapData` types
+ #[diplomat::opaque]
+ pub struct CodePointRangeIterator<'a>(pub Box<dyn Iterator<Item = RangeInclusive<u32>> + 'a>);
+
+ impl<'a> CodePointRangeIterator<'a> {
+ /// Advance the iterator by one and return the next range.
+ ///
+ /// If the iterator is out of items, `done` will be true
+ #[allow(clippy::should_implement_trait)] // Rust isn't calling this code
+ pub fn next(&mut self) -> CodePointRangeIteratorResult {
+ self.0
+ .next()
+ .map(|r| CodePointRangeIteratorResult {
+ start: *r.start(),
+ end: *r.end(),
+ done: false,
+ })
+ .unwrap_or(CodePointRangeIteratorResult {
+ start: 0,
+ end: 0,
+ done: true,
+ })
+ }
+ }
+}
diff --git a/intl/icu_capi/src/properties_maps.rs b/intl/icu_capi/src/properties_maps.rs
new file mode 100644
index 0000000000..3d5cd6dfce
--- /dev/null
+++ b/intl/icu_capi/src/properties_maps.rs
@@ -0,0 +1,311 @@
+// 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]
+pub mod ffi {
+ use crate::provider::ffi::ICU4XDataProvider;
+ use alloc::boxed::Box;
+ use icu_collections::codepointtrie::TrieValue;
+ use icu_properties::{maps, GeneralCategory, GeneralCategoryGroup};
+
+ use crate::errors::ffi::ICU4XError;
+ use crate::properties_iter::ffi::CodePointRangeIterator;
+ use crate::properties_sets::ffi::ICU4XCodePointSetData;
+
+ #[diplomat::opaque]
+ /// An ICU4X Unicode Map Property object, capable of querying whether a code point (key) to obtain the Unicode property value, for a specific Unicode property.
+ ///
+ /// For properties whose values fit into 8 bits.
+ #[diplomat::rust_link(icu::properties, Mod)]
+ #[diplomat::rust_link(icu::properties::maps::CodePointMapData, Struct)]
+ #[diplomat::rust_link(icu::properties::maps::CodePointMapData::from_data, FnInStruct, hidden)]
+ #[diplomat::rust_link(
+ icu::properties::maps::CodePointMapData::try_into_converted,
+ FnInStruct,
+ hidden
+ )]
+ #[diplomat::rust_link(icu::properties::maps::CodePointMapDataBorrowed, Struct)]
+ pub struct ICU4XCodePointMapData8(maps::CodePointMapData<u8>);
+
+ fn convert_8<P: TrieValue>(data: maps::CodePointMapData<P>) -> Box<ICU4XCodePointMapData8> {
+ #[allow(clippy::expect_used)] // infallible for the chosen properties
+ Box::new(ICU4XCodePointMapData8(
+ data.try_into_converted()
+ .expect("try_into_converted to u8 must be infallible"),
+ ))
+ }
+
+ impl ICU4XCodePointMapData8 {
+ /// Gets the value for a code point.
+ #[diplomat::rust_link(icu::properties::maps::CodePointMapDataBorrowed::get, FnInStruct)]
+ pub fn get(&self, cp: char) -> u8 {
+ self.0.as_borrowed().get(cp)
+ }
+
+ /// Gets the value for a code point (specified as a 32 bit integer, in UTF-32)
+ #[diplomat::rust_link(
+ icu::properties::maps::CodePointMapDataBorrowed::get32,
+ FnInStruct,
+ hidden
+ )]
+ pub fn get32(&self, cp: u32) -> u8 {
+ self.0.as_borrowed().get32(cp)
+ }
+
+ /// Converts a general category to its corresponding mask value
+ ///
+ /// Nonexistant general categories will map to the empty mask
+ #[diplomat::rust_link(icu::properties::GeneralCategoryGroup, Struct)]
+ pub fn general_category_to_mask(gc: u8) -> u32 {
+ if let Ok(gc) = GeneralCategory::try_from(gc) {
+ let group: GeneralCategoryGroup = gc.into();
+ group.into()
+ } else {
+ 0
+ }
+ }
+
+ /// Produces an iterator over ranges of code points that map to `value`
+ #[diplomat::rust_link(
+ icu::properties::maps::CodePointMapDataBorrowed::iter_ranges_for_value,
+ FnInStruct
+ )]
+ pub fn iter_ranges_for_value<'a>(&'a self, value: u8) -> Box<CodePointRangeIterator<'a>> {
+ Box::new(CodePointRangeIterator(Box::new(
+ self.0.as_borrowed().iter_ranges_for_value(value),
+ )))
+ }
+
+ /// Produces an iterator over ranges of code points that do not map to `value`
+ #[diplomat::rust_link(
+ icu::properties::maps::CodePointMapDataBorrowed::iter_ranges_for_value_complemented,
+ FnInStruct
+ )]
+ pub fn iter_ranges_for_value_complemented<'a>(
+ &'a self,
+ value: u8,
+ ) -> Box<CodePointRangeIterator<'a>> {
+ Box::new(CodePointRangeIterator(Box::new(
+ self.0
+ .as_borrowed()
+ .iter_ranges_for_value_complemented(value),
+ )))
+ }
+
+ /// Given a mask value (the nth bit marks property value = n), produce an iterator over ranges of code points
+ /// whose property values are contained in the mask.
+ ///
+ /// The main mask property supported is that for General_Category, which can be obtained via `general_category_to_mask()` or
+ /// by using `ICU4XGeneralCategoryNameToMaskMapper`
+ ///
+ /// Should only be used on maps for properties with values less than 32 (like Generak_Category),
+ /// other maps will have unpredictable results
+ #[diplomat::rust_link(
+ icu::properties::maps::CodePointMapDataBorrowed::iter_ranges_for_group,
+ FnInStruct
+ )]
+ pub fn iter_ranges_for_mask<'a>(&'a self, mask: u32) -> Box<CodePointRangeIterator<'a>> {
+ let ranges = self
+ .0
+ .as_borrowed()
+ .iter_ranges_mapped(move |v| {
+ let val_mask = 1_u32.checked_shl(v.into()).unwrap_or(0);
+ val_mask & mask != 0
+ })
+ .filter(|v| v.value)
+ .map(|v| v.range);
+ Box::new(CodePointRangeIterator(Box::new(ranges)))
+ }
+
+ /// Gets a [`ICU4XCodePointSetData`] representing all entries in this map that map to the given value
+ #[diplomat::rust_link(
+ icu::properties::maps::CodePointMapDataBorrowed::get_set_for_value,
+ FnInStruct
+ )]
+ pub fn get_set_for_value(&self, value: u8) -> Box<ICU4XCodePointSetData> {
+ Box::new(ICU4XCodePointSetData(
+ self.0.as_borrowed().get_set_for_value(value),
+ ))
+ }
+
+ #[diplomat::rust_link(icu::properties::maps::general_category, Fn)]
+ #[diplomat::rust_link(icu::properties::maps::load_general_category, Fn, hidden)]
+ pub fn load_general_category(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointMapData8>, ICU4XError> {
+ Ok(convert_8(call_constructor_unstable!(
+ maps::general_category [r => Ok(r.static_to_owned())],
+ maps::load_general_category,
+ provider,
+ )?))
+ }
+
+ #[diplomat::rust_link(icu::properties::maps::bidi_class, Fn)]
+ #[diplomat::rust_link(icu::properties::maps::load_bidi_class, Fn, hidden)]
+ pub fn load_bidi_class(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointMapData8>, ICU4XError> {
+ Ok(convert_8(call_constructor_unstable!(
+ maps::bidi_class [r => Ok(r.static_to_owned())],
+ maps::load_bidi_class,
+ provider,
+ )?))
+ }
+
+ #[diplomat::rust_link(icu::properties::maps::east_asian_width, Fn)]
+ #[diplomat::rust_link(icu::properties::maps::load_east_asian_width, Fn, hidden)]
+ pub fn load_east_asian_width(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointMapData8>, ICU4XError> {
+ Ok(convert_8(call_constructor_unstable!(
+ maps::east_asian_width [r => Ok(r.static_to_owned())],
+ maps::load_east_asian_width,
+ provider,
+ )?))
+ }
+
+ #[diplomat::rust_link(icu::properties::maps::indic_syllabic_category, Fn)]
+ #[diplomat::rust_link(icu::properties::maps::load_indic_syllabic_category, Fn, hidden)]
+ pub fn load_indic_syllabic_category(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointMapData8>, ICU4XError> {
+ Ok(convert_8(call_constructor_unstable!(
+ maps::indic_syllabic_category [r => Ok(r.static_to_owned())],
+ maps::load_indic_syllabic_category,
+ provider,
+ )?))
+ }
+
+ #[diplomat::rust_link(icu::properties::maps::line_break, Fn)]
+ #[diplomat::rust_link(icu::properties::maps::load_line_break, Fn, hidden)]
+ pub fn load_line_break(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointMapData8>, ICU4XError> {
+ Ok(convert_8(call_constructor_unstable!(
+ maps::line_break [r => Ok(r.static_to_owned())],
+ maps::load_line_break,
+ provider,
+ )?))
+ }
+
+ #[diplomat::rust_link(icu::properties::maps::grapheme_cluster_break, Fn)]
+ #[diplomat::rust_link(icu::properties::maps::load_grapheme_cluster_break, Fn, hidden)]
+ #[diplomat::attr(dart, rename = "grapheme_cluster_break")]
+ pub fn try_grapheme_cluster_break(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointMapData8>, ICU4XError> {
+ Ok(convert_8(call_constructor_unstable!(
+ maps::grapheme_cluster_break [r => Ok(r.static_to_owned())],
+ maps::load_grapheme_cluster_break,
+ provider,
+ )?))
+ }
+
+ #[diplomat::rust_link(icu::properties::maps::word_break, Fn)]
+ #[diplomat::rust_link(icu::properties::maps::load_word_break, Fn, hidden)]
+ pub fn load_word_break(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointMapData8>, ICU4XError> {
+ Ok(convert_8(call_constructor_unstable!(
+ maps::word_break [r => Ok(r.static_to_owned())],
+ maps::load_word_break,
+ provider,
+ )?))
+ }
+
+ #[diplomat::rust_link(icu::properties::maps::sentence_break, Fn)]
+ #[diplomat::rust_link(icu::properties::maps::load_sentence_break, Fn, hidden)]
+ pub fn load_sentence_break(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointMapData8>, ICU4XError> {
+ Ok(convert_8(call_constructor_unstable!(
+ maps::sentence_break [r => Ok(r.static_to_owned())],
+ maps::load_sentence_break,
+ provider,
+ )?))
+ }
+ }
+
+ #[diplomat::opaque]
+ /// An ICU4X Unicode Map Property object, capable of querying whether a code point (key) to obtain the Unicode property value, for a specific Unicode property.
+ ///
+ /// For properties whose values fit into 16 bits.
+ #[diplomat::rust_link(icu::properties, Mod)]
+ #[diplomat::rust_link(icu::properties::maps::CodePointMapData, Struct)]
+ #[diplomat::rust_link(icu::properties::maps::CodePointMapDataBorrowed, Struct)]
+ pub struct ICU4XCodePointMapData16(maps::CodePointMapData<u16>);
+
+ impl ICU4XCodePointMapData16 {
+ /// Gets the value for a code point.
+ #[diplomat::rust_link(icu::properties::maps::CodePointMapDataBorrowed::get, FnInStruct)]
+ pub fn get(&self, cp: char) -> u16 {
+ self.0.as_borrowed().get(cp)
+ }
+
+ /// Gets the value for a code point (specified as a 32 bit integer, in UTF-32)
+ #[diplomat::rust_link(
+ icu::properties::maps::CodePointMapDataBorrowed::get32,
+ FnInStruct,
+ hidden
+ )]
+ pub fn get32(&self, cp: u32) -> u16 {
+ self.0.as_borrowed().get32(cp)
+ }
+
+ /// Produces an iterator over ranges of code points that map to `value`
+ #[diplomat::rust_link(
+ icu::properties::maps::CodePointMapDataBorrowed::iter_ranges_for_value,
+ FnInStruct
+ )]
+ pub fn iter_ranges_for_value<'a>(&'a self, value: u16) -> Box<CodePointRangeIterator<'a>> {
+ Box::new(CodePointRangeIterator(Box::new(
+ self.0.as_borrowed().iter_ranges_for_value(value),
+ )))
+ }
+
+ /// Produces an iterator over ranges of code points that do not map to `value`
+ #[diplomat::rust_link(
+ icu::properties::maps::CodePointMapDataBorrowed::iter_ranges_for_value_complemented,
+ FnInStruct
+ )]
+ pub fn iter_ranges_for_value_complemented<'a>(
+ &'a self,
+ value: u16,
+ ) -> Box<CodePointRangeIterator<'a>> {
+ Box::new(CodePointRangeIterator(Box::new(
+ self.0
+ .as_borrowed()
+ .iter_ranges_for_value_complemented(value),
+ )))
+ }
+
+ /// Gets a [`ICU4XCodePointSetData`] representing all entries in this map that map to the given value
+ #[diplomat::rust_link(
+ icu::properties::maps::CodePointMapDataBorrowed::get_set_for_value,
+ FnInStruct
+ )]
+ pub fn get_set_for_value(&self, value: u16) -> Box<ICU4XCodePointSetData> {
+ Box::new(ICU4XCodePointSetData(
+ self.0.as_borrowed().get_set_for_value(value),
+ ))
+ }
+
+ #[diplomat::rust_link(icu::properties::maps::script, Fn)]
+ #[diplomat::rust_link(icu::properties::maps::load_script, Fn, hidden)]
+ pub fn load_script(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointMapData16>, ICU4XError> {
+ #[allow(clippy::expect_used)] // script is a 16-bit property
+ Ok(Box::new(ICU4XCodePointMapData16(
+ call_constructor_unstable!(
+ maps::script [r => Ok(r.static_to_owned())],
+ maps::load_script,
+ provider,
+ )?
+ .try_into_converted()
+ .expect("try_into_converted to u16 must be infallible"),
+ )))
+ }
+ }
+}
diff --git a/intl/icu_capi/src/properties_names.rs b/intl/icu_capi/src/properties_names.rs
new file mode 100644
index 0000000000..a5ab0e143a
--- /dev/null
+++ b/intl/icu_capi/src/properties_names.rs
@@ -0,0 +1,259 @@
+// 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]
+pub mod ffi {
+ use crate::provider::ffi::ICU4XDataProvider;
+ use alloc::boxed::Box;
+ use icu_properties::{
+ names::PropertyValueNameToEnumMapper, BidiClass, EastAsianWidth, GeneralCategory,
+ GeneralCategoryGroup, GraphemeClusterBreak, IndicSyllabicCategory, LineBreak, Script,
+ SentenceBreak, WordBreak,
+ };
+
+ use crate::errors::ffi::ICU4XError;
+
+ /// A type capable of looking up a property value from a string name.
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::properties::names::PropertyValueNameToEnumMapper, Struct)]
+ #[diplomat::rust_link(icu::properties::names::PropertyValueNameToEnumMapperBorrowed, Struct)]
+ #[diplomat::rust_link(
+ icu::properties::names::PropertyValueNameToEnumMapper::from_data,
+ FnInStruct,
+ hidden
+ )]
+ pub struct ICU4XPropertyValueNameToEnumMapper(PropertyValueNameToEnumMapper<u16>);
+
+ impl ICU4XPropertyValueNameToEnumMapper {
+ /// Get the property value matching the given name, using strict matching
+ ///
+ /// Returns -1 if the name is unknown for this property
+ #[diplomat::rust_link(
+ icu::properties::names::PropertyValueNameToEnumMapperBorrowed::get_strict,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu::properties::names::PropertyValueNameToEnumMapperBorrowed::get_strict_u16,
+ FnInStruct,
+ hidden
+ )]
+ pub fn get_strict(&self, name: &str) -> i16 {
+ self.0
+ .as_borrowed()
+ .get_strict(name)
+ .map(|x| x as i16)
+ .unwrap_or(-1)
+ }
+
+ /// Get the property value matching the given name, using loose matching
+ ///
+ /// Returns -1 if the name is unknown for this property
+ #[diplomat::rust_link(
+ icu::properties::names::PropertyValueNameToEnumMapperBorrowed::get_loose,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu::properties::names::PropertyValueNameToEnumMapperBorrowed::get_loose_u16,
+ FnInStruct,
+ hidden
+ )]
+ pub fn get_loose(&self, name: &str) -> i16 {
+ self.0
+ .as_borrowed()
+ .get_loose(name)
+ .map(|x| x as i16)
+ .unwrap_or(-1)
+ }
+
+ #[diplomat::rust_link(
+ icu::properties::GeneralCategory::get_name_to_enum_mapper,
+ FnInStruct
+ )]
+ pub fn load_general_category(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XPropertyValueNameToEnumMapper>, ICU4XError> {
+ Ok(Box::new(ICU4XPropertyValueNameToEnumMapper(
+ call_constructor_unstable!(
+ GeneralCategory::name_to_enum_mapper [r => Ok(r.static_to_owned())],
+ GeneralCategory::get_name_to_enum_mapper,
+ provider,
+ )?
+ .erase(),
+ )))
+ }
+
+ #[diplomat::rust_link(icu::properties::BidiClass::name_to_enum_mapper, FnInStruct)]
+ pub fn load_bidi_class(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XPropertyValueNameToEnumMapper>, ICU4XError> {
+ Ok(Box::new(ICU4XPropertyValueNameToEnumMapper(
+ call_constructor_unstable!(
+ BidiClass::name_to_enum_mapper [r => Ok(r.static_to_owned())],
+ BidiClass::get_name_to_enum_mapper,
+ provider,
+ )?
+ .erase(),
+ )))
+ }
+
+ #[diplomat::rust_link(icu::properties::EastAsianWidth::name_to_enum_mapper, FnInStruct)]
+ pub fn load_east_asian_width(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XPropertyValueNameToEnumMapper>, ICU4XError> {
+ Ok(Box::new(ICU4XPropertyValueNameToEnumMapper(
+ call_constructor_unstable!(
+ EastAsianWidth::name_to_enum_mapper [r => Ok(r.static_to_owned())],
+ EastAsianWidth::get_name_to_enum_mapper,
+ provider,
+ )?
+ .erase(),
+ )))
+ }
+
+ #[diplomat::rust_link(
+ icu::properties::IndicSyllabicCategory::name_to_enum_mapper,
+ FnInStruct
+ )]
+ pub fn load_indic_syllabic_category(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XPropertyValueNameToEnumMapper>, ICU4XError> {
+ Ok(Box::new(ICU4XPropertyValueNameToEnumMapper(
+ call_constructor_unstable!(
+ IndicSyllabicCategory::name_to_enum_mapper [r => Ok(r.static_to_owned())],
+ IndicSyllabicCategory::get_name_to_enum_mapper,
+ provider,
+ )?
+ .erase(),
+ )))
+ }
+
+ #[diplomat::rust_link(icu::properties::LineBreak::name_to_enum_mapper, FnInStruct)]
+ pub fn load_line_break(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XPropertyValueNameToEnumMapper>, ICU4XError> {
+ Ok(Box::new(ICU4XPropertyValueNameToEnumMapper(
+ call_constructor_unstable!(
+ LineBreak::name_to_enum_mapper [r => Ok(r.static_to_owned())],
+ LineBreak::get_name_to_enum_mapper,
+ provider,
+ )?
+ .erase(),
+ )))
+ }
+
+ #[diplomat::rust_link(
+ icu::properties::GraphemeClusterBreak::get_name_to_enum_mapper,
+ FnInStruct
+ )]
+ pub fn load_grapheme_cluster_break(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XPropertyValueNameToEnumMapper>, ICU4XError> {
+ Ok(Box::new(ICU4XPropertyValueNameToEnumMapper(
+ call_constructor_unstable!(
+ GraphemeClusterBreak::name_to_enum_mapper [r => Ok(r.static_to_owned())],
+ GraphemeClusterBreak::get_name_to_enum_mapper,
+ provider,
+ )?
+ .erase(),
+ )))
+ }
+
+ #[diplomat::rust_link(icu::properties::WordBreak::name_to_enum_mapper, FnInStruct)]
+ pub fn load_word_break(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XPropertyValueNameToEnumMapper>, ICU4XError> {
+ Ok(Box::new(ICU4XPropertyValueNameToEnumMapper(
+ call_constructor_unstable!(
+ WordBreak::name_to_enum_mapper [r => Ok(r.static_to_owned())],
+ WordBreak::get_name_to_enum_mapper,
+ provider,
+ )?
+ .erase(),
+ )))
+ }
+
+ #[diplomat::rust_link(icu::properties::SentenceBreak::name_to_enum_mapper, FnInStruct)]
+ pub fn load_sentence_break(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XPropertyValueNameToEnumMapper>, ICU4XError> {
+ Ok(Box::new(ICU4XPropertyValueNameToEnumMapper(
+ call_constructor_unstable!(
+ SentenceBreak::name_to_enum_mapper [r => Ok(r.static_to_owned())],
+ SentenceBreak::get_name_to_enum_mapper,
+ provider,
+ )?
+ .erase(),
+ )))
+ }
+
+ #[diplomat::rust_link(icu::properties::Script::name_to_enum_mapper, FnInStruct)]
+ pub fn load_script(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XPropertyValueNameToEnumMapper>, ICU4XError> {
+ Ok(Box::new(ICU4XPropertyValueNameToEnumMapper(
+ call_constructor_unstable!(
+ Script::name_to_enum_mapper [r => Ok(r.static_to_owned())],
+ Script::get_name_to_enum_mapper,
+ provider,
+ )?
+ .erase(),
+ )))
+ }
+ }
+
+ /// A type capable of looking up General Category mask values from a string name.
+ #[diplomat::opaque]
+ #[diplomat::rust_link(
+ icu::properties::GeneralCategoryGroup::get_name_to_enum_mapper,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(icu::properties::names::PropertyValueNameToEnumMapper, Struct)]
+ pub struct ICU4XGeneralCategoryNameToMaskMapper(
+ PropertyValueNameToEnumMapper<GeneralCategoryGroup>,
+ );
+
+ impl ICU4XGeneralCategoryNameToMaskMapper {
+ /// Get the mask value matching the given name, using strict matching
+ ///
+ /// Returns 0 if the name is unknown for this property
+ // #[diplomat::rust_link(icu::properties::maps::PropertyValueNameToEnumMapperBorrowed::get_strict, FnInStruct)]
+ // #[diplomat::rust_link(icu::properties::maps::PropertyValueNameToEnumMapperBorrowed::get_strict_u16, FnInStruct, hidden)]
+ pub fn get_strict(&self, name: &str) -> u32 {
+ self.0
+ .as_borrowed()
+ .get_strict(name)
+ .map(Into::into)
+ .unwrap_or(0)
+ }
+
+ /// Get the mask value matching the given name, using loose matching
+ ///
+ /// Returns 0 if the name is unknown for this property
+ // #[diplomat::rust_link(icu::properties::maps::PropertyValueNameToEnumMapperBorrowed::get_loose, FnInStruct)]
+ // #[diplomat::rust_link(icu::properties::maps::PropertyValueNameToEnumMapperBorrowed::get_loose_u16, FnInStruct, hidden)]
+ pub fn get_loose(&self, name: &str) -> u32 {
+ self.0
+ .as_borrowed()
+ .get_loose(name)
+ .map(Into::into)
+ .unwrap_or(0)
+ }
+
+ #[diplomat::rust_link(
+ icu::properties::GeneralCategoryGroup::get_name_to_enum_mapper,
+ FnInStruct
+ )]
+ pub fn load(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XGeneralCategoryNameToMaskMapper>, ICU4XError> {
+ Ok(Box::new(ICU4XGeneralCategoryNameToMaskMapper(
+ call_constructor_unstable!(
+ GeneralCategoryGroup::name_to_enum_mapper [r => Ok(r.static_to_owned())],
+ GeneralCategoryGroup::get_name_to_enum_mapper,
+ provider,
+ )?,
+ )))
+ }
+ }
+}
diff --git a/intl/icu_capi/src/properties_sets.rs b/intl/icu_capi/src/properties_sets.rs
new file mode 100644
index 0000000000..af14b9cbc8
--- /dev/null
+++ b/intl/icu_capi/src/properties_sets.rs
@@ -0,0 +1,887 @@
+// 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]
+pub mod ffi {
+ use crate::provider::ffi::ICU4XDataProvider;
+ use alloc::boxed::Box;
+ use core::str;
+ use icu_properties::sets;
+
+ use crate::errors::ffi::ICU4XError;
+ use crate::properties_iter::ffi::CodePointRangeIterator;
+
+ #[diplomat::opaque]
+ /// An ICU4X Unicode Set Property object, capable of querying whether a code point is contained in a set based on a Unicode property.
+ #[diplomat::rust_link(icu::properties, Mod)]
+ #[diplomat::rust_link(icu::properties::sets::CodePointSetData, Struct)]
+ #[diplomat::rust_link(icu::properties::sets::CodePointSetData::from_data, FnInStruct, hidden)]
+ #[diplomat::rust_link(icu::properties::sets::CodePointSetDataBorrowed, Struct)]
+ pub struct ICU4XCodePointSetData(pub sets::CodePointSetData);
+
+ impl ICU4XCodePointSetData {
+ /// Checks whether the code point is in the set.
+ #[diplomat::rust_link(
+ icu::properties::sets::CodePointSetDataBorrowed::contains,
+ FnInStruct
+ )]
+ pub fn contains(&self, cp: char) -> bool {
+ self.0.as_borrowed().contains(cp)
+ }
+ /// Checks whether the code point (specified as a 32 bit integer, in UTF-32) is in the set.
+ #[diplomat::rust_link(
+ icu::properties::sets::CodePointSetDataBorrowed::contains32,
+ FnInStruct,
+ hidden
+ )]
+ #[diplomat::attr(dart, disable)]
+ pub fn contains32(&self, cp: u32) -> bool {
+ self.0.as_borrowed().contains32(cp)
+ }
+
+ /// Produces an iterator over ranges of code points contained in this set
+ #[diplomat::rust_link(
+ icu::properties::sets::CodePointSetDataBorrowed::iter_ranges,
+ FnInStruct
+ )]
+ pub fn iter_ranges<'a>(&'a self) -> Box<CodePointRangeIterator<'a>> {
+ Box::new(CodePointRangeIterator(Box::new(
+ self.0.as_borrowed().iter_ranges(),
+ )))
+ }
+
+ /// Produces an iterator over ranges of code points not contained in this set
+ #[diplomat::rust_link(
+ icu::properties::sets::CodePointSetDataBorrowed::iter_ranges_complemented,
+ FnInStruct
+ )]
+ pub fn iter_ranges_complemented<'a>(&'a self) -> Box<CodePointRangeIterator<'a>> {
+ Box::new(CodePointRangeIterator(Box::new(
+ self.0.as_borrowed().iter_ranges_complemented(),
+ )))
+ }
+
+ /// which is a mask with the same format as the `U_GC_XX_MASK` mask in ICU4C
+ #[diplomat::rust_link(icu::properties::sets::for_general_category_group, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_for_general_category_group, Fn, hidden)]
+ pub fn load_for_general_category_group(
+ provider: &ICU4XDataProvider,
+ group: u32,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::for_general_category_group [r => Ok(r)],
+ sets::load_for_general_category_group,
+ provider,
+ group.into(),
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::ascii_hex_digit, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_ascii_hex_digit, Fn, hidden)]
+ pub fn load_ascii_hex_digit(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::ascii_hex_digit [r => Ok(r.static_to_owned())],
+ sets::load_ascii_hex_digit,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::alnum, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_alnum, Fn, hidden)]
+ pub fn load_alnum(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::alnum [r => Ok(r.static_to_owned())],
+ sets::load_alnum,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::alphabetic, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_alphabetic, Fn, hidden)]
+ pub fn load_alphabetic(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::alphabetic [r => Ok(r.static_to_owned())],
+ sets::load_alphabetic,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::bidi_control, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_bidi_control, Fn, hidden)]
+ pub fn load_bidi_control(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::bidi_control [r => Ok(r.static_to_owned())],
+ sets::load_bidi_control,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::bidi_mirrored, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_bidi_mirrored, Fn, hidden)]
+ pub fn load_bidi_mirrored(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::bidi_mirrored [r => Ok(r.static_to_owned())],
+ sets::load_bidi_mirrored,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::blank, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_blank, Fn, hidden)]
+ pub fn load_blank(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::blank [r => Ok(r.static_to_owned())],
+ sets::load_blank,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::cased, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_cased, Fn, hidden)]
+ pub fn load_cased(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::cased [r => Ok(r.static_to_owned())],
+ sets::load_cased,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::case_ignorable, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_case_ignorable, Fn, hidden)]
+ pub fn load_case_ignorable(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::case_ignorable [r => Ok(r.static_to_owned())],
+ sets::load_case_ignorable,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::full_composition_exclusion, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_full_composition_exclusion, Fn, hidden)]
+ pub fn load_full_composition_exclusion(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::full_composition_exclusion [r => Ok(r.static_to_owned())],
+ sets::load_full_composition_exclusion,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::changes_when_casefolded, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_changes_when_casefolded, Fn, hidden)]
+ pub fn load_changes_when_casefolded(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::changes_when_casefolded [r => Ok(r.static_to_owned())],
+ sets::load_changes_when_casefolded,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::changes_when_casemapped, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_changes_when_casemapped, Fn, hidden)]
+ pub fn load_changes_when_casemapped(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::changes_when_casemapped [r => Ok(r.static_to_owned())],
+ sets::load_changes_when_casemapped,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::changes_when_nfkc_casefolded, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_changes_when_nfkc_casefolded, Fn, hidden)]
+ pub fn load_changes_when_nfkc_casefolded(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::changes_when_nfkc_casefolded [r => Ok(r.static_to_owned())],
+ sets::load_changes_when_nfkc_casefolded,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::changes_when_lowercased, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_changes_when_lowercased, Fn, hidden)]
+ pub fn load_changes_when_lowercased(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::changes_when_lowercased [r => Ok(r.static_to_owned())],
+ sets::load_changes_when_lowercased,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::changes_when_titlecased, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_changes_when_titlecased, Fn, hidden)]
+ pub fn load_changes_when_titlecased(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::changes_when_titlecased [r => Ok(r.static_to_owned())],
+ sets::load_changes_when_titlecased,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::changes_when_uppercased, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_changes_when_uppercased, Fn, hidden)]
+ pub fn load_changes_when_uppercased(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::changes_when_uppercased [r => Ok(r.static_to_owned())],
+ sets::load_changes_when_uppercased,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::dash, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_dash, Fn, hidden)]
+ pub fn load_dash(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::dash [r => Ok(r.static_to_owned())],
+ sets::load_dash,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::deprecated, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_deprecated, Fn, hidden)]
+ pub fn load_deprecated(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::deprecated [r => Ok(r.static_to_owned())],
+ sets::load_deprecated,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::default_ignorable_code_point, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_default_ignorable_code_point, Fn, hidden)]
+ pub fn load_default_ignorable_code_point(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::default_ignorable_code_point [r => Ok(r.static_to_owned())],
+ sets::load_default_ignorable_code_point,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::diacritic, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_diacritic, Fn, hidden)]
+ pub fn load_diacritic(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::diacritic [r => Ok(r.static_to_owned())],
+ sets::load_diacritic,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::emoji_modifier_base, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_emoji_modifier_base, Fn, hidden)]
+ pub fn load_emoji_modifier_base(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::emoji_modifier_base [r => Ok(r.static_to_owned())],
+ sets::load_emoji_modifier_base,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::emoji_component, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_emoji_component, Fn, hidden)]
+ pub fn load_emoji_component(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::emoji_component [r => Ok(r.static_to_owned())],
+ sets::load_emoji_component,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::emoji_modifier, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_emoji_modifier, Fn, hidden)]
+ pub fn load_emoji_modifier(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::emoji_modifier [r => Ok(r.static_to_owned())],
+ sets::load_emoji_modifier,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::emoji, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_emoji, Fn, hidden)]
+ pub fn load_emoji(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::emoji [r => Ok(r.static_to_owned())],
+ sets::load_emoji,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::emoji_presentation, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_emoji_presentation, Fn, hidden)]
+ pub fn load_emoji_presentation(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::emoji_presentation [r => Ok(r.static_to_owned())],
+ sets::load_emoji_presentation,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::extender, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_extender, Fn, hidden)]
+ pub fn load_extender(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::extender [r => Ok(r.static_to_owned())],
+ sets::load_extender,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::extended_pictographic, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_extended_pictographic, Fn, hidden)]
+ pub fn load_extended_pictographic(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::extended_pictographic [r => Ok(r.static_to_owned())],
+ sets::load_extended_pictographic,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::graph, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_graph, Fn, hidden)]
+ pub fn load_graph(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::graph [r => Ok(r.static_to_owned())],
+ sets::load_graph,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::grapheme_base, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_grapheme_base, Fn, hidden)]
+ pub fn load_grapheme_base(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::grapheme_base [r => Ok(r.static_to_owned())],
+ sets::load_grapheme_base,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::grapheme_extend, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_grapheme_extend, Fn, hidden)]
+ pub fn load_grapheme_extend(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::grapheme_extend [r => Ok(r.static_to_owned())],
+ sets::load_grapheme_extend,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::grapheme_link, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_grapheme_link, Fn, hidden)]
+ pub fn load_grapheme_link(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::grapheme_link [r => Ok(r.static_to_owned())],
+ sets::load_grapheme_link,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::hex_digit, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_hex_digit, Fn, hidden)]
+ pub fn load_hex_digit(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::hex_digit [r => Ok(r.static_to_owned())],
+ sets::load_hex_digit,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::hyphen, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_hyphen, Fn, hidden)]
+ pub fn load_hyphen(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::hyphen [r => Ok(r.static_to_owned())],
+ sets::load_hyphen,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::id_continue, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_id_continue, Fn, hidden)]
+ pub fn load_id_continue(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::id_continue [r => Ok(r.static_to_owned())],
+ sets::load_id_continue,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::ideographic, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_ideographic, Fn, hidden)]
+ pub fn load_ideographic(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::ideographic [r => Ok(r.static_to_owned())],
+ sets::load_ideographic,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::id_start, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_id_start, Fn, hidden)]
+ pub fn load_id_start(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::id_start [r => Ok(r.static_to_owned())],
+ sets::load_id_start,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::ids_binary_operator, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_ids_binary_operator, Fn, hidden)]
+ pub fn load_ids_binary_operator(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::ids_binary_operator [r => Ok(r.static_to_owned())],
+ sets::load_ids_binary_operator,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::ids_trinary_operator, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_ids_trinary_operator, Fn, hidden)]
+ pub fn load_ids_trinary_operator(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::ids_trinary_operator [r => Ok(r.static_to_owned())],
+ sets::load_ids_trinary_operator,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::join_control, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_join_control, Fn, hidden)]
+ pub fn load_join_control(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::join_control [r => Ok(r.static_to_owned())],
+ sets::load_join_control,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::logical_order_exception, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_logical_order_exception, Fn, hidden)]
+ pub fn load_logical_order_exception(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::logical_order_exception [r => Ok(r.static_to_owned())],
+ sets::load_logical_order_exception,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::lowercase, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_lowercase, Fn, hidden)]
+ pub fn load_lowercase(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::lowercase [r => Ok(r.static_to_owned())],
+ sets::load_lowercase,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::math, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_math, Fn, hidden)]
+ pub fn load_math(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::math [r => Ok(r.static_to_owned())],
+ sets::load_math,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::noncharacter_code_point, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_noncharacter_code_point, Fn, hidden)]
+ pub fn load_noncharacter_code_point(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::noncharacter_code_point [r => Ok(r.static_to_owned())],
+ sets::load_noncharacter_code_point,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::nfc_inert, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_nfc_inert, Fn, hidden)]
+ pub fn load_nfc_inert(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::nfc_inert [r => Ok(r.static_to_owned())],
+ sets::load_nfc_inert,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::nfd_inert, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_nfd_inert, Fn, hidden)]
+ pub fn load_nfd_inert(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::nfd_inert [r => Ok(r.static_to_owned())],
+ sets::load_nfd_inert,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::nfkc_inert, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_nfkc_inert, Fn, hidden)]
+ pub fn load_nfkc_inert(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::nfkc_inert [r => Ok(r.static_to_owned())],
+ sets::load_nfkc_inert,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::nfkd_inert, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_nfkd_inert, Fn, hidden)]
+ pub fn load_nfkd_inert(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::nfkd_inert [r => Ok(r.static_to_owned())],
+ sets::load_nfkd_inert,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::pattern_syntax, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_pattern_syntax, Fn, hidden)]
+ pub fn load_pattern_syntax(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::pattern_syntax [r => Ok(r.static_to_owned())],
+ sets::load_pattern_syntax,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::pattern_white_space, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_pattern_white_space, Fn, hidden)]
+ pub fn load_pattern_white_space(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::pattern_white_space [r => Ok(r.static_to_owned())],
+ sets::load_pattern_white_space,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::prepended_concatenation_mark, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_prepended_concatenation_mark, Fn, hidden)]
+ pub fn load_prepended_concatenation_mark(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::prepended_concatenation_mark [r => Ok(r.static_to_owned())],
+ sets::load_prepended_concatenation_mark,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::print, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_print, Fn, hidden)]
+ pub fn load_print(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::print [r => Ok(r.static_to_owned())],
+ sets::load_print,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::quotation_mark, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_quotation_mark, Fn, hidden)]
+ pub fn load_quotation_mark(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::quotation_mark [r => Ok(r.static_to_owned())],
+ sets::load_quotation_mark,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::radical, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_radical, Fn, hidden)]
+ pub fn load_radical(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::radical [r => Ok(r.static_to_owned())],
+ sets::load_radical,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::regional_indicator, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_regional_indicator, Fn, hidden)]
+ pub fn load_regional_indicator(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::regional_indicator [r => Ok(r.static_to_owned())],
+ sets::load_regional_indicator,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::soft_dotted, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_soft_dotted, Fn, hidden)]
+ pub fn load_soft_dotted(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::soft_dotted [r => Ok(r.static_to_owned())],
+ sets::load_soft_dotted,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::segment_starter, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_segment_starter, Fn, hidden)]
+ pub fn load_segment_starter(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::segment_starter [r => Ok(r.static_to_owned())],
+ sets::load_segment_starter,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::case_sensitive, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_case_sensitive, Fn, hidden)]
+ pub fn load_case_sensitive(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::case_sensitive [r => Ok(r.static_to_owned())],
+ sets::load_case_sensitive,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::sentence_terminal, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_sentence_terminal, Fn, hidden)]
+ pub fn load_sentence_terminal(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::sentence_terminal [r => Ok(r.static_to_owned())],
+ sets::load_sentence_terminal,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::terminal_punctuation, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_terminal_punctuation, Fn, hidden)]
+ pub fn load_terminal_punctuation(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::terminal_punctuation [r => Ok(r.static_to_owned())],
+ sets::load_terminal_punctuation,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::unified_ideograph, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_unified_ideograph, Fn, hidden)]
+ pub fn load_unified_ideograph(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::unified_ideograph [r => Ok(r.static_to_owned())],
+ sets::load_unified_ideograph,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::uppercase, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_uppercase, Fn, hidden)]
+ pub fn load_uppercase(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::uppercase [r => Ok(r.static_to_owned())],
+ sets::load_uppercase,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::variation_selector, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_variation_selector, Fn, hidden)]
+ pub fn load_variation_selector(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::variation_selector [r => Ok(r.static_to_owned())],
+ sets::load_variation_selector,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::white_space, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_white_space, Fn, hidden)]
+ pub fn load_white_space(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::white_space [r => Ok(r.static_to_owned())],
+ sets::load_white_space,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::xdigit, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_xdigit, Fn, hidden)]
+ pub fn load_xdigit(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::xdigit [r => Ok(r.static_to_owned())],
+ sets::load_xdigit,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::xid_continue, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_xid_continue, Fn, hidden)]
+ pub fn load_xid_continue(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::xid_continue [r => Ok(r.static_to_owned())],
+ sets::load_xid_continue,
+ provider
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::xid_start, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_xid_start, Fn, hidden)]
+ pub fn load_xid_start(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::xid_start [r => Ok(r.static_to_owned())],
+ sets::load_xid_start,
+ provider
+ )?)))
+ }
+
+ /// Loads data for a property specified as a string as long as it is one of the
+ /// [ECMA-262 binary properties][ecma] (not including Any, ASCII, and Assigned pseudoproperties).
+ ///
+ /// Returns `ICU4XError::PropertyUnexpectedPropertyNameError` in case the string does not
+ /// match any property in the list
+ ///
+ /// [ecma]: https://tc39.es/ecma262/#table-binary-unicode-properties
+ #[diplomat::rust_link(icu::properties::sets::for_ecma262, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_for_ecma262, Fn, hidden)]
+ pub fn load_for_ecma262(
+ provider: &ICU4XDataProvider,
+ property_name: &str,
+ ) -> Result<Box<ICU4XCodePointSetData>, ICU4XError> {
+ let name = property_name.as_bytes(); // #2520
+ let name = if let Ok(s) = str::from_utf8(name) {
+ s
+ } else {
+ return Err(ICU4XError::TinyStrNonAsciiError);
+ };
+ Ok(Box::new(ICU4XCodePointSetData(call_constructor_unstable!(
+ sets::load_for_ecma262 [r => r.map(|r| r.static_to_owned())],
+ sets::load_for_ecma262_unstable,
+ provider,
+ name
+ )?)))
+ }
+ }
+}
diff --git a/intl/icu_capi/src/properties_unisets.rs b/intl/icu_capi/src/properties_unisets.rs
new file mode 100644
index 0000000000..7d2cf8cccc
--- /dev/null
+++ b/intl/icu_capi/src/properties_unisets.rs
@@ -0,0 +1,149 @@
+// 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]
+pub mod ffi {
+ use crate::locale::ffi::ICU4XLocale;
+ use crate::provider::ffi::ICU4XDataProvider;
+ use alloc::boxed::Box;
+ use core::str;
+ use icu_properties::{exemplar_chars, sets};
+
+ use crate::errors::ffi::ICU4XError;
+
+ #[diplomat::opaque]
+ /// An ICU4X Unicode Set Property object, capable of querying whether a code point is contained in a set based on a Unicode property.
+ #[diplomat::rust_link(icu::properties, Mod)]
+ #[diplomat::rust_link(icu::properties::sets::UnicodeSetData, Struct)]
+ #[diplomat::rust_link(icu::properties::sets::UnicodeSetData::from_data, FnInStruct, hidden)]
+ #[diplomat::rust_link(icu::properties::sets::UnicodeSetDataBorrowed, Struct)]
+ pub struct ICU4XUnicodeSetData(pub sets::UnicodeSetData);
+
+ impl ICU4XUnicodeSetData {
+ /// Checks whether the string is in the set.
+ #[diplomat::rust_link(icu::properties::sets::UnicodeSetDataBorrowed::contains, FnInStruct)]
+ pub fn contains(&self, s: &str) -> bool {
+ let s = s.as_bytes(); // #2520
+ let s = if let Ok(s) = str::from_utf8(s) {
+ s
+ } else {
+ return false;
+ };
+ self.0.as_borrowed().contains(s)
+ }
+ /// Checks whether the code point is in the set.
+ #[diplomat::rust_link(
+ icu::properties::sets::UnicodeSetDataBorrowed::contains_char,
+ FnInStruct
+ )]
+ pub fn contains_char(&self, cp: char) -> bool {
+ self.0.as_borrowed().contains_char(cp)
+ }
+ /// Checks whether the code point (specified as a 32 bit integer, in UTF-32) is in the set.
+ #[diplomat::rust_link(
+ icu::properties::sets::UnicodeSetDataBorrowed::contains32,
+ FnInStruct,
+ hidden
+ )]
+ #[diplomat::attr(dart, disable)]
+ pub fn contains32(&self, cp: u32) -> bool {
+ self.0.as_borrowed().contains32(cp)
+ }
+
+ #[diplomat::rust_link(icu::properties::sets::basic_emoji, Fn)]
+ #[diplomat::rust_link(icu::properties::sets::load_basic_emoji, Fn, hidden)]
+ pub fn load_basic_emoji(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XUnicodeSetData>, ICU4XError> {
+ Ok(Box::new(ICU4XUnicodeSetData(call_constructor_unstable!(
+ sets::basic_emoji [r => Ok(r.static_to_owned())],
+ sets::load_basic_emoji,
+ provider,
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::exemplar_chars::exemplars_main, Fn)]
+ #[diplomat::rust_link(icu::properties::exemplar_chars::load_exemplars_main, Fn, hidden)]
+ pub fn load_exemplars_main(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ ) -> Result<Box<ICU4XUnicodeSetData>, ICU4XError> {
+ let locale = locale.to_datalocale();
+ Ok(Box::new(ICU4XUnicodeSetData(call_constructor_unstable!(
+ exemplar_chars::exemplars_main,
+ exemplar_chars::load_exemplars_main,
+ provider,
+ &locale
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::exemplar_chars::exemplars_auxiliary, Fn)]
+ #[diplomat::rust_link(
+ icu::properties::exemplar_chars::load_exemplars_auxiliary,
+ Fn,
+ hidden
+ )]
+ pub fn load_exemplars_auxiliary(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ ) -> Result<Box<ICU4XUnicodeSetData>, ICU4XError> {
+ let locale = locale.to_datalocale();
+ Ok(Box::new(ICU4XUnicodeSetData(call_constructor_unstable!(
+ exemplar_chars::exemplars_auxiliary,
+ exemplar_chars::load_exemplars_auxiliary,
+ provider,
+ &locale
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::exemplar_chars::exemplars_punctuation, Fn)]
+ #[diplomat::rust_link(
+ icu::properties::exemplar_chars::load_exemplars_punctuation,
+ Fn,
+ hidden
+ )]
+ pub fn load_exemplars_punctuation(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ ) -> Result<Box<ICU4XUnicodeSetData>, ICU4XError> {
+ let locale = locale.to_datalocale();
+ Ok(Box::new(ICU4XUnicodeSetData(call_constructor_unstable!(
+ exemplar_chars::exemplars_punctuation,
+ exemplar_chars::load_exemplars_punctuation,
+ provider,
+ &locale
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::exemplar_chars::exemplars_numbers, Fn)]
+ #[diplomat::rust_link(icu::properties::exemplar_chars::load_exemplars_numbers, Fn, hidden)]
+ pub fn load_exemplars_numbers(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ ) -> Result<Box<ICU4XUnicodeSetData>, ICU4XError> {
+ let locale = locale.to_datalocale();
+ Ok(Box::new(ICU4XUnicodeSetData(call_constructor_unstable!(
+ exemplar_chars::exemplars_numbers,
+ exemplar_chars::load_exemplars_numbers,
+ provider,
+ &locale
+ )?)))
+ }
+
+ #[diplomat::rust_link(icu::properties::exemplar_chars::exemplars_index, Fn)]
+ #[diplomat::rust_link(icu::properties::exemplar_chars::load_exemplars_index, Fn, hidden)]
+ pub fn load_exemplars_index(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ ) -> Result<Box<ICU4XUnicodeSetData>, ICU4XError> {
+ let locale = locale.to_datalocale();
+ Ok(Box::new(ICU4XUnicodeSetData(call_constructor_unstable!(
+ exemplar_chars::exemplars_index,
+ exemplar_chars::load_exemplars_index,
+ provider,
+ &locale
+ )?)))
+ }
+ }
+}
diff --git a/intl/icu_capi/src/provider.rs b/intl/icu_capi/src/provider.rs
new file mode 100644
index 0000000000..18234f374c
--- /dev/null
+++ b/intl/icu_capi/src/provider.rs
@@ -0,0 +1,344 @@
+// 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 ).
+
+#[allow(unused_imports)] // feature-specific
+use alloc::boxed::Box;
+use icu_provider::prelude::*;
+#[allow(unused_imports)] // feature-specific
+use icu_provider::MaybeSendSync;
+use icu_provider_adapters::empty::EmptyDataProvider;
+#[allow(unused_imports)] // feature-specific
+use yoke::{trait_hack::YokeTraitHack, Yokeable};
+#[allow(unused_imports)] // feature-specific
+use zerofrom::ZeroFrom;
+
+pub enum ICU4XDataProviderInner {
+ Destroyed,
+ Empty,
+ #[cfg(feature = "compiled_data")]
+ Compiled,
+ #[cfg(feature = "buffer_provider")]
+ Buffer(Box<dyn BufferProvider + 'static>),
+}
+
+#[diplomat::bridge]
+pub mod ffi {
+ use super::ICU4XDataProviderInner;
+ use crate::errors::ffi::ICU4XError;
+ use alloc::boxed::Box;
+ #[allow(unused_imports)] // feature-gated
+ use icu_provider_adapters::fallback::LocaleFallbackProvider;
+ #[allow(unused_imports)] // feature-gated
+ use icu_provider_adapters::fork::predicates::MissingLocalePredicate;
+
+ #[diplomat::opaque]
+ /// An ICU4X data provider, capable of loading ICU4X data keys from some source.
+ #[diplomat::rust_link(icu_provider, Mod)]
+ pub struct ICU4XDataProvider(pub ICU4XDataProviderInner);
+
+ #[cfg(feature = "buffer_provider")]
+ fn convert_buffer_provider<D: icu_provider::BufferProvider + 'static>(
+ x: D,
+ ) -> ICU4XDataProvider {
+ ICU4XDataProvider(super::ICU4XDataProviderInner::Buffer(Box::new(x)))
+ }
+
+ impl ICU4XDataProvider {
+ /// Constructs an [`ICU4XDataProvider`] that uses compiled data.
+ ///
+ /// Requires the `compiled_data` feature.
+ ///
+ /// This provider cannot be modified or combined with other providers, so `enable_fallback`,
+ /// `enabled_fallback_with`, `fork_by_locale`, and `fork_by_key` will return `Err`s.
+ #[cfg(feature = "compiled_data")]
+ pub fn create_compiled() -> Box<ICU4XDataProvider> {
+ Box::new(Self(ICU4XDataProviderInner::Compiled))
+ }
+
+ /// Constructs an `FsDataProvider` and returns it as an [`ICU4XDataProvider`].
+ /// Requires the `provider_fs` Cargo feature.
+ /// Not supported in WASM.
+ #[diplomat::rust_link(icu_provider_fs::FsDataProvider, Struct)]
+ #[cfg(all(
+ feature = "provider_fs",
+ not(any(target_arch = "wasm32", target_os = "none"))
+ ))]
+ #[diplomat::attr(dart, disable)]
+ pub fn create_fs(path: &str) -> Result<Box<ICU4XDataProvider>, ICU4XError> {
+ // #2520
+ // In the future we can start using OsString APIs to support non-utf8 paths
+ core::str::from_utf8(path.as_bytes())
+ .map_err(|e| ICU4XError::DataIoError.log_original(&e))?;
+
+ Ok(Box::new(convert_buffer_provider(
+ icu_provider_fs::FsDataProvider::try_new(path)?,
+ )))
+ }
+
+ /// Deprecated
+ ///
+ /// Use `create_compiled()`.
+ #[cfg(all(
+ feature = "provider_test",
+ any(feature = "any_provider", feature = "buffer_provider")
+ ))]
+ #[diplomat::attr(dart, disable)]
+ pub fn create_test() -> Box<ICU4XDataProvider> {
+ Self::create_compiled()
+ }
+
+ /// Constructs a `BlobDataProvider` and returns it as an [`ICU4XDataProvider`].
+ #[diplomat::rust_link(icu_provider_blob::BlobDataProvider, Struct)]
+ #[cfg(feature = "buffer_provider")]
+ pub fn create_from_byte_slice(
+ blob: &'static [u8],
+ ) -> Result<Box<ICU4XDataProvider>, ICU4XError> {
+ Ok(Box::new(convert_buffer_provider(
+ icu_provider_blob::BlobDataProvider::try_new_from_static_blob(blob)?,
+ )))
+ }
+
+ /// Constructs an empty [`ICU4XDataProvider`].
+ #[diplomat::rust_link(icu_provider_adapters::empty::EmptyDataProvider, Struct)]
+ #[diplomat::rust_link(
+ icu_provider_adapters::empty::EmptyDataProvider::new,
+ FnInStruct,
+ hidden
+ )]
+ pub fn create_empty() -> Box<ICU4XDataProvider> {
+ Box::new(ICU4XDataProvider(ICU4XDataProviderInner::Empty))
+ }
+
+ /// Creates a provider that tries the current provider and then, if the current provider
+ /// doesn't support the data key, another provider `other`.
+ ///
+ /// This takes ownership of the `other` provider, leaving an empty provider in its place.
+ ///
+ /// The providers must be the same type (Any or Buffer). This condition is satisfied if
+ /// both providers originate from the same constructor, such as `create_from_byte_slice`
+ /// or `create_fs`. If the condition is not upheld, a runtime error occurs.
+ #[diplomat::rust_link(icu_provider_adapters::fork::ForkByKeyProvider, Typedef)]
+ #[diplomat::rust_link(
+ icu_provider_adapters::fork::predicates::MissingDataKeyPredicate,
+ Struct,
+ hidden
+ )]
+ pub fn fork_by_key(&mut self, other: &mut ICU4XDataProvider) -> Result<(), ICU4XError> {
+ #[allow(unused_imports)]
+ use ICU4XDataProviderInner::*;
+ *self = match (
+ core::mem::replace(&mut self.0, Destroyed),
+ core::mem::replace(&mut other.0, Destroyed),
+ ) {
+ (Destroyed, _) | (_, Destroyed) => Err(icu_provider::DataError::custom(
+ "This provider has been destroyed",
+ ))?,
+ #[cfg(feature = "compiled_data")]
+ (Compiled, _) | (_, Compiled) => Err(icu_provider::DataError::custom(
+ "The compiled provider cannot be modified",
+ ))?,
+ (Empty, Empty) => ICU4XDataProvider(ICU4XDataProviderInner::Empty),
+ #[cfg(feature = "buffer_provider")]
+ (Empty, b) | (b, Empty) => ICU4XDataProvider(b),
+ #[cfg(feature = "buffer_provider")]
+ (Buffer(a), Buffer(b)) => convert_buffer_provider(
+ icu_provider_adapters::fork::ForkByKeyProvider::new(a, b),
+ ),
+ };
+ Ok(())
+ }
+
+ /// Same as `fork_by_key` but forks by locale instead of key.
+ #[diplomat::rust_link(
+ icu_provider_adapters::fork::predicates::MissingLocalePredicate,
+ Struct
+ )]
+ pub fn fork_by_locale(&mut self, other: &mut ICU4XDataProvider) -> Result<(), ICU4XError> {
+ #[allow(unused_imports)]
+ use ICU4XDataProviderInner::*;
+ *self = match (
+ core::mem::replace(&mut self.0, Destroyed),
+ core::mem::replace(&mut other.0, Destroyed),
+ ) {
+ (Destroyed, _) | (_, Destroyed) => Err(icu_provider::DataError::custom(
+ "This provider has been destroyed",
+ ))?,
+ #[cfg(feature = "compiled_data")]
+ (Compiled, _) | (_, Compiled) => Err(icu_provider::DataError::custom(
+ "The compiled provider cannot be modified",
+ ))?,
+ (Empty, Empty) => ICU4XDataProvider(ICU4XDataProviderInner::Empty),
+ #[cfg(feature = "buffer_provider")]
+ (Empty, b) | (b, Empty) => ICU4XDataProvider(b),
+ #[cfg(feature = "buffer_provider")]
+ (Buffer(a), Buffer(b)) => convert_buffer_provider(
+ icu_provider_adapters::fork::ForkByErrorProvider::new_with_predicate(
+ a,
+ b,
+ MissingLocalePredicate,
+ ),
+ ),
+ };
+ Ok(())
+ }
+
+ /// Enables locale fallbacking for data requests made to this provider.
+ ///
+ /// Note that the test provider (from `create_test`) already has fallbacking enabled.
+ #[diplomat::rust_link(
+ icu_provider_adapters::fallback::LocaleFallbackProvider::try_new,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu_provider_adapters::fallback::LocaleFallbackProvider,
+ Struct,
+ compact
+ )]
+ pub fn enable_locale_fallback(&mut self) -> Result<(), ICU4XError> {
+ use ICU4XDataProviderInner::*;
+ *self = match core::mem::replace(&mut self.0, Destroyed) {
+ Destroyed => Err(icu_provider::DataError::custom(
+ "This provider has been destroyed",
+ ))?,
+ #[cfg(feature = "compiled_data")]
+ Compiled => Err(icu_provider::DataError::custom(
+ "The compiled provider cannot be modified",
+ ))?,
+ Empty => Err(icu_provider::DataErrorKind::MissingDataKey.into_error())?,
+ #[cfg(feature = "buffer_provider")]
+ Buffer(inner) => convert_buffer_provider(
+ LocaleFallbackProvider::try_new_with_buffer_provider(inner)?,
+ ),
+ };
+ Ok(())
+ }
+
+ #[diplomat::rust_link(
+ icu_provider_adapters::fallback::LocaleFallbackProvider::new_with_fallbacker,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu_provider_adapters::fallback::LocaleFallbackProvider,
+ Struct,
+ compact
+ )]
+ #[allow(unused_variables)] // feature-gated
+ #[cfg(feature = "icu_locid_transform")]
+ pub fn enable_locale_fallback_with(
+ &mut self,
+ fallbacker: &crate::fallbacker::ffi::ICU4XLocaleFallbacker,
+ ) -> Result<(), ICU4XError> {
+ use ICU4XDataProviderInner::*;
+ *self = match core::mem::replace(&mut self.0, Destroyed) {
+ Destroyed => Err(icu_provider::DataError::custom(
+ "This provider has been destroyed",
+ ))?,
+ #[cfg(feature = "compiled_data")]
+ Compiled => Err(icu_provider::DataError::custom(
+ "The compiled provider cannot be modified",
+ ))?,
+ Empty => Err(icu_provider::DataErrorKind::MissingDataKey.into_error())?,
+ #[cfg(feature = "buffer_provider")]
+ Buffer(inner) => convert_buffer_provider(
+ LocaleFallbackProvider::new_with_fallbacker(inner, fallbacker.0.clone()),
+ ),
+ };
+ Ok(())
+ }
+ }
+}
+
+macro_rules! load {
+ () => {
+ fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> {
+ use ICU4XDataProviderInner::*;
+ match self {
+ Destroyed => Err(icu_provider::DataError::custom(
+ "This provider has been destroyed",
+ ))?,
+ Empty => EmptyDataProvider::new().load(req),
+ #[cfg(feature = "buffer_provider")]
+ Buffer(buffer_provider) => buffer_provider.as_deserializing().load(req),
+ #[cfg(feature = "compiled_data")]
+ Compiled => unreachable!(),
+ }
+ }
+ };
+}
+
+#[macro_export]
+macro_rules! call_constructor {
+ ($compiled:path [$pre_transform:ident => $transform:expr], $any:path, $buffer:path, $provider:expr $(, $args:expr)* $(,)?) => {
+ match &$provider.0 {
+ $crate::provider::ICU4XDataProviderInner::Destroyed => Err(icu_provider::DataError::custom(
+ "This provider has been destroyed",
+ ))?,
+ $crate::provider::ICU4XDataProviderInner::Empty => $any(&icu_provider_adapters::empty::EmptyDataProvider::new(), $($args,)*),
+ #[cfg(feature = "buffer_provider")]
+ $crate::provider::ICU4XDataProviderInner::Buffer(buffer_provider) => $buffer(buffer_provider, $($args,)*),
+ #[cfg(feature = "compiled_data")]
+ $crate::provider::ICU4XDataProviderInner::Compiled => { let $pre_transform = $compiled($($args,)*); $transform },
+ }
+ };
+ ($compiled:path, $any:path, $buffer:path, $provider:expr $(, $args:expr)* $(,)?) => {
+ match &$provider.0 {
+ $crate::provider::ICU4XDataProviderInner::Destroyed => Err(icu_provider::DataError::custom(
+ "This provider has been destroyed",
+ ))?,
+ $crate::provider::ICU4XDataProviderInner::Empty => $any(&icu_provider_adapters::empty::EmptyDataProvider::new(), $($args,)*),
+ #[cfg(feature = "buffer_provider")]
+ $crate::provider::ICU4XDataProviderInner::Buffer(buffer_provider) => $buffer(buffer_provider, $($args,)*),
+ #[cfg(feature = "compiled_data")]
+ $crate::provider::ICU4XDataProviderInner::Compiled => $compiled($($args,)*),
+ }
+ };
+}
+
+#[macro_export]
+macro_rules! call_constructor_unstable {
+ ($compiled:path [$pre_transform:ident => $transform:expr], $unstable:path, $provider:expr $(, $args:expr)* $(,)?) => {
+ match &$provider.0 {
+ $crate::provider::ICU4XDataProviderInner::Destroyed => Err(icu_provider::DataError::custom(
+ "This provider has been destroyed",
+ ))?,
+ $crate::provider::ICU4XDataProviderInner::Empty => $unstable(&icu_provider_adapters::empty::EmptyDataProvider::new(), $($args,)*),
+ #[cfg(feature = "buffer_provider")]
+ $crate::provider::ICU4XDataProviderInner::Buffer(buffer_provider) => $unstable(&icu_provider::AsDeserializingBufferProvider::as_deserializing(buffer_provider), $($args,)*),
+ #[cfg(feature = "compiled_data")]
+ $crate::provider::ICU4XDataProviderInner::Compiled => { let $pre_transform = $compiled($($args,)*); $transform },
+ }
+ };
+ ($compiled:path, $unstable:path, $provider:expr $(, $args:expr)* $(,)?) => {
+ match &$provider.0 {
+ $crate::provider::ICU4XDataProviderInner::Destroyed => Err(icu_provider::DataError::custom(
+ "This provider has been destroyed",
+ ))?,
+ $crate::provider::ICU4XDataProviderInner::Empty => $unstable(&icu_provider_adapters::empty::EmptyDataProvider::new(), $($args,)*),
+ #[cfg(feature = "buffer_provider")]
+ $crate::provider::ICU4XDataProviderInner::Buffer(buffer_provider) => $unstable(&icu_provider::AsDeserializingBufferProvider::as_deserializing(buffer_provider), $($args,)*),
+ #[cfg(feature = "compiled_data")]
+ $crate::provider::ICU4XDataProviderInner::Compiled => $compiled($($args,)*),
+ }
+ };
+}
+
+#[cfg(not(feature = "buffer_provider"))]
+impl<M> DataProvider<M> for ICU4XDataProviderInner
+where
+ M: KeyedDataMarker,
+{
+ load!();
+}
+
+#[cfg(feature = "buffer_provider")]
+impl<M> DataProvider<M> for ICU4XDataProviderInner
+where
+ M: KeyedDataMarker,
+ // Actual bound:
+ // for<'de> <M::Yokeable as Yokeable<'de>>::Output: Deserialize<'de>,
+ // Necessary workaround bound (see `yoke::trait_hack` docs):
+ for<'de> YokeTraitHack<<M::Yokeable as Yokeable<'de>>::Output>: serde::Deserialize<'de>,
+{
+ load!();
+}
diff --git a/intl/icu_capi/src/script.rs b/intl/icu_capi/src/script.rs
new file mode 100644
index 0000000000..aeb7e8bdde
--- /dev/null
+++ b/intl/icu_capi/src/script.rs
@@ -0,0 +1,156 @@
+// 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]
+pub mod ffi {
+ use crate::provider::ffi::ICU4XDataProvider;
+ use alloc::boxed::Box;
+ use icu_properties::{script, sets::CodePointSetData, Script};
+
+ use crate::errors::ffi::ICU4XError;
+ use crate::properties_iter::ffi::CodePointRangeIterator;
+ use crate::properties_sets::ffi::ICU4XCodePointSetData;
+
+ #[diplomat::opaque]
+ /// An ICU4X ScriptWithExtensions map object, capable of holding a map of codepoints to scriptextensions values
+ #[diplomat::rust_link(icu::properties::script::ScriptWithExtensions, Struct)]
+ #[diplomat::rust_link(
+ icu::properties::script::ScriptWithExtensions::from_data,
+ FnInStruct,
+ hidden
+ )]
+ pub struct ICU4XScriptWithExtensions(pub script::ScriptWithExtensions);
+
+ #[diplomat::opaque]
+ /// A slightly faster ICU4XScriptWithExtensions object
+ #[diplomat::rust_link(icu::properties::script::ScriptWithExtensionsBorrowed, Struct)]
+ pub struct ICU4XScriptWithExtensionsBorrowed<'a>(pub script::ScriptWithExtensionsBorrowed<'a>);
+ #[diplomat::opaque]
+ /// An object that represents the Script_Extensions property for a single character
+ #[diplomat::rust_link(icu::properties::script::ScriptExtensionsSet, Struct)]
+ pub struct ICU4XScriptExtensionsSet<'a>(pub script::ScriptExtensionsSet<'a>);
+
+ impl ICU4XScriptWithExtensions {
+ #[diplomat::rust_link(icu::properties::script::script_with_extensions, Fn)]
+ pub fn create(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XScriptWithExtensions>, ICU4XError> {
+ Ok(Box::new(ICU4XScriptWithExtensions(call_constructor!(
+ script::script_with_extensions [r => Ok(r.static_to_owned())],
+ script::load_script_with_extensions_with_any_provider,
+ script::load_script_with_extensions_with_buffer_provider,
+ provider
+ )?)))
+ }
+
+ /// Get the Script property value for a code point
+ #[diplomat::rust_link(
+ icu::properties::script::ScriptWithExtensionsBorrowed::get_script_val,
+ FnInStruct
+ )]
+ pub fn get_script_val(&self, code_point: u32) -> u16 {
+ self.0.as_borrowed().get_script_val(code_point).0
+ }
+
+ /// Check if the Script_Extensions property of the given code point covers the given script
+ #[diplomat::rust_link(
+ icu::properties::script::ScriptWithExtensionsBorrowed::has_script,
+ FnInStruct
+ )]
+ pub fn has_script(&self, code_point: u32, script: u16) -> bool {
+ self.0.as_borrowed().has_script(code_point, Script(script))
+ }
+
+ /// Borrow this object for a slightly faster variant with more operations
+ #[diplomat::rust_link(
+ icu::properties::script::ScriptWithExtensions::as_borrowed,
+ FnInStruct
+ )]
+ pub fn as_borrowed<'a>(&'a self) -> Box<ICU4XScriptWithExtensionsBorrowed<'a>> {
+ Box::new(ICU4XScriptWithExtensionsBorrowed(self.0.as_borrowed()))
+ }
+
+ /// Get a list of ranges of code points that contain this script in their Script_Extensions values
+ #[diplomat::rust_link(
+ icu::properties::script::ScriptWithExtensionsBorrowed::get_script_extensions_ranges,
+ FnInStruct
+ )]
+ pub fn iter_ranges_for_script<'a>(
+ &'a self,
+ script: u16,
+ ) -> Box<CodePointRangeIterator<'a>> {
+ Box::new(CodePointRangeIterator(Box::new(
+ self.0
+ .as_borrowed()
+ .get_script_extensions_ranges(Script(script)),
+ )))
+ }
+ }
+
+ impl<'a> ICU4XScriptWithExtensionsBorrowed<'a> {
+ /// Get the Script property value for a code point
+ #[diplomat::rust_link(
+ icu::properties::script::ScriptWithExtensionsBorrowed::get_script_val,
+ FnInStruct
+ )]
+ pub fn get_script_val(&self, code_point: u32) -> u16 {
+ self.0.get_script_val(code_point).0
+ }
+ /// Get the Script property value for a code point
+ #[diplomat::rust_link(
+ icu::properties::script::ScriptWithExtensionsBorrowed::get_script_extensions_val,
+ FnInStruct
+ )]
+ pub fn get_script_extensions_val(
+ &self,
+ code_point: u32,
+ ) -> Box<ICU4XScriptExtensionsSet<'a>> {
+ Box::new(ICU4XScriptExtensionsSet(
+ self.0.get_script_extensions_val(code_point),
+ ))
+ }
+ /// Check if the Script_Extensions property of the given code point covers the given script
+ #[diplomat::rust_link(
+ icu::properties::script::ScriptWithExtensionsBorrowed::has_script,
+ FnInStruct
+ )]
+ pub fn has_script(&self, code_point: u32, script: u16) -> bool {
+ self.0.has_script(code_point, Script(script))
+ }
+
+ /// Build the CodePointSetData corresponding to a codepoints matching a particular script
+ /// in their Script_Extensions
+ #[diplomat::rust_link(
+ icu::properties::script::ScriptWithExtensionsBorrowed::get_script_extensions_set,
+ FnInStruct
+ )]
+ pub fn get_script_extensions_set(&self, script: u16) -> Box<ICU4XCodePointSetData> {
+ let list = self
+ .0
+ .get_script_extensions_set(Script(script))
+ .into_owned();
+ let set = CodePointSetData::from_code_point_inversion_list(list);
+ Box::new(ICU4XCodePointSetData(set))
+ }
+ }
+ impl<'a> ICU4XScriptExtensionsSet<'a> {
+ /// Check if the Script_Extensions property of the given code point covers the given script
+ #[diplomat::rust_link(icu::properties::script::ScriptExtensionsSet::contains, FnInStruct)]
+ pub fn contains(&self, script: u16) -> bool {
+ self.0.contains(&Script(script))
+ }
+
+ /// Get the number of scripts contained in here
+ #[diplomat::rust_link(icu::properties::script::ScriptExtensionsSet::iter, FnInStruct)]
+ pub fn count(&self) -> usize {
+ self.0.array_len()
+ }
+
+ /// Get script at index, returning an error if out of bounds
+ #[diplomat::rust_link(icu::properties::script::ScriptExtensionsSet::iter, FnInStruct)]
+ pub fn script_at(&self, index: usize) -> Result<u16, ()> {
+ self.0.array_get(index).map(|x| x.0).ok_or(())
+ }
+ }
+}
diff --git a/intl/icu_capi/src/segmenter_grapheme.rs b/intl/icu_capi/src/segmenter_grapheme.rs
new file mode 100644
index 0000000000..5d3f65fc05
--- /dev/null
+++ b/intl/icu_capi/src/segmenter_grapheme.rs
@@ -0,0 +1,154 @@
+// 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]
+pub mod ffi {
+ use crate::errors::ffi::ICU4XError;
+ use crate::provider::ffi::ICU4XDataProvider;
+ use alloc::boxed::Box;
+ use core::convert::TryFrom;
+ use icu_segmenter::{
+ GraphemeClusterBreakIteratorLatin1, GraphemeClusterBreakIteratorPotentiallyIllFormedUtf8,
+ GraphemeClusterBreakIteratorUtf16, GraphemeClusterSegmenter,
+ };
+
+ #[diplomat::opaque]
+ /// An ICU4X grapheme-cluster-break segmenter, capable of finding grapheme cluster breakpoints
+ /// in strings.
+ #[diplomat::rust_link(icu::segmenter::GraphemeClusterSegmenter, Struct)]
+ pub struct ICU4XGraphemeClusterSegmenter(GraphemeClusterSegmenter);
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::segmenter::GraphemeClusterBreakIterator, Struct)]
+ #[diplomat::rust_link(
+ icu::segmenter::GraphemeClusterBreakIteratorPotentiallyIllFormedUtf8,
+ Typedef,
+ hidden
+ )]
+ pub struct ICU4XGraphemeClusterBreakIteratorUtf8<'a>(
+ GraphemeClusterBreakIteratorPotentiallyIllFormedUtf8<'a, 'a>,
+ );
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::segmenter::GraphemeClusterBreakIterator, Struct)]
+ #[diplomat::rust_link(icu::segmenter::GraphemeClusterBreakIteratorUtf16, Typedef, hidden)]
+ pub struct ICU4XGraphemeClusterBreakIteratorUtf16<'a>(
+ GraphemeClusterBreakIteratorUtf16<'a, 'a>,
+ );
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::segmenter::GraphemeClusterBreakIterator, Struct)]
+ #[diplomat::rust_link(icu::segmenter::GraphemeClusterBreakIteratorLatin1, Typedef, hidden)]
+ pub struct ICU4XGraphemeClusterBreakIteratorLatin1<'a>(
+ GraphemeClusterBreakIteratorLatin1<'a, 'a>,
+ );
+
+ impl ICU4XGraphemeClusterSegmenter {
+ /// Construct an [`ICU4XGraphemeClusterSegmenter`].
+ #[diplomat::rust_link(icu::segmenter::GraphemeClusterSegmenter::new, FnInStruct)]
+ pub fn create(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XGraphemeClusterSegmenter>, ICU4XError> {
+ Ok(Box::new(ICU4XGraphemeClusterSegmenter(call_constructor!(
+ GraphemeClusterSegmenter::new [r => Ok(r)],
+ GraphemeClusterSegmenter::try_new_with_any_provider,
+ GraphemeClusterSegmenter::try_new_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Segments a (potentially ill-formed) UTF-8 string.
+ #[diplomat::rust_link(
+ icu::segmenter::GraphemeClusterSegmenter::segment_str,
+ FnInStruct,
+ hidden
+ )]
+ #[diplomat::rust_link(icu::segmenter::GraphemeClusterSegmenter::segment_utf8, FnInStruct)]
+ pub fn segment_utf8<'a>(
+ &'a self,
+ input: &'a str,
+ ) -> Box<ICU4XGraphemeClusterBreakIteratorUtf8<'a>> {
+ let input = input.as_bytes(); // #2520
+ Box::new(ICU4XGraphemeClusterBreakIteratorUtf8(
+ self.0.segment_utf8(input),
+ ))
+ }
+
+ /// Segments a UTF-16 string.
+ #[diplomat::rust_link(icu::segmenter::GraphemeClusterSegmenter::segment_utf16, FnInStruct)]
+ pub fn segment_utf16<'a>(
+ &'a self,
+ input: &'a [u16],
+ ) -> Box<ICU4XGraphemeClusterBreakIteratorUtf16<'a>> {
+ Box::new(ICU4XGraphemeClusterBreakIteratorUtf16(
+ self.0.segment_utf16(input),
+ ))
+ }
+
+ /// Segments a Latin-1 string.
+ #[diplomat::rust_link(icu::segmenter::GraphemeClusterSegmenter::segment_latin1, FnInStruct)]
+ pub fn segment_latin1<'a>(
+ &'a self,
+ input: &'a [u8],
+ ) -> Box<ICU4XGraphemeClusterBreakIteratorLatin1<'a>> {
+ Box::new(ICU4XGraphemeClusterBreakIteratorLatin1(
+ self.0.segment_latin1(input),
+ ))
+ }
+ }
+
+ impl<'a> ICU4XGraphemeClusterBreakIteratorUtf8<'a> {
+ /// Finds the next breakpoint. Returns -1 if at the end of the string or if the index is
+ /// out of range of a 32-bit signed integer.
+ #[allow(clippy::should_implement_trait)]
+ #[diplomat::rust_link(icu::segmenter::GraphemeClusterBreakIterator::next, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::segmenter::GraphemeClusterBreakIterator::Item,
+ AssociatedTypeInStruct,
+ hidden
+ )]
+ pub fn next(&mut self) -> i32 {
+ self.0
+ .next()
+ .and_then(|u| i32::try_from(u).ok())
+ .unwrap_or(-1)
+ }
+ }
+
+ impl<'a> ICU4XGraphemeClusterBreakIteratorUtf16<'a> {
+ /// Finds the next breakpoint. Returns -1 if at the end of the string or if the index is
+ /// out of range of a 32-bit signed integer.
+ #[allow(clippy::should_implement_trait)]
+ #[diplomat::rust_link(icu::segmenter::GraphemeClusterBreakIterator::next, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::segmenter::GraphemeClusterBreakIterator::Item,
+ AssociatedTypeInStruct,
+ hidden
+ )]
+ pub fn next(&mut self) -> i32 {
+ self.0
+ .next()
+ .and_then(|u| i32::try_from(u).ok())
+ .unwrap_or(-1)
+ }
+ }
+
+ impl<'a> ICU4XGraphemeClusterBreakIteratorLatin1<'a> {
+ /// Finds the next breakpoint. Returns -1 if at the end of the string or if the index is
+ /// out of range of a 32-bit signed integer.
+ #[allow(clippy::should_implement_trait)]
+ #[diplomat::rust_link(icu::segmenter::GraphemeClusterBreakIterator::next, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::segmenter::GraphemeClusterBreakIterator::Item,
+ AssociatedTypeInStruct,
+ hidden
+ )]
+ pub fn next(&mut self) -> i32 {
+ self.0
+ .next()
+ .and_then(|u| i32::try_from(u).ok())
+ .unwrap_or(-1)
+ }
+ }
+}
diff --git a/intl/icu_capi/src/segmenter_line.rs b/intl/icu_capi/src/segmenter_line.rs
new file mode 100644
index 0000000000..81aa9b49ff
--- /dev/null
+++ b/intl/icu_capi/src/segmenter_line.rs
@@ -0,0 +1,271 @@
+// 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 icu_segmenter::LineBreakOptions;
+use icu_segmenter::LineBreakStrictness;
+use icu_segmenter::LineBreakWordOption;
+
+#[diplomat::bridge]
+pub mod ffi {
+ use crate::errors::ffi::ICU4XError;
+ use crate::provider::ffi::ICU4XDataProvider;
+ use alloc::boxed::Box;
+ use core::convert::TryFrom;
+ use icu_segmenter::{
+ LineBreakIteratorLatin1, LineBreakIteratorPotentiallyIllFormedUtf8, LineBreakIteratorUtf16,
+ LineSegmenter,
+ };
+
+ #[diplomat::opaque]
+ /// An ICU4X line-break segmenter, capable of finding breakpoints in strings.
+ #[diplomat::rust_link(icu::segmenter::LineSegmenter, Struct)]
+ pub struct ICU4XLineSegmenter(LineSegmenter);
+
+ #[diplomat::rust_link(icu::segmenter::LineBreakStrictness, Enum)]
+ pub enum ICU4XLineBreakStrictness {
+ Loose,
+ Normal,
+ Strict,
+ Anywhere,
+ }
+
+ #[diplomat::rust_link(icu::segmenter::LineBreakWordOption, Enum)]
+ pub enum ICU4XLineBreakWordOption {
+ Normal,
+ BreakAll,
+ KeepAll,
+ }
+
+ #[diplomat::rust_link(icu::segmenter::LineBreakOptions, Struct)]
+ pub struct ICU4XLineBreakOptionsV1 {
+ pub strictness: ICU4XLineBreakStrictness,
+ pub word_option: ICU4XLineBreakWordOption,
+ pub ja_zh: bool,
+ }
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::segmenter::LineBreakIterator, Struct)]
+ #[diplomat::rust_link(
+ icu::segmenter::LineBreakIteratorPotentiallyIllFormedUtf8,
+ Typedef,
+ compact
+ )]
+ pub struct ICU4XLineBreakIteratorUtf8<'a>(LineBreakIteratorPotentiallyIllFormedUtf8<'a, 'a>);
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::segmenter::LineBreakIterator, Struct)]
+ #[diplomat::rust_link(icu::segmenter::LineBreakIteratorUtf16, Typedef, compact)]
+ pub struct ICU4XLineBreakIteratorUtf16<'a>(LineBreakIteratorUtf16<'a, 'a>);
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::segmenter::LineBreakIterator, Struct)]
+ #[diplomat::rust_link(icu::segmenter::LineBreakIteratorLatin1, Typedef, compact)]
+ pub struct ICU4XLineBreakIteratorLatin1<'a>(LineBreakIteratorLatin1<'a, 'a>);
+
+ impl ICU4XLineSegmenter {
+ /// Construct a [`ICU4XLineSegmenter`] with default options. It automatically loads the best
+ /// available payload data for Burmese, Khmer, Lao, and Thai.
+ #[diplomat::rust_link(icu::segmenter::LineSegmenter::new_auto, FnInStruct)]
+ pub fn create_auto(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XLineSegmenter>, ICU4XError> {
+ Ok(Box::new(ICU4XLineSegmenter(call_constructor!(
+ LineSegmenter::new_auto [r => Ok(r)],
+ LineSegmenter::try_new_auto_with_any_provider,
+ LineSegmenter::try_new_auto_with_buffer_provider,
+ provider
+ )?)))
+ }
+
+ /// Construct a [`ICU4XLineSegmenter`] with default options and LSTM payload data for
+ /// Burmese, Khmer, Lao, and Thai.
+ #[diplomat::rust_link(icu::segmenter::LineSegmenter::new_lstm, FnInStruct)]
+ pub fn create_lstm(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XLineSegmenter>, ICU4XError> {
+ Ok(Box::new(ICU4XLineSegmenter(call_constructor!(
+ LineSegmenter::new_lstm [r => Ok(r)],
+ LineSegmenter::try_new_lstm_with_any_provider,
+ LineSegmenter::try_new_lstm_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Construct a [`ICU4XLineSegmenter`] with default options and dictionary payload data for
+ /// Burmese, Khmer, Lao, and Thai..
+ #[diplomat::rust_link(icu::segmenter::LineSegmenter::new_dictionary, FnInStruct)]
+ pub fn create_dictionary(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XLineSegmenter>, ICU4XError> {
+ Ok(Box::new(ICU4XLineSegmenter(call_constructor!(
+ LineSegmenter::new_dictionary [r => Ok(r)],
+ LineSegmenter::try_new_dictionary_with_any_provider,
+ LineSegmenter::try_new_dictionary_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Construct a [`ICU4XLineSegmenter`] with custom options. It automatically loads the best
+ /// available payload data for Burmese, Khmer, Lao, and Thai.
+ #[diplomat::rust_link(icu::segmenter::LineSegmenter::new_auto_with_options, FnInStruct)]
+ pub fn create_auto_with_options_v1(
+ provider: &ICU4XDataProvider,
+ options: ICU4XLineBreakOptionsV1,
+ ) -> Result<Box<ICU4XLineSegmenter>, ICU4XError> {
+ Ok(Box::new(ICU4XLineSegmenter(call_constructor!(
+ LineSegmenter::new_auto_with_options [r => Ok(r)],
+ LineSegmenter::try_new_auto_with_options_with_any_provider,
+ LineSegmenter::try_new_auto_with_options_with_buffer_provider,
+ provider,
+ options.into(),
+ )?)))
+ }
+
+ /// Construct a [`ICU4XLineSegmenter`] with custom options and LSTM payload data for
+ /// Burmese, Khmer, Lao, and Thai.
+ #[diplomat::rust_link(icu::segmenter::LineSegmenter::new_lstm_with_options, FnInStruct)]
+ pub fn create_lstm_with_options_v1(
+ provider: &ICU4XDataProvider,
+ options: ICU4XLineBreakOptionsV1,
+ ) -> Result<Box<ICU4XLineSegmenter>, ICU4XError> {
+ Ok(Box::new(ICU4XLineSegmenter(call_constructor!(
+ LineSegmenter::new_lstm_with_options [r => Ok(r)],
+ LineSegmenter::try_new_lstm_with_options_with_any_provider,
+ LineSegmenter::try_new_lstm_with_options_with_buffer_provider,
+ provider,
+ options.into(),
+ )?)))
+ }
+
+ /// Construct a [`ICU4XLineSegmenter`] with custom options and dictionary payload data for
+ /// Burmese, Khmer, Lao, and Thai.
+ #[diplomat::rust_link(
+ icu::segmenter::LineSegmenter::new_dictionary_with_options,
+ FnInStruct
+ )]
+ pub fn create_dictionary_with_options_v1(
+ provider: &ICU4XDataProvider,
+ options: ICU4XLineBreakOptionsV1,
+ ) -> Result<Box<ICU4XLineSegmenter>, ICU4XError> {
+ Ok(Box::new(ICU4XLineSegmenter(call_constructor!(
+ LineSegmenter::new_dictionary_with_options [r => Ok(r)],
+ LineSegmenter::try_new_dictionary_with_options_with_any_provider,
+ LineSegmenter::try_new_dictionary_with_options_with_buffer_provider,
+ provider,
+ options.into(),
+ )?)))
+ }
+
+ /// Segments a (potentially ill-formed) UTF-8 string.
+ #[diplomat::rust_link(icu::segmenter::LineSegmenter::segment_utf8, FnInStruct)]
+ #[diplomat::rust_link(icu::segmenter::LineSegmenter::segment_str, FnInStruct, hidden)]
+ pub fn segment_utf8<'a>(&'a self, input: &'a str) -> Box<ICU4XLineBreakIteratorUtf8<'a>> {
+ let input = input.as_bytes(); // #2520
+ Box::new(ICU4XLineBreakIteratorUtf8(self.0.segment_utf8(input)))
+ }
+
+ /// Segments a UTF-16 string.
+ #[diplomat::rust_link(icu::segmenter::LineSegmenter::segment_utf16, FnInStruct)]
+ pub fn segment_utf16<'a>(
+ &'a self,
+ input: &'a [u16],
+ ) -> Box<ICU4XLineBreakIteratorUtf16<'a>> {
+ Box::new(ICU4XLineBreakIteratorUtf16(self.0.segment_utf16(input)))
+ }
+
+ /// Segments a Latin-1 string.
+ #[diplomat::rust_link(icu::segmenter::LineSegmenter::segment_latin1, FnInStruct)]
+ pub fn segment_latin1<'a>(
+ &'a self,
+ input: &'a [u8],
+ ) -> Box<ICU4XLineBreakIteratorLatin1<'a>> {
+ Box::new(ICU4XLineBreakIteratorLatin1(self.0.segment_latin1(input)))
+ }
+ }
+
+ impl<'a> ICU4XLineBreakIteratorUtf8<'a> {
+ /// Finds the next breakpoint. Returns -1 if at the end of the string or if the index is
+ /// out of range of a 32-bit signed integer.
+ #[allow(clippy::should_implement_trait)]
+ #[diplomat::rust_link(icu::segmenter::LineBreakIterator::next, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::segmenter::LineBreakIterator::Item,
+ AssociatedTypeInStruct,
+ hidden
+ )]
+ pub fn next(&mut self) -> i32 {
+ self.0
+ .next()
+ .and_then(|u| i32::try_from(u).ok())
+ .unwrap_or(-1)
+ }
+ }
+
+ impl<'a> ICU4XLineBreakIteratorUtf16<'a> {
+ /// Finds the next breakpoint. Returns -1 if at the end of the string or if the index is
+ /// out of range of a 32-bit signed integer.
+ #[allow(clippy::should_implement_trait)]
+ #[diplomat::rust_link(icu::segmenter::LineBreakIterator::next, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::segmenter::LineBreakIterator::Item,
+ AssociatedTypeInStruct,
+ hidden
+ )]
+ pub fn next(&mut self) -> i32 {
+ self.0
+ .next()
+ .and_then(|u| i32::try_from(u).ok())
+ .unwrap_or(-1)
+ }
+ }
+
+ impl<'a> ICU4XLineBreakIteratorLatin1<'a> {
+ /// Finds the next breakpoint. Returns -1 if at the end of the string or if the index is
+ /// out of range of a 32-bit signed integer.
+ #[allow(clippy::should_implement_trait)]
+ #[diplomat::rust_link(icu::segmenter::LineBreakIterator::next, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::segmenter::LineBreakIterator::Item,
+ AssociatedTypeInStruct,
+ hidden
+ )]
+ pub fn next(&mut self) -> i32 {
+ self.0
+ .next()
+ .and_then(|u| i32::try_from(u).ok())
+ .unwrap_or(-1)
+ }
+ }
+}
+
+impl From<ffi::ICU4XLineBreakStrictness> for LineBreakStrictness {
+ fn from(other: ffi::ICU4XLineBreakStrictness) -> Self {
+ match other {
+ ffi::ICU4XLineBreakStrictness::Loose => Self::Loose,
+ ffi::ICU4XLineBreakStrictness::Normal => Self::Normal,
+ ffi::ICU4XLineBreakStrictness::Strict => Self::Strict,
+ ffi::ICU4XLineBreakStrictness::Anywhere => Self::Anywhere,
+ }
+ }
+}
+
+impl From<ffi::ICU4XLineBreakWordOption> for LineBreakWordOption {
+ fn from(other: ffi::ICU4XLineBreakWordOption) -> Self {
+ match other {
+ ffi::ICU4XLineBreakWordOption::Normal => Self::Normal,
+ ffi::ICU4XLineBreakWordOption::BreakAll => Self::BreakAll,
+ ffi::ICU4XLineBreakWordOption::KeepAll => Self::KeepAll,
+ }
+ }
+}
+
+impl From<ffi::ICU4XLineBreakOptionsV1> for LineBreakOptions {
+ fn from(other: ffi::ICU4XLineBreakOptionsV1) -> Self {
+ let mut options = LineBreakOptions::default();
+ options.strictness = other.strictness.into();
+ options.word_option = other.word_option.into();
+ options.ja_zh = other.ja_zh;
+ options
+ }
+}
diff --git a/intl/icu_capi/src/segmenter_sentence.rs b/intl/icu_capi/src/segmenter_sentence.rs
new file mode 100644
index 0000000000..be01e0da8c
--- /dev/null
+++ b/intl/icu_capi/src/segmenter_sentence.rs
@@ -0,0 +1,141 @@
+// 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]
+pub mod ffi {
+ use crate::errors::ffi::ICU4XError;
+ use crate::provider::ffi::ICU4XDataProvider;
+ use alloc::boxed::Box;
+ use core::convert::TryFrom;
+ use icu_segmenter::{
+ SentenceBreakIteratorLatin1, SentenceBreakIteratorPotentiallyIllFormedUtf8,
+ SentenceBreakIteratorUtf16, SentenceSegmenter,
+ };
+
+ #[diplomat::opaque]
+ /// An ICU4X sentence-break segmenter, capable of finding sentence breakpoints in strings.
+ #[diplomat::rust_link(icu::segmenter::SentenceSegmenter, Struct)]
+ pub struct ICU4XSentenceSegmenter(SentenceSegmenter);
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::segmenter::SentenceBreakIterator, Struct)]
+ #[diplomat::rust_link(
+ icu::segmenter::SentenceBreakIteratorPotentiallyIllFormedUtf8,
+ Typedef,
+ hidden
+ )]
+ pub struct ICU4XSentenceBreakIteratorUtf8<'a>(
+ SentenceBreakIteratorPotentiallyIllFormedUtf8<'a, 'a>,
+ );
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::segmenter::SentenceBreakIterator, Struct)]
+ #[diplomat::rust_link(icu::segmenter::SentenceBreakIteratorUtf16, Typedef, hidden)]
+ pub struct ICU4XSentenceBreakIteratorUtf16<'a>(SentenceBreakIteratorUtf16<'a, 'a>);
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::segmenter::SentenceBreakIterator, Struct)]
+ #[diplomat::rust_link(icu::segmenter::SentenceBreakIteratorLatin1, Typedef, hidden)]
+ pub struct ICU4XSentenceBreakIteratorLatin1<'a>(SentenceBreakIteratorLatin1<'a, 'a>);
+
+ impl ICU4XSentenceSegmenter {
+ /// Construct an [`ICU4XSentenceSegmenter`].
+ #[diplomat::rust_link(icu::segmenter::SentenceSegmenter::new, FnInStruct)]
+ pub fn create(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XSentenceSegmenter>, ICU4XError> {
+ Ok(Box::new(ICU4XSentenceSegmenter(call_constructor!(
+ SentenceSegmenter::new [r => Ok(r)],
+ SentenceSegmenter::try_new_with_any_provider,
+ SentenceSegmenter::try_new_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Segments a (potentially ill-formed) UTF-8 string.
+ #[diplomat::rust_link(icu::segmenter::SentenceSegmenter::segment_utf8, FnInStruct)]
+ #[diplomat::rust_link(icu::segmenter::SentenceSegmenter::segment_str, FnInStruct, hidden)]
+ pub fn segment_utf8<'a>(
+ &'a self,
+ input: &'a str,
+ ) -> Box<ICU4XSentenceBreakIteratorUtf8<'a>> {
+ let input = input.as_bytes(); // #2520
+ Box::new(ICU4XSentenceBreakIteratorUtf8(self.0.segment_utf8(input)))
+ }
+
+ /// Segments a UTF-16 string.
+ #[diplomat::rust_link(icu::segmenter::SentenceSegmenter::segment_utf16, FnInStruct)]
+ pub fn segment_utf16<'a>(
+ &'a self,
+ input: &'a [u16],
+ ) -> Box<ICU4XSentenceBreakIteratorUtf16<'a>> {
+ Box::new(ICU4XSentenceBreakIteratorUtf16(self.0.segment_utf16(input)))
+ }
+
+ /// Segments a Latin-1 string.
+ #[diplomat::rust_link(icu::segmenter::SentenceSegmenter::segment_latin1, FnInStruct)]
+ pub fn segment_latin1<'a>(
+ &'a self,
+ input: &'a [u8],
+ ) -> Box<ICU4XSentenceBreakIteratorLatin1<'a>> {
+ Box::new(ICU4XSentenceBreakIteratorLatin1(
+ self.0.segment_latin1(input),
+ ))
+ }
+ }
+
+ impl<'a> ICU4XSentenceBreakIteratorUtf8<'a> {
+ /// Finds the next breakpoint. Returns -1 if at the end of the string or if the index is
+ /// out of range of a 32-bit signed integer.
+ #[allow(clippy::should_implement_trait)]
+ #[diplomat::rust_link(icu::segmenter::SentenceBreakIterator::next, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::segmenter::SentenceBreakIterator::Item,
+ AssociatedTypeInStruct,
+ hidden
+ )]
+ pub fn next(&mut self) -> i32 {
+ self.0
+ .next()
+ .and_then(|u| i32::try_from(u).ok())
+ .unwrap_or(-1)
+ }
+ }
+
+ impl<'a> ICU4XSentenceBreakIteratorUtf16<'a> {
+ /// Finds the next breakpoint. Returns -1 if at the end of the string or if the index is
+ /// out of range of a 32-bit signed integer.
+ #[allow(clippy::should_implement_trait)]
+ #[diplomat::rust_link(icu::segmenter::SentenceBreakIterator::next, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::segmenter::SentenceBreakIterator::Item,
+ AssociatedTypeInStruct,
+ hidden
+ )]
+ pub fn next(&mut self) -> i32 {
+ self.0
+ .next()
+ .and_then(|u| i32::try_from(u).ok())
+ .unwrap_or(-1)
+ }
+ }
+
+ impl<'a> ICU4XSentenceBreakIteratorLatin1<'a> {
+ /// Finds the next breakpoint. Returns -1 if at the end of the string or if the index is
+ /// out of range of a 32-bit signed integer.
+ #[allow(clippy::should_implement_trait)]
+ #[diplomat::rust_link(icu::segmenter::SentenceBreakIterator::next, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::segmenter::SentenceBreakIterator::Item,
+ AssociatedTypeInStruct,
+ hidden
+ )]
+ pub fn next(&mut self) -> i32 {
+ self.0
+ .next()
+ .and_then(|u| i32::try_from(u).ok())
+ .unwrap_or(-1)
+ }
+ }
+}
diff --git a/intl/icu_capi/src/segmenter_word.rs b/intl/icu_capi/src/segmenter_word.rs
new file mode 100644
index 0000000000..226bcdd334
--- /dev/null
+++ b/intl/icu_capi/src/segmenter_word.rs
@@ -0,0 +1,213 @@
+// 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]
+pub mod ffi {
+ use crate::errors::ffi::ICU4XError;
+ use crate::provider::ffi::ICU4XDataProvider;
+ use alloc::boxed::Box;
+ use core::convert::TryFrom;
+ use icu_segmenter::{
+ WordBreakIteratorLatin1, WordBreakIteratorPotentiallyIllFormedUtf8, WordBreakIteratorUtf16,
+ WordSegmenter, WordType,
+ };
+
+ #[diplomat::enum_convert(WordType, needs_wildcard)]
+ #[diplomat::rust_link(icu::segmenter::WordType, Enum)]
+ pub enum ICU4XSegmenterWordType {
+ None = 0,
+ Number = 1,
+ Letter = 2,
+ }
+
+ #[diplomat::opaque]
+ /// An ICU4X word-break segmenter, capable of finding word breakpoints in strings.
+ #[diplomat::rust_link(icu::segmenter::WordSegmenter, Struct)]
+ pub struct ICU4XWordSegmenter(WordSegmenter);
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::segmenter::WordBreakIterator, Struct)]
+ #[diplomat::rust_link(
+ icu::segmenter::WordBreakIteratorPotentiallyIllFormedUtf8,
+ Typedef,
+ hidden
+ )]
+ pub struct ICU4XWordBreakIteratorUtf8<'a>(WordBreakIteratorPotentiallyIllFormedUtf8<'a, 'a>);
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::segmenter::WordBreakIterator, Struct)]
+ #[diplomat::rust_link(icu::segmenter::WordBreakIteratorUtf16, Typedef, hidden)]
+ pub struct ICU4XWordBreakIteratorUtf16<'a>(WordBreakIteratorUtf16<'a, 'a>);
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::segmenter::WordBreakIterator, Struct)]
+ #[diplomat::rust_link(icu::segmenter::WordBreakIteratorLatin1, Typedef, hidden)]
+ pub struct ICU4XWordBreakIteratorLatin1<'a>(WordBreakIteratorLatin1<'a, 'a>);
+
+ impl ICU4XWordSegmenter {
+ /// Construct an [`ICU4XWordSegmenter`] with automatically selecting the best available LSTM
+ /// or dictionary payload data.
+ ///
+ /// Note: currently, it uses dictionary for Chinese and Japanese, and LSTM for Burmese,
+ /// Khmer, Lao, and Thai.
+ #[diplomat::rust_link(icu::segmenter::WordSegmenter::new_auto, FnInStruct)]
+ pub fn create_auto(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XWordSegmenter>, ICU4XError> {
+ Ok(Box::new(ICU4XWordSegmenter(call_constructor!(
+ WordSegmenter::new_auto [r => Ok(r)],
+ WordSegmenter::try_new_auto_with_any_provider,
+ WordSegmenter::try_new_auto_with_buffer_provider,
+ provider
+ )?)))
+ }
+
+ /// Construct an [`ICU4XWordSegmenter`] with LSTM payload data for Burmese, Khmer, Lao, and
+ /// Thai.
+ ///
+ /// Warning: [`ICU4XWordSegmenter`] created by this function doesn't handle Chinese or
+ /// Japanese.
+ #[diplomat::rust_link(icu::segmenter::WordSegmenter::new_lstm, FnInStruct)]
+ pub fn create_lstm(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XWordSegmenter>, ICU4XError> {
+ Ok(Box::new(ICU4XWordSegmenter(call_constructor!(
+ WordSegmenter::new_lstm [r => Ok(r)],
+ WordSegmenter::try_new_lstm_with_any_provider,
+ WordSegmenter::try_new_lstm_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Construct an [`ICU4XWordSegmenter`] with dictionary payload data for Chinese, Japanese,
+ /// Burmese, Khmer, Lao, and Thai.
+ #[diplomat::rust_link(icu::segmenter::WordSegmenter::new_dictionary, FnInStruct)]
+ pub fn create_dictionary(
+ provider: &ICU4XDataProvider,
+ ) -> Result<Box<ICU4XWordSegmenter>, ICU4XError> {
+ Ok(Box::new(ICU4XWordSegmenter(call_constructor!(
+ WordSegmenter::new_dictionary [r => Ok(r)],
+ WordSegmenter::try_new_dictionary_with_any_provider,
+ WordSegmenter::try_new_dictionary_with_buffer_provider,
+ provider,
+ )?)))
+ }
+
+ /// Segments a (potentially ill-formed) UTF-8 string.
+ #[diplomat::rust_link(icu::segmenter::WordSegmenter::segment_utf8, FnInStruct)]
+ #[diplomat::rust_link(icu::segmenter::WordSegmenter::segment_str, FnInStruct, hidden)]
+ pub fn segment_utf8<'a>(&'a self, input: &'a str) -> Box<ICU4XWordBreakIteratorUtf8<'a>> {
+ let input = input.as_bytes(); // #2520
+ Box::new(ICU4XWordBreakIteratorUtf8(self.0.segment_utf8(input)))
+ }
+
+ /// Segments a UTF-16 string.
+ #[diplomat::rust_link(icu::segmenter::WordSegmenter::segment_utf16, FnInStruct)]
+ pub fn segment_utf16<'a>(
+ &'a self,
+ input: &'a [u16],
+ ) -> Box<ICU4XWordBreakIteratorUtf16<'a>> {
+ Box::new(ICU4XWordBreakIteratorUtf16(self.0.segment_utf16(input)))
+ }
+
+ /// Segments a Latin-1 string.
+ #[diplomat::rust_link(icu::segmenter::WordSegmenter::segment_latin1, FnInStruct)]
+ pub fn segment_latin1<'a>(
+ &'a self,
+ input: &'a [u8],
+ ) -> Box<ICU4XWordBreakIteratorLatin1<'a>> {
+ Box::new(ICU4XWordBreakIteratorLatin1(self.0.segment_latin1(input)))
+ }
+ }
+
+ impl<'a> ICU4XWordBreakIteratorUtf8<'a> {
+ /// Finds the next breakpoint. Returns -1 if at the end of the string or if the index is
+ /// out of range of a 32-bit signed integer.
+ #[allow(clippy::should_implement_trait)]
+ #[diplomat::rust_link(icu::segmenter::WordBreakIterator::next, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::segmenter::WordBreakIterator::Item,
+ AssociatedTypeInStruct,
+ hidden
+ )]
+ pub fn next(&mut self) -> i32 {
+ self.0
+ .next()
+ .and_then(|u| i32::try_from(u).ok())
+ .unwrap_or(-1)
+ }
+
+ /// Return the status value of break boundary.
+ #[diplomat::rust_link(icu::segmenter::WordBreakIterator::word_type, FnInStruct)]
+ pub fn word_type(&self) -> ICU4XSegmenterWordType {
+ self.0.word_type().into()
+ }
+
+ /// Return true when break boundary is word-like such as letter/number/CJK
+ #[diplomat::rust_link(icu::segmenter::WordBreakIterator::is_word_like, FnInStruct)]
+ pub fn is_word_like(&self) -> bool {
+ self.0.is_word_like()
+ }
+ }
+
+ impl<'a> ICU4XWordBreakIteratorUtf16<'a> {
+ /// Finds the next breakpoint. Returns -1 if at the end of the string or if the index is
+ /// out of range of a 32-bit signed integer.
+ #[allow(clippy::should_implement_trait)]
+ #[diplomat::rust_link(icu::segmenter::WordBreakIterator::next, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::segmenter::WordBreakIterator::Item,
+ AssociatedTypeInStruct,
+ hidden
+ )]
+ pub fn next(&mut self) -> i32 {
+ self.0
+ .next()
+ .and_then(|u| i32::try_from(u).ok())
+ .unwrap_or(-1)
+ }
+
+ /// Return the status value of break boundary.
+ #[diplomat::rust_link(icu::segmenter::WordBreakIterator::word_type, FnInStruct)]
+ pub fn word_type(&self) -> ICU4XSegmenterWordType {
+ self.0.word_type().into()
+ }
+
+ /// Return true when break boundary is word-like such as letter/number/CJK
+ #[diplomat::rust_link(icu::segmenter::WordBreakIterator::is_word_like, FnInStruct)]
+ pub fn is_word_like(&self) -> bool {
+ self.0.is_word_like()
+ }
+ }
+
+ impl<'a> ICU4XWordBreakIteratorLatin1<'a> {
+ /// Finds the next breakpoint. Returns -1 if at the end of the string or if the index is
+ /// out of range of a 32-bit signed integer.
+ #[allow(clippy::should_implement_trait)]
+ #[diplomat::rust_link(icu::segmenter::WordBreakIterator::next, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::segmenter::WordBreakIterator::Item,
+ AssociatedTypeInStruct,
+ hidden
+ )]
+ pub fn next(&mut self) -> i32 {
+ self.0
+ .next()
+ .and_then(|u| i32::try_from(u).ok())
+ .unwrap_or(-1)
+ }
+
+ /// Return the status value of break boundary.
+ #[diplomat::rust_link(icu::segmenter::WordBreakIterator::word_type, FnInStruct)]
+ pub fn word_type(&self) -> ICU4XSegmenterWordType {
+ self.0.word_type().into()
+ }
+
+ /// Return true when break boundary is word-like such as letter/number/CJK
+ #[diplomat::rust_link(icu::segmenter::WordBreakIterator::is_word_like, FnInStruct)]
+ pub fn is_word_like(&self) -> bool {
+ self.0.is_word_like()
+ }
+ }
+}
diff --git a/intl/icu_capi/src/time.rs b/intl/icu_capi/src/time.rs
new file mode 100644
index 0000000000..2a6e6e4d6e
--- /dev/null
+++ b/intl/icu_capi/src/time.rs
@@ -0,0 +1,68 @@
+// 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]
+pub mod ffi {
+ use alloc::boxed::Box;
+
+ use icu_calendar::types::Time;
+
+ use crate::errors::ffi::ICU4XError;
+
+ #[diplomat::opaque]
+ /// An ICU4X Time object representing a time in terms of hour, minute, second, nanosecond
+ #[diplomat::rust_link(icu::calendar::types::Time, Struct)]
+ pub struct ICU4XTime(pub Time);
+
+ impl ICU4XTime {
+ /// Creates a new [`ICU4XTime`] given field values
+ #[diplomat::rust_link(icu::calendar::types::Time, Struct)]
+ pub fn create(
+ hour: u8,
+ minute: u8,
+ second: u8,
+ nanosecond: u32,
+ ) -> Result<Box<ICU4XTime>, ICU4XError> {
+ let hour = hour.try_into()?;
+ let minute = minute.try_into()?;
+ let second = second.try_into()?;
+ let nanosecond = nanosecond.try_into()?;
+ let time = Time {
+ hour,
+ minute,
+ second,
+ nanosecond,
+ };
+ Ok(Box::new(ICU4XTime(time)))
+ }
+
+ /// Creates a new [`ICU4XTime`] representing midnight (00:00.000).
+ #[diplomat::rust_link(icu::calendar::types::Time, Struct)]
+ pub fn create_midnight() -> Result<Box<ICU4XTime>, ICU4XError> {
+ let time = Time::midnight();
+ Ok(Box::new(ICU4XTime(time)))
+ }
+
+ /// Returns the hour in this time
+ #[diplomat::rust_link(icu::calendar::types::Time::hour, StructField)]
+ pub fn hour(&self) -> u8 {
+ self.0.hour.into()
+ }
+ /// Returns the minute in this time
+ #[diplomat::rust_link(icu::calendar::types::Time::minute, StructField)]
+ pub fn minute(&self) -> u8 {
+ self.0.minute.into()
+ }
+ /// Returns the second in this time
+ #[diplomat::rust_link(icu::calendar::types::Time::second, StructField)]
+ pub fn second(&self) -> u8 {
+ self.0.second.into()
+ }
+ /// Returns the nanosecond in this time
+ #[diplomat::rust_link(icu::calendar::types::Time::nanosecond, StructField)]
+ pub fn nanosecond(&self) -> u32 {
+ self.0.nanosecond.into()
+ }
+ }
+}
diff --git a/intl/icu_capi/src/timezone.rs b/intl/icu_capi/src/timezone.rs
new file mode 100644
index 0000000000..4c8b2659b7
--- /dev/null
+++ b/intl/icu_capi/src/timezone.rs
@@ -0,0 +1,328 @@
+// 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 icu_timezone::CustomTimeZone;
+
+#[diplomat::bridge]
+pub mod ffi {
+ use crate::errors::ffi::ICU4XError;
+ use alloc::boxed::Box;
+ use core::fmt::Write;
+ use core::str::{self};
+ use icu_timezone::CustomTimeZone;
+ use icu_timezone::GmtOffset;
+ use icu_timezone::ZoneVariant;
+
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone, Struct)]
+ pub struct ICU4XCustomTimeZone(pub CustomTimeZone);
+
+ impl ICU4XCustomTimeZone {
+ /// Creates a time zone from an offset string.
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone::from_str, FnInStruct)]
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone::try_from_bytes, FnInStruct, hidden)]
+ #[diplomat::rust_link(icu::timezone::GmtOffset::from_str, FnInStruct, hidden)]
+ #[diplomat::rust_link(icu::timezone::GmtOffset::try_from_bytes, FnInStruct, hidden)]
+ pub fn create_from_string(s: &str) -> Result<Box<ICU4XCustomTimeZone>, ICU4XError> {
+ let bytes = s.as_bytes();
+ Ok(Box::new(ICU4XCustomTimeZone::from(
+ CustomTimeZone::try_from_bytes(bytes)?,
+ )))
+ }
+
+ /// Creates a time zone with no information.
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone::new_empty, FnInStruct)]
+ pub fn create_empty() -> Box<ICU4XCustomTimeZone> {
+ Box::new(CustomTimeZone::new_empty().into())
+ }
+
+ /// Creates a time zone for UTC.
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone::utc, FnInStruct)]
+ #[diplomat::rust_link(icu::timezone::GmtOffset::utc, FnInStruct, hidden)]
+ pub fn create_utc() -> Box<ICU4XCustomTimeZone> {
+ Box::new(CustomTimeZone::utc().into())
+ }
+
+ /// Sets the `gmt_offset` field from offset seconds.
+ ///
+ /// Errors if the offset seconds are out of range.
+ #[diplomat::rust_link(icu::timezone::GmtOffset::try_from_offset_seconds, FnInStruct)]
+ #[diplomat::rust_link(icu::timezone::GmtOffset, Struct, compact)]
+ #[diplomat::rust_link(
+ icu::timezone::GmtOffset::from_offset_seconds_unchecked,
+ FnInStruct,
+ hidden
+ )]
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone::new_with_offset, FnInStruct, hidden)]
+ pub fn try_set_gmt_offset_seconds(
+ &mut self,
+ offset_seconds: i32,
+ ) -> Result<(), ICU4XError> {
+ self.0.gmt_offset = Some(GmtOffset::try_from_offset_seconds(offset_seconds)?);
+ Ok(())
+ }
+
+ /// Clears the `gmt_offset` field.
+ #[diplomat::rust_link(icu::timezone::GmtOffset::offset_seconds, FnInStruct)]
+ #[diplomat::rust_link(icu::timezone::GmtOffset, Struct, compact)]
+ pub fn clear_gmt_offset(&mut self) {
+ self.0.gmt_offset.take();
+ }
+
+ /// Returns the value of the `gmt_offset` field as offset seconds.
+ ///
+ /// Errors if the `gmt_offset` field is empty.
+ #[diplomat::rust_link(icu::timezone::GmtOffset::offset_seconds, FnInStruct)]
+ #[diplomat::rust_link(icu::timezone::GmtOffset, Struct, compact)]
+ pub fn gmt_offset_seconds(&self) -> Result<i32, ICU4XError> {
+ self.0
+ .gmt_offset
+ .ok_or(ICU4XError::TimeZoneMissingInputError)
+ .map(GmtOffset::offset_seconds)
+ }
+
+ /// Returns whether the `gmt_offset` field is positive.
+ ///
+ /// Errors if the `gmt_offset` field is empty.
+ #[diplomat::rust_link(icu::timezone::GmtOffset::is_positive, FnInStruct)]
+ pub fn is_gmt_offset_positive(&self) -> Result<bool, ICU4XError> {
+ self.0
+ .gmt_offset
+ .ok_or(ICU4XError::TimeZoneMissingInputError)
+ .map(GmtOffset::is_positive)
+ }
+
+ /// Returns whether the `gmt_offset` field is zero.
+ ///
+ /// Errors if the `gmt_offset` field is empty (which is not the same as zero).
+ #[diplomat::rust_link(icu::timezone::GmtOffset::is_zero, FnInStruct)]
+ pub fn is_gmt_offset_zero(&self) -> Result<bool, ICU4XError> {
+ self.0
+ .gmt_offset
+ .ok_or(ICU4XError::TimeZoneMissingInputError)
+ .map(GmtOffset::is_zero)
+ }
+
+ /// Returns whether the `gmt_offset` field has nonzero minutes.
+ ///
+ /// Errors if the `gmt_offset` field is empty.
+ #[diplomat::rust_link(icu::timezone::GmtOffset::has_minutes, FnInStruct)]
+ pub fn gmt_offset_has_minutes(&self) -> Result<bool, ICU4XError> {
+ self.0
+ .gmt_offset
+ .ok_or(ICU4XError::TimeZoneMissingInputError)
+ .map(GmtOffset::has_minutes)
+ }
+
+ /// Returns whether the `gmt_offset` field has nonzero seconds.
+ ///
+ /// Errors if the `gmt_offset` field is empty.
+ #[diplomat::rust_link(icu::timezone::GmtOffset::has_seconds, FnInStruct)]
+ pub fn gmt_offset_has_seconds(&self) -> Result<bool, ICU4XError> {
+ self.0
+ .gmt_offset
+ .ok_or(ICU4XError::TimeZoneMissingInputError)
+ .map(GmtOffset::has_seconds)
+ }
+
+ /// Sets the `time_zone_id` field from a BCP-47 string.
+ ///
+ /// Errors if the string is not a valid BCP-47 time zone ID.
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone::time_zone_id, StructField)]
+ #[diplomat::rust_link(icu::timezone::TimeZoneBcp47Id, Struct, compact)]
+ #[diplomat::rust_link(icu::timezone::TimeZoneBcp47Id::from_str, FnInStruct, hidden)]
+ pub fn try_set_time_zone_id(&mut self, id: &str) -> Result<(), ICU4XError> {
+ self.0.time_zone_id = Some(id.parse()?);
+ Ok(())
+ }
+
+ /// Sets the `time_zone_id` field from an IANA string by looking up
+ /// the corresponding BCP-47 string.
+ ///
+ /// Errors if the string is not a valid BCP-47 time zone ID.
+ #[diplomat::rust_link(icu::timezone::IanaToBcp47MapperBorrowed::get, FnInStruct)]
+ pub fn try_set_iana_time_zone_id(
+ &mut self,
+ mapper: &crate::iana_bcp47_mapper::ffi::ICU4XIanaToBcp47Mapper,
+ id: &str,
+ ) -> Result<(), ICU4XError> {
+ match mapper.0.as_borrowed().get(id) {
+ Some(id) => {
+ self.0.time_zone_id = Some(id);
+ Ok(())
+ }
+ None => Err(ICU4XError::TimeZoneInvalidIdError),
+ }
+ }
+
+ /// Clears the `time_zone_id` field.
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone::time_zone_id, StructField)]
+ #[diplomat::rust_link(icu::timezone::TimeZoneBcp47Id, Struct, compact)]
+ pub fn clear_time_zone_id(&mut self) {
+ self.0.time_zone_id.take();
+ }
+
+ /// Writes the value of the `time_zone_id` field as a string.
+ ///
+ /// Errors if the `time_zone_id` field is empty.
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone::time_zone_id, StructField)]
+ #[diplomat::rust_link(icu::timezone::TimeZoneBcp47Id, Struct, compact)]
+ pub fn time_zone_id(
+ &self,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ write.write_str(
+ self.0
+ .time_zone_id
+ .ok_or(ICU4XError::TimeZoneMissingInputError)?
+ .0
+ .as_str(),
+ )?;
+ Ok(())
+ }
+
+ /// Sets the `metazone_id` field from a string.
+ ///
+ /// Errors if the string is not a valid BCP-47 metazone ID.
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone::metazone_id, StructField)]
+ #[diplomat::rust_link(icu::timezone::MetazoneId, Struct, compact)]
+ #[diplomat::rust_link(icu::timezone::MetazoneId::from_str, FnInStruct, hidden)]
+ pub fn try_set_metazone_id(&mut self, id: &str) -> Result<(), ICU4XError> {
+ self.0.metazone_id = Some(id.parse()?);
+ Ok(())
+ }
+
+ /// Clears the `metazone_id` field.
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone::metazone_id, StructField)]
+ #[diplomat::rust_link(icu::timezone::MetazoneId, Struct, compact)]
+ pub fn clear_metazone_id(&mut self) {
+ self.0.metazone_id.take();
+ }
+
+ /// Writes the value of the `metazone_id` field as a string.
+ ///
+ /// Errors if the `metazone_id` field is empty.
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone::metazone_id, StructField)]
+ #[diplomat::rust_link(icu::timezone::MetazoneId, Struct, compact)]
+ pub fn metazone_id(
+ &self,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ write.write_str(
+ self.0
+ .metazone_id
+ .ok_or(ICU4XError::TimeZoneMissingInputError)?
+ .0
+ .as_str(),
+ )?;
+ Ok(())
+ }
+
+ /// Sets the `zone_variant` field from a string.
+ ///
+ /// Errors if the string is not a valid zone variant.
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone::zone_variant, StructField)]
+ #[diplomat::rust_link(icu::timezone::ZoneVariant, Struct, compact)]
+ #[diplomat::rust_link(icu::timezone::ZoneVariant::from_str, FnInStruct, hidden)]
+ pub fn try_set_zone_variant(&mut self, id: &str) -> Result<(), ICU4XError> {
+ self.0.zone_variant = Some(id.parse()?);
+ Ok(())
+ }
+
+ /// Clears the `zone_variant` field.
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone::zone_variant, StructField)]
+ #[diplomat::rust_link(icu::timezone::ZoneVariant, Struct, compact)]
+ pub fn clear_zone_variant(&mut self) {
+ self.0.zone_variant.take();
+ }
+
+ /// Writes the value of the `zone_variant` field as a string.
+ ///
+ /// Errors if the `zone_variant` field is empty.
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone::zone_variant, StructField)]
+ #[diplomat::rust_link(icu::timezone::ZoneVariant, Struct, compact)]
+ pub fn zone_variant(
+ &self,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ write.write_str(
+ self.0
+ .zone_variant
+ .ok_or(ICU4XError::TimeZoneMissingInputError)?
+ .0
+ .as_str(),
+ )?;
+ Ok(())
+ }
+
+ /// Sets the `zone_variant` field to standard time.
+ #[diplomat::rust_link(icu::timezone::ZoneVariant::standard, FnInStruct)]
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone::zone_variant, StructField, compact)]
+ pub fn set_standard_time(&mut self) {
+ self.0.zone_variant = Some(ZoneVariant::standard())
+ }
+
+ /// Sets the `zone_variant` field to daylight time.
+ #[diplomat::rust_link(icu::timezone::ZoneVariant::daylight, FnInStruct)]
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone::zone_variant, StructField, compact)]
+ pub fn set_daylight_time(&mut self) {
+ self.0.zone_variant = Some(ZoneVariant::daylight())
+ }
+
+ /// Returns whether the `zone_variant` field is standard time.
+ ///
+ /// Errors if the `zone_variant` field is empty.
+ #[diplomat::rust_link(icu::timezone::ZoneVariant::standard, FnInStruct)]
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone::zone_variant, StructField, compact)]
+ pub fn is_standard_time(&self) -> Result<bool, ICU4XError> {
+ Ok(self
+ .0
+ .zone_variant
+ .ok_or(ICU4XError::TimeZoneMissingInputError)?
+ == ZoneVariant::standard())
+ }
+
+ /// Returns whether the `zone_variant` field is daylight time.
+ ///
+ /// Errors if the `zone_variant` field is empty.
+ #[diplomat::rust_link(icu::timezone::ZoneVariant::daylight, FnInStruct)]
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone::zone_variant, StructField, compact)]
+ pub fn is_daylight_time(&self) -> Result<bool, ICU4XError> {
+ Ok(self
+ .0
+ .zone_variant
+ .ok_or(ICU4XError::TimeZoneMissingInputError)?
+ == ZoneVariant::daylight())
+ }
+
+ /// Sets the metazone based on the time zone and the local timestamp.
+ #[diplomat::rust_link(icu::timezone::CustomTimeZone::maybe_calculate_metazone, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::timezone::MetazoneCalculator::compute_metazone_from_time_zone,
+ FnInStruct,
+ compact
+ )]
+ #[cfg(feature = "icu_timezone")]
+ pub fn maybe_calculate_metazone(
+ &mut self,
+ metazone_calculator: &crate::metazone_calculator::ffi::ICU4XMetazoneCalculator,
+ local_datetime: &crate::datetime::ffi::ICU4XIsoDateTime,
+ ) {
+ self.0
+ .maybe_calculate_metazone(&metazone_calculator.0, &local_datetime.0);
+ }
+ }
+}
+
+impl From<CustomTimeZone> for ffi::ICU4XCustomTimeZone {
+ fn from(other: CustomTimeZone) -> Self {
+ Self(other)
+ }
+}
+
+impl From<ffi::ICU4XCustomTimeZone> for CustomTimeZone {
+ fn from(other: ffi::ICU4XCustomTimeZone) -> Self {
+ other.0
+ }
+}
diff --git a/intl/icu_capi/src/timezone_formatter.rs b/intl/icu_capi/src/timezone_formatter.rs
new file mode 100644
index 0000000000..0cd4b04bc3
--- /dev/null
+++ b/intl/icu_capi/src/timezone_formatter.rs
@@ -0,0 +1,298 @@
+// 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 icu_datetime::time_zone::{FallbackFormat, TimeZoneFormatterOptions};
+
+macro_rules! call_method {
+ ($self:ident, $compiled:ident, $unstable:ident, $provider:expr) => {
+ match &$provider.0 {
+ $crate::provider::ICU4XDataProviderInner::Destroyed => Err(
+ icu_provider::DataError::custom("This provider has been destroyed"),
+ )?,
+ $crate::provider::ICU4XDataProviderInner::Empty => $self
+ .0
+ .$unstable(&icu_provider_adapters::empty::EmptyDataProvider::new()),
+ #[cfg(feature = "buffer_provider")]
+ $crate::provider::ICU4XDataProviderInner::Buffer(buffer_provider) => $self.0.$unstable(
+ &icu_provider::AsDeserializingBufferProvider::as_deserializing(buffer_provider),
+ ),
+ #[cfg(feature = "compiled_data")]
+ $crate::provider::ICU4XDataProviderInner::Compiled => $self.0.$compiled(),
+ }
+ };
+}
+
+#[diplomat::bridge]
+pub mod ffi {
+ use crate::errors::ffi::ICU4XError;
+ use crate::locale::ffi::ICU4XLocale;
+ use crate::provider::ffi::ICU4XDataProvider;
+ use crate::timezone::ffi::ICU4XCustomTimeZone;
+ use alloc::boxed::Box;
+ use icu_datetime::time_zone::FallbackFormat;
+ use icu_datetime::time_zone::IsoFormat;
+ use icu_datetime::time_zone::IsoMinutes;
+ use icu_datetime::time_zone::IsoSeconds;
+ use icu_datetime::time_zone::TimeZoneFormatter;
+ use writeable::Writeable;
+
+ #[diplomat::opaque]
+ /// An ICU4X TimeZoneFormatter object capable of formatting an [`ICU4XCustomTimeZone`] type (and others) as a string
+ #[diplomat::rust_link(icu::datetime::time_zone::TimeZoneFormatter, Struct)]
+ pub struct ICU4XTimeZoneFormatter(pub TimeZoneFormatter);
+
+ #[diplomat::enum_convert(IsoFormat, needs_wildcard)]
+ #[diplomat::rust_link(icu::datetime::time_zone::IsoFormat, Enum)]
+ pub enum ICU4XIsoTimeZoneFormat {
+ Basic,
+ Extended,
+ UtcBasic,
+ UtcExtended,
+ }
+
+ #[diplomat::enum_convert(IsoMinutes, needs_wildcard)]
+ #[diplomat::rust_link(icu::datetime::time_zone::IsoMinutes, Enum)]
+ pub enum ICU4XIsoTimeZoneMinuteDisplay {
+ Required,
+ Optional,
+ }
+
+ #[diplomat::enum_convert(IsoSeconds, needs_wildcard)]
+ #[diplomat::rust_link(icu::datetime::time_zone::IsoSeconds, Enum)]
+ pub enum ICU4XIsoTimeZoneSecondDisplay {
+ Optional,
+ Never,
+ }
+
+ pub struct ICU4XIsoTimeZoneOptions {
+ pub format: ICU4XIsoTimeZoneFormat,
+ pub minutes: ICU4XIsoTimeZoneMinuteDisplay,
+ pub seconds: ICU4XIsoTimeZoneSecondDisplay,
+ }
+
+ impl ICU4XTimeZoneFormatter {
+ /// Creates a new [`ICU4XTimeZoneFormatter`] from locale data.
+ ///
+ /// Uses localized GMT as the fallback format.
+ #[diplomat::rust_link(icu::datetime::time_zone::TimeZoneFormatter::try_new, FnInStruct)]
+ #[diplomat::rust_link(icu::datetime::time_zone::FallbackFormat, Enum, compact)]
+ #[diplomat::rust_link(icu::datetime::time_zone::TimeZoneFormatterOptions, Struct, hidden)]
+ pub fn create_with_localized_gmt_fallback(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ ) -> Result<Box<ICU4XTimeZoneFormatter>, ICU4XError> {
+ let locale = locale.to_datalocale();
+
+ Ok(Box::new(ICU4XTimeZoneFormatter(call_constructor!(
+ TimeZoneFormatter::try_new,
+ TimeZoneFormatter::try_new_with_any_provider,
+ TimeZoneFormatter::try_new_with_buffer_provider,
+ provider,
+ &locale,
+ FallbackFormat::LocalizedGmt.into(),
+ )?)))
+ }
+
+ /// Creates a new [`ICU4XTimeZoneFormatter`] from locale data.
+ ///
+ /// Uses ISO-8601 as the fallback format.
+ #[diplomat::rust_link(icu::datetime::time_zone::TimeZoneFormatter::try_new, FnInStruct)]
+ #[diplomat::rust_link(icu::datetime::time_zone::FallbackFormat, Enum, compact)]
+ #[diplomat::rust_link(icu::datetime::time_zone::TimeZoneFormatterOptions, Struct, hidden)]
+ pub fn create_with_iso_8601_fallback(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ options: ICU4XIsoTimeZoneOptions,
+ ) -> Result<Box<ICU4XTimeZoneFormatter>, ICU4XError> {
+ let locale = locale.to_datalocale();
+
+ Ok(Box::new(ICU4XTimeZoneFormatter(call_constructor!(
+ TimeZoneFormatter::try_new,
+ TimeZoneFormatter::try_new_with_any_provider,
+ TimeZoneFormatter::try_new_with_buffer_provider,
+ provider,
+ &locale,
+ options.into(),
+ )?)))
+ }
+
+ /// Loads generic non-location long format. Example: "Pacific Time"
+ #[diplomat::rust_link(
+ icu::datetime::time_zone::TimeZoneFormatter::include_generic_non_location_long,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu::datetime::time_zone::TimeZoneFormatter::load_generic_non_location_long,
+ FnInStruct,
+ hidden
+ )]
+ pub fn load_generic_non_location_long(
+ &mut self,
+ provider: &ICU4XDataProvider,
+ ) -> Result<(), ICU4XError> {
+ call_method!(
+ self,
+ include_generic_non_location_long,
+ load_generic_non_location_long,
+ provider
+ )?;
+ Ok(())
+ }
+
+ /// Loads generic non-location short format. Example: "PT"
+ #[diplomat::rust_link(
+ icu::datetime::time_zone::TimeZoneFormatter::include_generic_non_location_short,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu::datetime::time_zone::TimeZoneFormatter::load_generic_non_location_short,
+ FnInStruct,
+ hidden
+ )]
+ pub fn load_generic_non_location_short(
+ &mut self,
+ provider: &ICU4XDataProvider,
+ ) -> Result<(), ICU4XError> {
+ call_method!(
+ self,
+ include_generic_non_location_short,
+ load_generic_non_location_short,
+ provider
+ )?;
+ Ok(())
+ }
+
+ /// Loads specific non-location long format. Example: "Pacific Standard Time"
+ #[diplomat::rust_link(
+ icu::datetime::time_zone::TimeZoneFormatter::include_specific_non_location_long,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu::datetime::time_zone::TimeZoneFormatter::load_specific_non_location_long,
+ FnInStruct,
+ hidden
+ )]
+ pub fn load_specific_non_location_long(
+ &mut self,
+ provider: &ICU4XDataProvider,
+ ) -> Result<(), ICU4XError> {
+ call_method!(
+ self,
+ include_specific_non_location_long,
+ load_specific_non_location_long,
+ provider
+ )?;
+ Ok(())
+ }
+
+ /// Loads specific non-location short format. Example: "PST"
+ #[diplomat::rust_link(
+ icu::datetime::time_zone::TimeZoneFormatter::include_specific_non_location_short,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu::datetime::time_zone::TimeZoneFormatter::load_specific_non_location_short,
+ FnInStruct,
+ hidden
+ )]
+ pub fn load_specific_non_location_short(
+ &mut self,
+ provider: &ICU4XDataProvider,
+ ) -> Result<(), ICU4XError> {
+ call_method!(
+ self,
+ include_specific_non_location_short,
+ load_specific_non_location_short,
+ provider
+ )?;
+ Ok(())
+ }
+
+ /// Loads generic location format. Example: "Los Angeles Time"
+ #[diplomat::rust_link(
+ icu::datetime::time_zone::TimeZoneFormatter::include_generic_location_format,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu::datetime::time_zone::TimeZoneFormatter::load_generic_location_format,
+ FnInStruct,
+ hidden
+ )]
+ pub fn load_generic_location_format(
+ &mut self,
+ provider: &ICU4XDataProvider,
+ ) -> Result<(), ICU4XError> {
+ call_method!(
+ self,
+ include_generic_location_format,
+ load_generic_location_format,
+ provider
+ )?;
+ Ok(())
+ }
+
+ /// Loads localized GMT format. Example: "GMT-07:00"
+ #[diplomat::rust_link(
+ icu::datetime::time_zone::TimeZoneFormatter::include_localized_gmt_format,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu::datetime::time_zone::TimeZoneFormatter::load_localized_gmt_format,
+ FnInStruct,
+ hidden
+ )]
+ pub fn include_localized_gmt_format(&mut self) -> Result<(), ICU4XError> {
+ self.0.include_localized_gmt_format()?;
+ Ok(())
+ }
+
+ /// Loads ISO-8601 format. Example: "-07:00"
+ #[diplomat::rust_link(
+ icu::datetime::time_zone::TimeZoneFormatter::include_iso_8601_format,
+ FnInStruct
+ )]
+ #[diplomat::rust_link(
+ icu::datetime::time_zone::TimeZoneFormatter::load_iso_8601_format,
+ FnInStruct,
+ hidden
+ )]
+ pub fn load_iso_8601_format(
+ &mut self,
+ options: ICU4XIsoTimeZoneOptions,
+ ) -> Result<(), ICU4XError> {
+ self.0.include_iso_8601_format(
+ options.format.into(),
+ options.minutes.into(),
+ options.seconds.into(),
+ )?;
+ Ok(())
+ }
+
+ /// Formats a [`ICU4XCustomTimeZone`] to a string.
+ #[diplomat::rust_link(icu::datetime::time_zone::TimeZoneFormatter::format, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::datetime::time_zone::TimeZoneFormatter::format_to_string,
+ FnInStruct
+ )]
+ pub fn format_custom_time_zone(
+ &self,
+ value: &ICU4XCustomTimeZone,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ self.0.format(&value.0).write_to(write)?;
+ Ok(())
+ }
+ }
+}
+
+impl From<ffi::ICU4XIsoTimeZoneOptions> for TimeZoneFormatterOptions {
+ fn from(other: ffi::ICU4XIsoTimeZoneOptions) -> Self {
+ FallbackFormat::Iso8601(
+ other.format.into(),
+ other.minutes.into(),
+ other.seconds.into(),
+ )
+ .into()
+ }
+}
diff --git a/intl/icu_capi/src/week.rs b/intl/icu_capi/src/week.rs
new file mode 100644
index 0000000000..f82b5aca07
--- /dev/null
+++ b/intl/icu_capi/src/week.rs
@@ -0,0 +1,93 @@
+// 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 icu_calendar::week::WeekOf;
+
+#[diplomat::bridge]
+pub mod ffi {
+ use crate::date::ffi::ICU4XIsoWeekday;
+ use crate::errors::ffi::ICU4XError;
+ use crate::locale::ffi::ICU4XLocale;
+ use crate::provider::ffi::ICU4XDataProvider;
+ use alloc::boxed::Box;
+ use icu_calendar::week::{RelativeUnit, WeekCalculator};
+
+ #[diplomat::rust_link(icu::calendar::week::RelativeUnit, Enum)]
+ #[diplomat::enum_convert(RelativeUnit)]
+ pub enum ICU4XWeekRelativeUnit {
+ Previous,
+ Current,
+ Next,
+ }
+
+ #[diplomat::rust_link(icu::calendar::week::WeekOf, Struct)]
+ pub struct ICU4XWeekOf {
+ pub week: u16,
+ pub unit: ICU4XWeekRelativeUnit,
+ }
+ /// A Week calculator, useful to be passed in to `week_of_year()` on Date and DateTime types
+ #[diplomat::opaque]
+ #[diplomat::rust_link(icu::calendar::week::WeekCalculator, Struct)]
+ pub struct ICU4XWeekCalculator(pub WeekCalculator);
+
+ impl ICU4XWeekCalculator {
+ /// Creates a new [`ICU4XWeekCalculator`] from locale data.
+ #[diplomat::rust_link(icu::calendar::week::WeekCalculator::try_new, FnInStruct)]
+ pub fn create(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ ) -> Result<Box<ICU4XWeekCalculator>, ICU4XError> {
+ let locale = locale.to_datalocale();
+
+ Ok(Box::new(ICU4XWeekCalculator(call_constructor!(
+ WeekCalculator::try_new,
+ WeekCalculator::try_new_with_any_provider,
+ WeekCalculator::try_new_with_buffer_provider,
+ provider,
+ &locale,
+ )?)))
+ }
+
+ #[diplomat::rust_link(
+ icu::calendar::week::WeekCalculator::first_weekday,
+ StructField,
+ compact
+ )]
+ #[diplomat::rust_link(
+ icu::calendar::week::WeekCalculator::min_week_days,
+ StructField,
+ compact
+ )]
+ pub fn create_from_first_day_of_week_and_min_week_days(
+ first_weekday: ICU4XIsoWeekday,
+ min_week_days: u8,
+ ) -> Box<ICU4XWeekCalculator> {
+ let mut calculator = WeekCalculator::default();
+ calculator.first_weekday = first_weekday.into();
+ calculator.min_week_days = min_week_days;
+ Box::new(ICU4XWeekCalculator(calculator))
+ }
+
+ /// Returns the weekday that starts the week for this object's locale
+ #[diplomat::rust_link(icu::calendar::week::WeekCalculator::first_weekday, StructField)]
+ pub fn first_weekday(&self) -> ICU4XIsoWeekday {
+ self.0.first_weekday.into()
+ }
+ /// The minimum number of days overlapping a year required for a week to be
+ /// considered part of that year
+ #[diplomat::rust_link(icu::calendar::week::WeekCalculator::min_week_days, StructField)]
+ pub fn min_week_days(&self) -> u8 {
+ self.0.min_week_days
+ }
+ }
+}
+
+impl From<WeekOf> for ffi::ICU4XWeekOf {
+ fn from(other: WeekOf) -> Self {
+ ffi::ICU4XWeekOf {
+ week: other.week,
+ unit: other.unit.into(),
+ }
+ }
+}
diff --git a/intl/icu_capi/src/zoned_formatter.rs b/intl/icu_capi/src/zoned_formatter.rs
new file mode 100644
index 0000000000..6cafaa7666
--- /dev/null
+++ b/intl/icu_capi/src/zoned_formatter.rs
@@ -0,0 +1,198 @@
+// 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]
+pub mod ffi {
+ use alloc::boxed::Box;
+ use icu_calendar::{DateTime, Gregorian};
+ use icu_datetime::{options::length, TypedZonedDateTimeFormatter, ZonedDateTimeFormatter};
+ use writeable::Writeable;
+
+ use crate::{
+ datetime::ffi::ICU4XDateTime, datetime::ffi::ICU4XIsoDateTime,
+ datetime_formatter::ffi::ICU4XDateLength, datetime_formatter::ffi::ICU4XTimeLength,
+ errors::ffi::ICU4XError, locale::ffi::ICU4XLocale, provider::ffi::ICU4XDataProvider,
+ timezone::ffi::ICU4XCustomTimeZone, timezone_formatter::ffi::ICU4XIsoTimeZoneOptions,
+ };
+
+ // TODO(https://github.com/rust-diplomat/diplomat/issues/248)
+ #[allow(unused_imports)]
+ use crate::{
+ timezone_formatter::ffi::ICU4XIsoTimeZoneFormat,
+ timezone_formatter::ffi::ICU4XIsoTimeZoneMinuteDisplay,
+ timezone_formatter::ffi::ICU4XIsoTimeZoneSecondDisplay,
+ };
+
+ #[diplomat::opaque]
+ /// An object capable of formatting a date time with time zone to a string.
+ #[diplomat::rust_link(icu::datetime::TypedZonedDateTimeFormatter, Struct)]
+ pub struct ICU4XGregorianZonedDateTimeFormatter(pub TypedZonedDateTimeFormatter<Gregorian>);
+
+ impl ICU4XGregorianZonedDateTimeFormatter {
+ /// Creates a new [`ICU4XGregorianZonedDateTimeFormatter`] from locale data.
+ ///
+ /// This function has `date_length` and `time_length` arguments and uses default options
+ /// for the time zone.
+ #[diplomat::rust_link(icu::datetime::TypedZonedDateTimeFormatter::try_new, FnInStruct)]
+ pub fn create_with_lengths(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ date_length: ICU4XDateLength,
+ time_length: ICU4XTimeLength,
+ ) -> Result<Box<ICU4XGregorianZonedDateTimeFormatter>, ICU4XError> {
+ let locale = locale.to_datalocale();
+
+ Ok(Box::new(ICU4XGregorianZonedDateTimeFormatter(
+ call_constructor!(
+ TypedZonedDateTimeFormatter::<Gregorian>::try_new,
+ TypedZonedDateTimeFormatter::<Gregorian>::try_new_with_any_provider,
+ TypedZonedDateTimeFormatter::<Gregorian>::try_new_with_buffer_provider,
+ provider,
+ &locale,
+ length::Bag::from_date_time_style(date_length.into(), time_length.into())
+ .into(),
+ Default::default(),
+ )?,
+ )))
+ }
+
+ /// Creates a new [`ICU4XGregorianZonedDateTimeFormatter`] from locale data.
+ ///
+ /// This function has `date_length` and `time_length` arguments and uses an ISO-8601 style
+ /// fallback for the time zone with the given configurations.
+ #[diplomat::rust_link(icu::datetime::TypedZonedDateTimeFormatter::try_new, FnInStruct)]
+ pub fn create_with_lengths_and_iso_8601_time_zone_fallback(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ date_length: ICU4XDateLength,
+ time_length: ICU4XTimeLength,
+ zone_options: ICU4XIsoTimeZoneOptions,
+ ) -> Result<Box<ICU4XGregorianZonedDateTimeFormatter>, ICU4XError> {
+ let locale = locale.to_datalocale();
+
+ Ok(Box::new(ICU4XGregorianZonedDateTimeFormatter(
+ call_constructor!(
+ TypedZonedDateTimeFormatter::<Gregorian>::try_new,
+ TypedZonedDateTimeFormatter::<Gregorian>::try_new_with_any_provider,
+ TypedZonedDateTimeFormatter::<Gregorian>::try_new_with_buffer_provider,
+ provider,
+ &locale,
+ length::Bag::from_date_time_style(date_length.into(), time_length.into())
+ .into(),
+ zone_options.into(),
+ )?,
+ )))
+ }
+
+ /// Formats a [`ICU4XIsoDateTime`] and [`ICU4XCustomTimeZone`] to a string.
+ #[diplomat::rust_link(icu::datetime::TypedZonedDateTimeFormatter::format, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::datetime::TypedZonedDateTimeFormatter::format_to_string,
+ FnInStruct,
+ hidden
+ )]
+ pub fn format_iso_datetime_with_custom_time_zone(
+ &self,
+ datetime: &ICU4XIsoDateTime,
+ time_zone: &ICU4XCustomTimeZone,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ let greg = DateTime::new_from_iso(datetime.0, Gregorian);
+ self.0.format(&greg, &time_zone.0).write_to(write)?;
+ Ok(())
+ }
+ }
+
+ #[diplomat::opaque]
+ /// An object capable of formatting a date time with time zone to a string.
+ #[diplomat::rust_link(icu::datetime::ZonedDateTimeFormatter, Struct)]
+ pub struct ICU4XZonedDateTimeFormatter(pub ZonedDateTimeFormatter);
+
+ impl ICU4XZonedDateTimeFormatter {
+ /// Creates a new [`ICU4XZonedDateTimeFormatter`] from locale data.
+ ///
+ /// This function has `date_length` and `time_length` arguments and uses default options
+ /// for the time zone.
+ #[diplomat::rust_link(icu::datetime::ZonedDateTimeFormatter::try_new, FnInStruct)]
+ pub fn create_with_lengths(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ date_length: ICU4XDateLength,
+ time_length: ICU4XTimeLength,
+ ) -> Result<Box<ICU4XZonedDateTimeFormatter>, ICU4XError> {
+ let locale = locale.to_datalocale();
+
+ Ok(Box::new(ICU4XZonedDateTimeFormatter(call_constructor!(
+ ZonedDateTimeFormatter::try_new,
+ ZonedDateTimeFormatter::try_new_with_any_provider,
+ ZonedDateTimeFormatter::try_new_with_buffer_provider,
+ provider,
+ &locale,
+ length::Bag::from_date_time_style(date_length.into(), time_length.into()).into(),
+ Default::default(),
+ )?)))
+ }
+
+ /// Creates a new [`ICU4XZonedDateTimeFormatter`] from locale data.
+ ///
+ /// This function has `date_length` and `time_length` arguments and uses an ISO-8601 style
+ /// fallback for the time zone with the given configurations.
+ #[diplomat::rust_link(icu::datetime::ZonedDateTimeFormatter::try_new, FnInStruct)]
+ pub fn create_with_lengths_and_iso_8601_time_zone_fallback(
+ provider: &ICU4XDataProvider,
+ locale: &ICU4XLocale,
+ date_length: ICU4XDateLength,
+ time_length: ICU4XTimeLength,
+ zone_options: ICU4XIsoTimeZoneOptions,
+ ) -> Result<Box<ICU4XZonedDateTimeFormatter>, ICU4XError> {
+ let locale = locale.to_datalocale();
+
+ Ok(Box::new(ICU4XZonedDateTimeFormatter(call_constructor!(
+ ZonedDateTimeFormatter::try_new,
+ ZonedDateTimeFormatter::try_new_with_any_provider,
+ ZonedDateTimeFormatter::try_new_with_buffer_provider,
+ provider,
+ &locale,
+ length::Bag::from_date_time_style(date_length.into(), time_length.into()).into(),
+ zone_options.into(),
+ )?)))
+ }
+
+ /// Formats a [`ICU4XDateTime`] and [`ICU4XCustomTimeZone`] to a string.
+ #[diplomat::rust_link(icu::datetime::ZonedDateTimeFormatter::format, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::datetime::ZonedDateTimeFormatter::format_to_string,
+ FnInStruct,
+ hidden
+ )]
+ pub fn format_datetime_with_custom_time_zone(
+ &self,
+ datetime: &ICU4XDateTime,
+ time_zone: &ICU4XCustomTimeZone,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ self.0.format(&datetime.0, &time_zone.0)?.write_to(write)?;
+ Ok(())
+ }
+
+ /// Formats a [`ICU4XIsoDateTime`] and [`ICU4XCustomTimeZone`] to a string.
+ #[diplomat::rust_link(icu::datetime::ZonedDateTimeFormatter::format, FnInStruct)]
+ #[diplomat::rust_link(
+ icu::datetime::ZonedDateTimeFormatter::format_to_string,
+ FnInStruct,
+ hidden
+ )]
+ pub fn format_iso_datetime_with_custom_time_zone(
+ &self,
+ datetime: &ICU4XIsoDateTime,
+ time_zone: &ICU4XCustomTimeZone,
+ write: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> Result<(), ICU4XError> {
+ self.0
+ .format(&datetime.0.to_any(), &time_zone.0)?
+ .write_to(write)?;
+ Ok(())
+ }
+ }
+}