icu_capi/
provider.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
5#[allow(unused_imports)] // feature-specific
6use alloc::boxed::Box;
7use icu_provider::prelude::*;
8#[allow(unused_imports)] // feature-specific
9use icu_provider::MaybeSendSync;
10use icu_provider_adapters::empty::EmptyDataProvider;
11#[allow(unused_imports)] // feature-specific
12use yoke::{trait_hack::YokeTraitHack, Yokeable};
13#[allow(unused_imports)] // feature-specific
14use zerofrom::ZeroFrom;
15
16pub enum ICU4XDataProviderInner {
17    Destroyed,
18    Empty,
19    #[cfg(feature = "compiled_data")]
20    Compiled,
21    #[cfg(feature = "buffer_provider")]
22    Buffer(Box<dyn BufferProvider + 'static>),
23}
24
25#[diplomat::bridge]
26pub mod ffi {
27    use super::ICU4XDataProviderInner;
28    use crate::errors::ffi::ICU4XError;
29    use alloc::boxed::Box;
30    #[allow(unused_imports)] // feature-gated
31    use icu_provider_adapters::fallback::LocaleFallbackProvider;
32    #[allow(unused_imports)] // feature-gated
33    use icu_provider_adapters::fork::predicates::MissingLocalePredicate;
34
35    #[diplomat::opaque]
36    /// An ICU4X data provider, capable of loading ICU4X data keys from some source.
37    #[diplomat::rust_link(icu_provider, Mod)]
38    pub struct ICU4XDataProvider(pub ICU4XDataProviderInner);
39
40    #[cfg(feature = "buffer_provider")]
41    fn convert_buffer_provider<D: icu_provider::BufferProvider + 'static>(
42        x: D,
43    ) -> ICU4XDataProvider {
44        ICU4XDataProvider(super::ICU4XDataProviderInner::Buffer(Box::new(x)))
45    }
46
47    impl ICU4XDataProvider {
48        /// Constructs an [`ICU4XDataProvider`] that uses compiled data.
49        ///
50        /// Requires the `compiled_data` feature.
51        ///
52        /// This provider cannot be modified or combined with other providers, so `enable_fallback`,
53        /// `enabled_fallback_with`, `fork_by_locale`, and `fork_by_key` will return `Err`s.
54        #[cfg(feature = "compiled_data")]
55        #[diplomat::attr(all(supports = constructors, supports = fallible_constructors, supports = named_constructors), named_constructor = "compiled")]
56        pub fn create_compiled() -> Box<ICU4XDataProvider> {
57            Box::new(Self(ICU4XDataProviderInner::Compiled))
58        }
59
60        /// Constructs an `FsDataProvider` and returns it as an [`ICU4XDataProvider`].
61        /// Requires the `provider_fs` Cargo feature.
62        /// Not supported in WASM.
63        #[diplomat::rust_link(icu_provider_fs::FsDataProvider, Struct)]
64        #[cfg(all(
65            feature = "provider_fs",
66            not(any(target_arch = "wasm32", target_os = "none"))
67        ))]
68        #[diplomat::attr(dart, disable)]
69        #[diplomat::attr(all(supports = constructors, supports = fallible_constructors, supports = named_constructors), named_constructor = "fs")]
70        pub fn create_fs(path: &DiplomatStr) -> Result<Box<ICU4XDataProvider>, ICU4XError> {
71            Ok(Box::new(convert_buffer_provider(
72                icu_provider_fs::FsDataProvider::try_new(
73                    // In the future we can start using OsString APIs to support non-utf8 paths
74                    core::str::from_utf8(path)
75                        .map_err(|e| ICU4XError::DataIoError.log_original(&e))?,
76                )?,
77            )))
78        }
79
80        /// Deprecated
81        ///
82        /// Use `create_compiled()`.
83        #[cfg(all(
84            feature = "provider_test",
85            any(feature = "any_provider", feature = "buffer_provider")
86        ))]
87        #[diplomat::attr(supports = constructors, disable)]
88        pub fn create_test() -> Box<ICU4XDataProvider> {
89            Self::create_compiled()
90        }
91
92        /// Constructs a `BlobDataProvider` and returns it as an [`ICU4XDataProvider`].
93        #[diplomat::rust_link(icu_provider_blob::BlobDataProvider, Struct)]
94        #[cfg(feature = "buffer_provider")]
95        #[diplomat::attr(all(supports = constructors, supports = fallible_constructors, supports = named_constructors), named_constructor = "from_byte_slice")]
96        pub fn create_from_byte_slice(
97            blob: &'static [DiplomatByte],
98        ) -> Result<Box<ICU4XDataProvider>, ICU4XError> {
99            Ok(Box::new(convert_buffer_provider(
100                icu_provider_blob::BlobDataProvider::try_new_from_static_blob(blob)?,
101            )))
102        }
103
104        /// Constructs an empty [`ICU4XDataProvider`].
105        #[diplomat::rust_link(icu_provider_adapters::empty::EmptyDataProvider, Struct)]
106        #[diplomat::rust_link(
107            icu_provider_adapters::empty::EmptyDataProvider::new,
108            FnInStruct,
109            hidden
110        )]
111        #[diplomat::attr(all(supports = constructors, supports = fallible_constructors, supports = named_constructors), named_constructor = "empty")]
112        pub fn create_empty() -> Box<ICU4XDataProvider> {
113            Box::new(ICU4XDataProvider(ICU4XDataProviderInner::Empty))
114        }
115
116        /// Creates a provider that tries the current provider and then, if the current provider
117        /// doesn't support the data key, another provider `other`.
118        ///
119        /// This takes ownership of the `other` provider, leaving an empty provider in its place.
120        ///
121        /// The providers must be the same type (Any or Buffer). This condition is satisfied if
122        /// both providers originate from the same constructor, such as `create_from_byte_slice`
123        /// or `create_fs`. If the condition is not upheld, a runtime error occurs.
124        #[diplomat::rust_link(icu_provider_adapters::fork::ForkByKeyProvider, Typedef)]
125        #[diplomat::rust_link(
126            icu_provider_adapters::fork::predicates::MissingDataKeyPredicate,
127            Struct,
128            hidden
129        )]
130        pub fn fork_by_key(&mut self, other: &mut ICU4XDataProvider) -> Result<(), ICU4XError> {
131            #[allow(unused_imports)]
132            use ICU4XDataProviderInner::*;
133            *self = match (
134                core::mem::replace(&mut self.0, Destroyed),
135                core::mem::replace(&mut other.0, Destroyed),
136            ) {
137                (Destroyed, _) | (_, Destroyed) => Err(icu_provider::DataError::custom(
138                    "This provider has been destroyed",
139                ))?,
140                #[cfg(feature = "compiled_data")]
141                (Compiled, _) | (_, Compiled) => Err(icu_provider::DataError::custom(
142                    "The compiled provider cannot be modified",
143                ))?,
144                (Empty, Empty) => ICU4XDataProvider(ICU4XDataProviderInner::Empty),
145                #[cfg(feature = "buffer_provider")]
146                (Empty, b) | (b, Empty) => ICU4XDataProvider(b),
147                #[cfg(feature = "buffer_provider")]
148                (Buffer(a), Buffer(b)) => convert_buffer_provider(
149                    icu_provider_adapters::fork::ForkByKeyProvider::new(a, b),
150                ),
151            };
152            Ok(())
153        }
154
155        /// Same as `fork_by_key` but forks by locale instead of key.
156        #[diplomat::rust_link(
157            icu_provider_adapters::fork::predicates::MissingLocalePredicate,
158            Struct
159        )]
160        pub fn fork_by_locale(&mut self, other: &mut ICU4XDataProvider) -> Result<(), ICU4XError> {
161            #[allow(unused_imports)]
162            use ICU4XDataProviderInner::*;
163            *self = match (
164                core::mem::replace(&mut self.0, Destroyed),
165                core::mem::replace(&mut other.0, Destroyed),
166            ) {
167                (Destroyed, _) | (_, Destroyed) => Err(icu_provider::DataError::custom(
168                    "This provider has been destroyed",
169                ))?,
170                #[cfg(feature = "compiled_data")]
171                (Compiled, _) | (_, Compiled) => Err(icu_provider::DataError::custom(
172                    "The compiled provider cannot be modified",
173                ))?,
174                (Empty, Empty) => ICU4XDataProvider(ICU4XDataProviderInner::Empty),
175                #[cfg(feature = "buffer_provider")]
176                (Empty, b) | (b, Empty) => ICU4XDataProvider(b),
177                #[cfg(feature = "buffer_provider")]
178                (Buffer(a), Buffer(b)) => convert_buffer_provider(
179                    icu_provider_adapters::fork::ForkByErrorProvider::new_with_predicate(
180                        a,
181                        b,
182                        MissingLocalePredicate,
183                    ),
184                ),
185            };
186            Ok(())
187        }
188
189        /// Enables locale fallbacking for data requests made to this provider.
190        ///
191        /// Note that the test provider (from `create_test`) already has fallbacking enabled.
192        #[diplomat::rust_link(
193            icu_provider_adapters::fallback::LocaleFallbackProvider::try_new,
194            FnInStruct
195        )]
196        #[diplomat::rust_link(
197            icu_provider_adapters::fallback::LocaleFallbackProvider,
198            Struct,
199            compact
200        )]
201        pub fn enable_locale_fallback(&mut self) -> Result<(), ICU4XError> {
202            use ICU4XDataProviderInner::*;
203            *self = match core::mem::replace(&mut self.0, Destroyed) {
204                Destroyed => Err(icu_provider::DataError::custom(
205                    "This provider has been destroyed",
206                ))?,
207                #[cfg(feature = "compiled_data")]
208                Compiled => Err(icu_provider::DataError::custom(
209                    "The compiled provider cannot be modified",
210                ))?,
211                Empty => Err(icu_provider::DataErrorKind::MissingDataKey.into_error())?,
212                #[cfg(feature = "buffer_provider")]
213                Buffer(inner) => convert_buffer_provider(
214                    LocaleFallbackProvider::try_new_with_buffer_provider(inner)?,
215                ),
216            };
217            Ok(())
218        }
219
220        #[diplomat::rust_link(
221            icu_provider_adapters::fallback::LocaleFallbackProvider::new_with_fallbacker,
222            FnInStruct
223        )]
224        #[diplomat::rust_link(
225            icu_provider_adapters::fallback::LocaleFallbackProvider,
226            Struct,
227            compact
228        )]
229        #[allow(unused_variables)] // feature-gated
230        #[cfg(feature = "icu_locid_transform")]
231        pub fn enable_locale_fallback_with(
232            &mut self,
233            fallbacker: &crate::fallbacker::ffi::ICU4XLocaleFallbacker,
234        ) -> Result<(), ICU4XError> {
235            use ICU4XDataProviderInner::*;
236            *self = match core::mem::replace(&mut self.0, Destroyed) {
237                Destroyed => Err(icu_provider::DataError::custom(
238                    "This provider has been destroyed",
239                ))?,
240                #[cfg(feature = "compiled_data")]
241                Compiled => Err(icu_provider::DataError::custom(
242                    "The compiled provider cannot be modified",
243                ))?,
244                Empty => Err(icu_provider::DataErrorKind::MissingDataKey.into_error())?,
245                #[cfg(feature = "buffer_provider")]
246                Buffer(inner) => convert_buffer_provider(
247                    LocaleFallbackProvider::new_with_fallbacker(inner, fallbacker.0.clone()),
248                ),
249            };
250            Ok(())
251        }
252    }
253}
254
255macro_rules! load {
256    () => {
257        fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> {
258            use ICU4XDataProviderInner::*;
259            match self {
260                Destroyed => Err(icu_provider::DataError::custom(
261                    "This provider has been destroyed",
262                ))?,
263                Empty => EmptyDataProvider::new().load(req),
264                #[cfg(feature = "buffer_provider")]
265                Buffer(buffer_provider) => buffer_provider.as_deserializing().load(req),
266                #[cfg(feature = "compiled_data")]
267                Compiled => unreachable!(),
268            }
269        }
270    };
271}
272
273#[macro_export]
274macro_rules! call_constructor {
275    ($compiled:path [$pre_transform:ident => $transform:expr], $any:path, $buffer:path, $provider:expr $(, $args:expr)* $(,)?) => {
276        match &$provider.0 {
277            $crate::provider::ICU4XDataProviderInner::Destroyed => Err(icu_provider::DataError::custom(
278                "This provider has been destroyed",
279            ))?,
280            $crate::provider::ICU4XDataProviderInner::Empty => $any(&icu_provider_adapters::empty::EmptyDataProvider::new(), $($args,)*),
281            #[cfg(feature = "buffer_provider")]
282            $crate::provider::ICU4XDataProviderInner::Buffer(buffer_provider) => $buffer(buffer_provider, $($args,)*),
283            #[cfg(feature = "compiled_data")]
284            $crate::provider::ICU4XDataProviderInner::Compiled => { let $pre_transform = $compiled($($args,)*); $transform },
285        }
286    };
287    ($compiled:path, $any:path, $buffer:path, $provider:expr $(, $args:expr)* $(,)?) => {
288        match &$provider.0 {
289            $crate::provider::ICU4XDataProviderInner::Destroyed => Err(icu_provider::DataError::custom(
290                "This provider has been destroyed",
291            ))?,
292            $crate::provider::ICU4XDataProviderInner::Empty => $any(&icu_provider_adapters::empty::EmptyDataProvider::new(), $($args,)*),
293            #[cfg(feature = "buffer_provider")]
294            $crate::provider::ICU4XDataProviderInner::Buffer(buffer_provider) => $buffer(buffer_provider, $($args,)*),
295            #[cfg(feature = "compiled_data")]
296            $crate::provider::ICU4XDataProviderInner::Compiled => $compiled($($args,)*),
297        }
298    };
299}
300
301#[macro_export]
302macro_rules! call_constructor_unstable {
303    ($compiled:path [$pre_transform:ident => $transform:expr], $unstable:path, $provider:expr $(, $args:expr)* $(,)?) => {
304        match &$provider.0 {
305            $crate::provider::ICU4XDataProviderInner::Destroyed => Err(icu_provider::DataError::custom(
306                "This provider has been destroyed",
307            ))?,
308            $crate::provider::ICU4XDataProviderInner::Empty => $unstable(&icu_provider_adapters::empty::EmptyDataProvider::new(), $($args,)*),
309            #[cfg(feature = "buffer_provider")]
310            $crate::provider::ICU4XDataProviderInner::Buffer(buffer_provider) => $unstable(&icu_provider::AsDeserializingBufferProvider::as_deserializing(buffer_provider), $($args,)*),
311            #[cfg(feature = "compiled_data")]
312            $crate::provider::ICU4XDataProviderInner::Compiled => { let $pre_transform = $compiled($($args,)*); $transform },
313        }
314    };
315    ($compiled:path, $unstable:path, $provider:expr $(, $args:expr)* $(,)?) => {
316        match &$provider.0 {
317            $crate::provider::ICU4XDataProviderInner::Destroyed => Err(icu_provider::DataError::custom(
318                "This provider has been destroyed",
319            ))?,
320            $crate::provider::ICU4XDataProviderInner::Empty => $unstable(&icu_provider_adapters::empty::EmptyDataProvider::new(), $($args,)*),
321            #[cfg(feature = "buffer_provider")]
322            $crate::provider::ICU4XDataProviderInner::Buffer(buffer_provider) => $unstable(&icu_provider::AsDeserializingBufferProvider::as_deserializing(buffer_provider), $($args,)*),
323            #[cfg(feature = "compiled_data")]
324            $crate::provider::ICU4XDataProviderInner::Compiled => $compiled($($args,)*),
325        }
326    };
327}
328
329#[cfg(not(feature = "buffer_provider"))]
330impl<M> DataProvider<M> for ICU4XDataProviderInner
331where
332    M: KeyedDataMarker,
333{
334    load!();
335}
336
337#[cfg(feature = "buffer_provider")]
338impl<M> DataProvider<M> for ICU4XDataProviderInner
339where
340    M: KeyedDataMarker,
341    // Actual bound:
342    //     for<'de> <M::Yokeable as Yokeable<'de>>::Output: Deserialize<'de>,
343    // Necessary workaround bound (see `yoke::trait_hack` docs):
344    for<'de> YokeTraitHack<<M::Yokeable as Yokeable<'de>>::Output>: serde::Deserialize<'de>,
345{
346    load!();
347}