1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
// 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 = "buffer_provider")]
use icu_provider::prelude::*;

#[cfg(feature = "buffer_provider")]
pub enum DataProviderInner {
    Destroyed,
    Buffer(alloc::boxed::Box<dyn BufferProvider + 'static>),
}

#[diplomat::bridge]
#[diplomat::abi_rename = "icu4x_{0}_mv1"]
#[diplomat::attr(auto, namespace = "icu4x")]
#[cfg(feature = "buffer_provider")]
pub mod ffi {
    use alloc::boxed::Box;

    use super::DataProviderInner;
    use crate::errors::ffi::DataError;

    #[diplomat::opaque]
    /// An ICU4X data provider, capable of loading ICU4X data keys from some source.
    ///
    /// Currently the only source supported is loading from "blob" formatted data from a bytes buffer or the file system.
    ///
    /// If you wish to use ICU4X's builtin "compiled data", use the version of the constructors that do not have `_with_provider`
    /// in their names.
    #[diplomat::rust_link(icu_provider, Mod)]
    pub struct DataProvider(pub DataProviderInner);

    fn convert_buffer_provider<D: icu_provider::buf::BufferProvider + 'static>(
        x: D,
    ) -> DataProvider {
        DataProvider(super::DataProviderInner::Buffer(Box::new(x)))
    }

    impl DataProvider {
        // These will be unused if almost *all* components are turned off, which is tedious and unproductive to gate for
        #[allow(unused)]
        pub(crate) fn call_constructor<F, Ret>(
            &self,
            ctor: F,
        ) -> Result<Ret, icu_provider::DataError>
        where
            F: FnOnce(
                &(dyn icu_provider::buf::BufferProvider + 'static),
            ) -> Result<Ret, icu_provider::DataError>,
        {
            match &self.0 {
                DataProviderInner::Destroyed => Err(icu_provider::DataError::custom(
                    "This provider has been destroyed",
                ))?,
                DataProviderInner::Buffer(ref buffer_provider) => ctor(buffer_provider),
            }
        }

        #[allow(unused)]
        #[cfg(any(feature = "datetime", feature = "decimal", feature = "experimental"))]
        pub(crate) fn call_constructor_custom_err<F, Ret, Err>(&self, ctor: F) -> Result<Ret, Err>
        where
            Err: From<icu_provider::DataError>,
            F: FnOnce(&(dyn icu_provider::buf::BufferProvider + 'static)) -> Result<Ret, Err>,
        {
            match &self.0 {
                DataProviderInner::Destroyed => Err(icu_provider::DataError::custom(
                    "This provider has been destroyed",
                ))?,
                DataProviderInner::Buffer(ref buffer_provider) => ctor(buffer_provider),
            }
        }

        /// Constructs an `FsDataProvider` and returns it as an [`DataProvider`].
        /// 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(any(dart, js), disable)]
        #[diplomat::attr(supports = fallible_constructors, named_constructor)]
        pub fn from_fs(path: &DiplomatStr) -> Result<Box<DataProvider>, DataError> {
            Ok(Box::new(convert_buffer_provider(
                icu_provider_fs::FsDataProvider::try_new(
                    // In the future we can start using OsString APIs to support non-utf8 paths
                    core::str::from_utf8(path)
                        .map_err(|_| DataError::Io)?
                        .into(),
                )?,
            )))
        }

        /// Constructs a `BlobDataProvider` and returns it as an [`DataProvider`].
        #[diplomat::rust_link(icu_provider_blob::BlobDataProvider, Struct)]
        #[diplomat::attr(supports = fallible_constructors, named_constructor)]
        #[diplomat::attr(not(supports = static_slices), disable)]
        pub fn from_byte_slice(
            blob: &'static [DiplomatByte],
        ) -> Result<Box<DataProvider>, DataError> {
            Ok(Box::new(convert_buffer_provider(
                icu_provider_blob::BlobDataProvider::try_new_from_static_blob(blob)?,
            )))
        }

        /// 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::ForkByMarkerProvider, Typedef)]
        #[diplomat::rust_link(
            icu_provider_adapters::fork::predicates::MarkerNotFoundPredicate,
            Struct,
            hidden
        )]
        pub fn fork_by_key(&mut self, other: &mut DataProvider) -> Result<(), DataError> {
            #[allow(unused_imports)]
            use DataProviderInner::*;
            *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",
                ))?,
                (Buffer(a), Buffer(b)) => convert_buffer_provider(
                    icu_provider_adapters::fork::ForkByMarkerProvider::new(a, b),
                ),
            };
            Ok(())
        }

        /// Same as `fork_by_key` but forks by locale instead of key.
        #[diplomat::rust_link(
            icu_provider_adapters::fork::predicates::IdentifierNotFoundPredicate,
            Struct
        )]
        pub fn fork_by_locale(&mut self, other: &mut DataProvider) -> Result<(), DataError> {
            #[allow(unused_imports)]
            use DataProviderInner::*;
            *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",
                ))?,
                (Buffer(a), Buffer(b)) => convert_buffer_provider(
                    icu_provider_adapters::fork::ForkByErrorProvider::new_with_predicate(
                        a,
                        b,
                        icu_provider_adapters::fork::predicates::IdentifierNotFoundPredicate,
                    ),
                ),
            };
            Ok(())
        }

        #[diplomat::rust_link(
            icu_provider_adapters::fallback::LocaleFallbackProvider::new,
            FnInStruct
        )]
        #[diplomat::rust_link(
            icu_provider_adapters::fallback::LocaleFallbackProvider,
            Struct,
            compact
        )]
        #[allow(unused_variables)] // feature-gated
        #[cfg(feature = "locale")]
        pub fn enable_locale_fallback_with(
            &mut self,
            fallbacker: &crate::fallbacker::ffi::LocaleFallbacker,
        ) -> Result<(), DataError> {
            use DataProviderInner::*;
            *self = match core::mem::replace(&mut self.0, Destroyed) {
                Destroyed => Err(icu_provider::DataError::custom(
                    "This provider has been destroyed",
                ))?,
                Buffer(inner) => convert_buffer_provider(
                    icu_provider_adapters::fallback::LocaleFallbackProvider::new(
                        inner,
                        fallbacker.0.clone(),
                    ),
                ),
            };
            Ok(())
        }
    }
}

