icu_capi/
errors.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5use self::ffi::ICU4XError;
6use core::fmt;
7#[cfg(feature = "icu_decimal")]
8use fixed_decimal::FixedDecimalError;
9#[cfg(any(
10    feature = "icu_datetime",
11    feature = "icu_timezone",
12    feature = "icu_calendar"
13))]
14use icu_calendar::CalendarError;
15#[cfg(feature = "icu_collator")]
16use icu_collator::CollatorError;
17#[cfg(feature = "icu_datetime")]
18use icu_datetime::DateTimeError;
19#[cfg(any(feature = "icu_decimal", feature = "icu_datetime"))]
20use icu_decimal::DecimalError;
21#[cfg(feature = "experimental_components")]
22use icu_experimental::units::ConversionError;
23#[cfg(feature = "icu_list")]
24use icu_list::ListError;
25use icu_locid::ParserError;
26#[cfg(feature = "icu_locid_transform")]
27use icu_locid_transform::LocaleTransformError;
28#[cfg(feature = "icu_normalizer")]
29use icu_normalizer::NormalizerError;
30#[cfg(any(feature = "icu_plurals", feature = "icu_datetime"))]
31use icu_plurals::PluralsError;
32#[cfg(feature = "icu_properties")]
33use icu_properties::PropertiesError;
34use icu_provider::{DataError, DataErrorKind};
35#[cfg(feature = "icu_segmenter")]
36use icu_segmenter::SegmenterError;
37#[cfg(any(feature = "icu_timezone", feature = "icu_datetime"))]
38use icu_timezone::TimeZoneError;
39
40#[diplomat::bridge]
41pub mod ffi {
42    use alloc::boxed::Box;
43
44    #[derive(Debug, PartialEq, Eq)]
45    #[repr(C)]
46    /// A common enum for errors that ICU4X may return, organized by API
47    ///
48    /// The error names are stable and can be checked against as strings in the JS API
49    #[diplomat::rust_link(fixed_decimal::FixedDecimalError, Enum, compact)]
50    #[diplomat::rust_link(icu::calendar::CalendarError, Enum, compact)]
51    #[diplomat::rust_link(icu::collator::CollatorError, Enum, compact)]
52    #[diplomat::rust_link(icu::datetime::DateTimeError, Enum, compact)]
53    #[diplomat::rust_link(icu::datetime::MismatchedCalendarError, Struct, hidden)]
54    #[diplomat::rust_link(icu::decimal::DecimalError, Enum, compact)]
55    #[diplomat::rust_link(icu::list::ListError, Enum, compact)]
56    #[diplomat::rust_link(icu::locid::ParserError, Enum, compact)]
57    #[diplomat::rust_link(icu::locid_transform::LocaleTransformError, Enum, compact)]
58    #[diplomat::rust_link(icu::normalizer::NormalizerError, Enum, compact)]
59    #[diplomat::rust_link(icu::plurals::PluralsError, Enum, compact)]
60    #[diplomat::rust_link(icu::properties::PropertiesError, Enum, compact)]
61    #[diplomat::rust_link(icu::provider::DataError, Struct, compact)]
62    #[diplomat::rust_link(icu::provider::DataErrorKind, Enum, compact)]
63    #[diplomat::rust_link(icu::segmenter::SegmenterError, Enum, compact)]
64    #[diplomat::rust_link(icu::timezone::TimeZoneError, Enum, compact)]
65    #[diplomat::rust_link(icu_experimental::units::ConversionError, Enum, compact)]
66    pub enum ICU4XError {
67        // general errors
68        /// The error is not currently categorized as ICU4XError.
69        /// Please file a bug
70        UnknownError = 0x00,
71        /// An error arising from writing to a string
72        /// Typically found when not enough space is allocated
73        /// Most APIs that return a string may return this error
74        WriteableError = 0x01,
75        /// Some input was out of bounds
76        OutOfBoundsError = 0x02,
77        /// Input expected to be UTF-8 was ill-formed
78        Utf8Error = 0x03,
79
80        // general data errors
81        // See DataError
82        DataMissingDataKeyError = 0x1_00,
83        DataMissingVariantError = 0x1_01,
84        DataMissingLocaleError = 0x1_02,
85        DataNeedsVariantError = 0x1_03,
86        DataNeedsLocaleError = 0x1_04,
87        DataExtraneousLocaleError = 0x1_05,
88        DataFilteredResourceError = 0x1_06,
89        DataMismatchedTypeError = 0x1_07,
90        DataMissingPayloadError = 0x1_08,
91        DataInvalidStateError = 0x1_09,
92        DataCustomError = 0x1_0A,
93        DataIoError = 0x1_0B,
94        DataUnavailableBufferFormatError = 0x1_0C,
95        DataMismatchedAnyBufferError = 0x1_0D,
96
97        // locale errors
98        /// The subtag being requested was not set
99        LocaleUndefinedSubtagError = 0x2_00,
100        /// The locale or subtag string failed to parse
101        LocaleParserLanguageError = 0x2_01,
102        LocaleParserSubtagError = 0x2_02,
103        LocaleParserExtensionError = 0x2_03,
104
105        // data struct errors
106        /// Attempted to construct an invalid data struct
107        DataStructValidityError = 0x3_00,
108
109        // property errors
110        PropertyUnknownScriptIdError = 0x4_00,
111        PropertyUnknownGeneralCategoryGroupError = 0x4_01,
112        PropertyUnexpectedPropertyNameError = 0x4_02,
113
114        // fixed_decimal errors
115        FixedDecimalLimitError = 0x5_00,
116        FixedDecimalSyntaxError = 0x5_01,
117
118        // plural errors
119        PluralsParserError = 0x6_00,
120
121        // datetime errors
122        CalendarParseError = 0x7_00,
123        CalendarOverflowError = 0x7_01,
124        CalendarUnderflowError = 0x7_02,
125        CalendarOutOfRangeError = 0x7_03,
126        CalendarUnknownEraError = 0x7_04,
127        CalendarUnknownMonthCodeError = 0x7_05,
128        CalendarMissingInputError = 0x7_06,
129        CalendarUnknownKindError = 0x7_07,
130        CalendarMissingError = 0x7_08,
131
132        // datetime format errors
133        DateTimePatternError = 0x8_00,
134        DateTimeMissingInputFieldError = 0x8_01,
135        DateTimeSkeletonError = 0x8_02,
136        DateTimeUnsupportedFieldError = 0x8_03,
137        DateTimeUnsupportedOptionsError = 0x8_04,
138        DateTimeMissingWeekdaySymbolError = 0x8_05,
139        DateTimeMissingMonthSymbolError = 0x8_06,
140        DateTimeFixedDecimalError = 0x8_07,
141        DateTimeMismatchedCalendarError = 0x8_08,
142
143        // dead
144        // tinystr errors
145        TinyStrTooLargeError = 0x9_00,
146        TinyStrContainsNullError = 0x9_01,
147        TinyStrNonAsciiError = 0x9_02,
148
149        // timezone errors
150        TimeZoneOffsetOutOfBoundsError = 0xA_00,
151        TimeZoneInvalidOffsetError = 0xA_01,
152        TimeZoneMissingInputError = 0xA_02,
153        TimeZoneInvalidIdError = 0xA_03,
154
155        // normalizer errors
156        NormalizerFutureExtensionError = 0xB_00,
157        NormalizerValidationError = 0xB_01,
158
159        // Units errors
160        #[cfg(feature = "experimental_components")]
161        InvalidCldrUnitIdentifierError = 0x0C_00,
162    }
163}
164
165impl ICU4XError {
166    #[cfg(feature = "logging")]
167    #[inline]
168    pub(crate) fn log_original<T: core::fmt::Display + ?Sized>(self, e: &T) -> Self {
169        use core::any;
170        log::warn!(
171            "Returning ICU4XError::{:?} based on original {}: {}",
172            self,
173            any::type_name::<T>(),
174            e
175        );
176        self
177    }
178
179    #[cfg(not(feature = "logging"))]
180    #[inline]
181    pub(crate) fn log_original<T: core::fmt::Display + ?Sized>(self, _e: &T) -> Self {
182        self
183    }
184}
185
186impl From<fmt::Error> for ICU4XError {
187    fn from(e: fmt::Error) -> Self {
188        ICU4XError::WriteableError.log_original(&e)
189    }
190}
191
192impl From<DataError> for ICU4XError {
193    fn from(e: DataError) -> Self {
194        match e.kind {
195            DataErrorKind::MissingDataKey => ICU4XError::DataMissingDataKeyError,
196            DataErrorKind::MissingLocale => ICU4XError::DataMissingLocaleError,
197            DataErrorKind::NeedsLocale => ICU4XError::DataNeedsLocaleError,
198            DataErrorKind::ExtraneousLocale => ICU4XError::DataExtraneousLocaleError,
199            DataErrorKind::FilteredResource => ICU4XError::DataFilteredResourceError,
200            DataErrorKind::MismatchedType(..) => ICU4XError::DataMismatchedTypeError,
201            DataErrorKind::MissingPayload => ICU4XError::DataMissingPayloadError,
202            DataErrorKind::InvalidState => ICU4XError::DataInvalidStateError,
203            DataErrorKind::Custom => ICU4XError::DataCustomError,
204            #[cfg(all(
205                feature = "provider_fs",
206                not(any(target_arch = "wasm32", target_os = "none"))
207            ))]
208            DataErrorKind::Io(..) => ICU4XError::DataIoError,
209            // datagen only
210            // DataErrorKind::MissingSourceData(..) => ..,
211            DataErrorKind::UnavailableBufferFormat(..) => {
212                ICU4XError::DataUnavailableBufferFormatError
213            }
214            _ => ICU4XError::UnknownError,
215        }
216        .log_original(&e)
217    }
218}
219
220impl From<core::str::Utf8Error> for ICU4XError {
221    fn from(_: core::str::Utf8Error) -> Self {
222        ICU4XError::Utf8Error
223    }
224}
225
226#[cfg(feature = "icu_collator")]
227impl From<CollatorError> for ICU4XError {
228    fn from(e: CollatorError) -> Self {
229        match e {
230            CollatorError::NotFound => ICU4XError::DataMissingPayloadError,
231            CollatorError::MalformedData => ICU4XError::DataInvalidStateError,
232            CollatorError::Data(_) => ICU4XError::DataIoError,
233            _ => ICU4XError::DataIoError,
234        }
235        .log_original(&e)
236    }
237}
238
239#[cfg(feature = "icu_properties")]
240impl From<PropertiesError> for ICU4XError {
241    fn from(e: PropertiesError) -> Self {
242        match e {
243            PropertiesError::PropDataLoad(e) => e.into(),
244            PropertiesError::UnknownScriptId(..) => ICU4XError::PropertyUnknownScriptIdError,
245            PropertiesError::UnknownGeneralCategoryGroup(..) => {
246                ICU4XError::PropertyUnknownGeneralCategoryGroupError
247            }
248            PropertiesError::UnexpectedPropertyName => {
249                ICU4XError::PropertyUnexpectedPropertyNameError
250            }
251            _ => ICU4XError::UnknownError,
252        }
253        .log_original(&e)
254    }
255}
256
257#[cfg(any(
258    feature = "icu_datetime",
259    feature = "icu_timezone",
260    feature = "icu_calendar"
261))]
262impl From<CalendarError> for ICU4XError {
263    fn from(e: CalendarError) -> Self {
264        match e {
265            CalendarError::Parse => ICU4XError::CalendarParseError,
266            CalendarError::Overflow { field: _, max: _ } => ICU4XError::CalendarOverflowError,
267            CalendarError::Underflow { field: _, min: _ } => ICU4XError::CalendarUnderflowError,
268            CalendarError::OutOfRange => ICU4XError::CalendarOutOfRangeError,
269            CalendarError::UnknownEra(..) => ICU4XError::CalendarUnknownEraError,
270            CalendarError::UnknownMonthCode(..) => ICU4XError::CalendarUnknownMonthCodeError,
271            CalendarError::MissingInput(_) => ICU4XError::CalendarMissingInputError,
272            CalendarError::UnknownAnyCalendarKind(_) => ICU4XError::CalendarUnknownKindError,
273            CalendarError::MissingCalendar => ICU4XError::CalendarMissingError,
274            CalendarError::Data(e) => e.into(),
275            _ => ICU4XError::UnknownError,
276        }
277        .log_original(&e)
278    }
279}
280
281#[cfg(feature = "icu_datetime")]
282impl From<DateTimeError> for ICU4XError {
283    fn from(e: DateTimeError) -> Self {
284        match e {
285            DateTimeError::Pattern(_) => ICU4XError::DateTimePatternError,
286            DateTimeError::Format(err) => err.into(),
287            DateTimeError::Data(err) => err.into(),
288            DateTimeError::MissingInputField(_) => ICU4XError::DateTimeMissingInputFieldError,
289            // TODO(#1324): Add back skeleton errors
290            // DateTimeFormatterError::Skeleton(_) => ICU4XError::DateTimeFormatSkeletonError,
291            DateTimeError::UnsupportedField(_) => ICU4XError::DateTimeUnsupportedFieldError,
292            DateTimeError::UnsupportedOptions => ICU4XError::DateTimeUnsupportedOptionsError,
293            DateTimeError::PluralRules(err) => err.into(),
294            DateTimeError::DateTimeInput(err) => err.into(),
295            DateTimeError::MissingWeekdaySymbol(_) => ICU4XError::DateTimeMissingWeekdaySymbolError,
296            DateTimeError::MissingMonthSymbol(_) => ICU4XError::DateTimeMissingMonthSymbolError,
297            DateTimeError::FixedDecimal => ICU4XError::DateTimeFixedDecimalError,
298            DateTimeError::FixedDecimalFormatter(err) => err.into(),
299            DateTimeError::MismatchedAnyCalendar(_, _) => {
300                ICU4XError::DateTimeMismatchedCalendarError
301            }
302            _ => ICU4XError::UnknownError,
303        }
304        .log_original(&e)
305    }
306}
307
308#[cfg(feature = "icu_decimal")]
309impl From<FixedDecimalError> for ICU4XError {
310    fn from(e: FixedDecimalError) -> Self {
311        match e {
312            FixedDecimalError::Limit => ICU4XError::FixedDecimalLimitError,
313            FixedDecimalError::Syntax => ICU4XError::FixedDecimalSyntaxError,
314            _ => ICU4XError::UnknownError,
315        }
316        .log_original(&e)
317    }
318}
319
320#[cfg(any(feature = "icu_plurals", feature = "icu_datetime"))]
321impl From<PluralsError> for ICU4XError {
322    fn from(e: PluralsError) -> Self {
323        match e {
324            PluralsError::Data(e) => e.into(),
325            _ => ICU4XError::UnknownError,
326        }
327        .log_original(&e)
328    }
329}
330
331#[cfg(any(feature = "icu_decimal", feature = "icu_datetime"))]
332impl From<DecimalError> for ICU4XError {
333    fn from(e: DecimalError) -> Self {
334        match e {
335            DecimalError::Data(e) => e.into(),
336            _ => ICU4XError::UnknownError,
337        }
338        .log_original(&e)
339    }
340}
341
342#[cfg(feature = "icu_locid_transform")]
343impl From<LocaleTransformError> for ICU4XError {
344    fn from(e: LocaleTransformError) -> Self {
345        match e {
346            LocaleTransformError::Data(e) => e.into(),
347            _ => ICU4XError::UnknownError,
348        }
349        .log_original(&e)
350    }
351}
352
353#[cfg(feature = "icu_segmenter")]
354impl From<SegmenterError> for ICU4XError {
355    fn from(e: SegmenterError) -> Self {
356        match e {
357            SegmenterError::Data(e) => e.into(),
358            _ => ICU4XError::UnknownError,
359        }
360        .log_original(&e)
361    }
362}
363
364#[cfg(feature = "icu_list")]
365impl From<ListError> for ICU4XError {
366    fn from(e: ListError) -> Self {
367        match e {
368            ListError::Data(e) => e.into(),
369            _ => ICU4XError::UnknownError,
370        }
371        .log_original(&e)
372    }
373}
374
375impl From<ParserError> for ICU4XError {
376    fn from(e: ParserError) -> Self {
377        match e {
378            ParserError::InvalidLanguage => ICU4XError::LocaleParserLanguageError,
379            ParserError::InvalidSubtag => ICU4XError::LocaleParserSubtagError,
380            ParserError::InvalidExtension => ICU4XError::LocaleParserExtensionError,
381            ParserError::DuplicatedExtension => ICU4XError::LocaleParserExtensionError,
382            _ => ICU4XError::UnknownError,
383        }
384        .log_original(&e)
385    }
386}
387
388#[cfg(any(feature = "icu_timezone", feature = "icu_datetime"))]
389impl From<TimeZoneError> for ICU4XError {
390    fn from(e: TimeZoneError) -> Self {
391        match e {
392            TimeZoneError::OffsetOutOfBounds => ICU4XError::TimeZoneOffsetOutOfBoundsError,
393            TimeZoneError::InvalidOffset => ICU4XError::TimeZoneInvalidOffsetError,
394            TimeZoneError::Data(err) => err.into(),
395            _ => ICU4XError::UnknownError,
396        }
397        .log_original(&e)
398    }
399}
400
401#[cfg(feature = "icu_normalizer")]
402impl From<NormalizerError> for ICU4XError {
403    fn from(e: NormalizerError) -> Self {
404        match e {
405            NormalizerError::FutureExtension => ICU4XError::NormalizerFutureExtensionError,
406            NormalizerError::ValidationError => ICU4XError::NormalizerValidationError,
407            NormalizerError::Data(err) => err.into(),
408            _ => ICU4XError::UnknownError,
409        }
410        .log_original(&e)
411    }
412}
413
414#[cfg(feature = "experimental_components")]
415impl From<ConversionError> for ICU4XError {
416    fn from(value: ConversionError) -> Self {
417        match value {
418            ConversionError::InvalidUnit => ICU4XError::InvalidCldrUnitIdentifierError,
419            _ => ICU4XError::UnknownError,
420        }
421    }
422}