#[cfg(feature = "buffer_provider")]
macro_rules! load {
    () => {
        fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> {
            use DataProviderInner::*;
            match self {
                Destroyed => Err(DataError::custom("This provider has been destroyed"))?,
                #[cfg(feature = "buffer_provider")]
                Buffer(buffer_provider) => buffer_provider.as_deserializing().load(req),
            }
        }
    };
}

#[cfg(feature = "buffer_provider")]
impl<M> DataProvider<M> for DataProviderInner
where
    M: DataMarker,
    // Actual bound:
    //     for<'de> <M::DataStruct as DataStruct<'de>>::Output: Deserialize<'de>,
    // Necessary workaround bound (see `yoke::trait_hack` docs):
    for<'de> yoke::trait_hack::YokeTraitHack<<M::DataStruct as yoke::Yokeable<'de>>::Output>:
        serde::Deserialize<'de>,
{
    load!();
}

#[macro_export]
macro_rules! call_constructor_unstable {
    ($unstable:path, $provider:expr $(, $args:expr)* $(,)?) => {
        match &$provider.0 {
            $crate::provider::DataProviderInner::Destroyed => Err(icu_provider::DataError::custom(
                "This provider has been destroyed",
            ))?,
            $crate::provider::DataProviderInner::Buffer(buffer_provider) => $unstable(&icu_provider::buf::AsDeserializingBufferProvider::as_deserializing(buffer_provider), $($args,)*),
        }
    };
}