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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
// 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 ).

//! 📚 *This module documents ICU4X constructor signatures.*
//!
//! One of the key differences between ICU4X and its parent projects, ICU4C and ICU4J, is in how
//! it deals with locale data.
//!
//! In ICU4X, data can always be explicitly passed to any function that requires data.
//! This enables ICU4X to achieve the following value propositions:
//!
//! 1. Configurable data sources (machine-readable data file, baked into code, JSON, etc).
//! 2. Dynamic data loading at runtime (load data on demand).
//! 3. Reduced overhead and code size (data is resolved locally at each call site).
//! 4. Explicit support for multiple ICU4X instances sharing data.
//!
//! However, as manual data management can be tedious, ICU4X also has a `compiled_data`
//! default Cargo feature that includes data and makes ICU4X work out-of-the box.
//!
//! Subsequently, there are 4 versions of all Rust ICU4X functions that use data:
//!
//! 1. `*`
//! 2. `*_unstable`
//! 3. `*_with_any_provider`
//! 4. `*_with_buffer_provider`
//!
//! # Which constructor should I use?
//!
//! ## When to use `*`
//!
//! If you don't want to customize data at runtime (i.e. if you don't care about code size,
//! updating your data, etc.) you can use the `compiled_data` Cargo feature and don't have to think
//! about where your data comes from.
//!
//! These constructors are sometimes `const` functions, this way Rust can most effectively optimize
//! your usage of ICU4X.
//!
//! ## When to use `*_unstable`
//!
//! Use this constructor if your data provider implements the [`DataProvider`] trait for all
//! data structs in *current and future* ICU4X versions. Examples:
//!
//! 1. `BakedDataProvider` generated for the specific ICU4X minor version
//! 2. Anything with a _blanket_ [`DataProvider`] impl
//!
//! Since the exact set of bounds may change at any time, including in minor SemVer releases,
//! it is the client's responsibility to guarantee that the requirement is upheld.
//!
//! ## When to use `*_with_any_provider`
//!
//! Use this constructor if you need to use a provider that implements [`AnyProvider`] but not
//! [`DataProvider`]. Examples:
//!
//! 1. [`FixedProvider`]
//! 2. [`ForkByMarkerProvider`] between two providers implementing [`AnyProvider`]
//! 3. Providers that cache or override certain markers but not others and therefore
//!    can't implement [`DataProvider`]
//!
//! ## When to use `*_with_buffer_provider`
//!
//! Use this constructor if your data originates as byte buffers that need to be deserialized.
//! All such providers should implement [`BufferProvider`]. Examples:
//!
//! 1. [`BlobDataProvider`]
//! 2. [`FsDataProvider`]
//! 3. [`ForkByMarkerProvider`] between two providers implementing [`BufferProvider`]
//!
//! Please note that you must enable the `serde` Cargo feature on each crate in which you use the
//! `*_with_buffer_provider` constructor.
//!
//! # Data Versioning Policy
//!
//! The `*_with_any_provider` and `*_with_buffer_provider` functions will succeed to compile and
//! run if given a data provider supporting all of the markers required for the object being
//! constructed, either the current or any previous version within the same SemVer major release.
//! For example, if a data file is built to support FooFormatter version 1.1, then FooFormatter
//! version 1.2 will be able to read the same data file. Likewise, backwards-compatible markers can
//! always be included by `icu_provider_export` to support older library versions.
//!
//! The `*_unstable` functions are only guaranteed to work on data built for the exact same minor version
//! of ICU4X. The advantage of the `*_unstable` functions is that they result in the smallest code
//! size and allow for automatic data slicing when `BakedDataProvider` is used. However, the type
//! bounds of this function may change over time, breaking SemVer guarantees. These functions
//! should therefore only be used when you have full control over your data lifecycle at compile
//! time.
//!
//! # Data Providers Over FFI
//!
//! Over FFI, there is only one data provider type: [`ICU4XDataProvider`]. Internally, it is an
//! `enum` between`dyn `[`BufferProvider`] and a unit compiled data variant.
//!
//! To control for code size, there are two Cargo features, `compiled_data` and `buffer_provider`,
//! that enable the corresponding items in the enum.
//!
//! In Rust ICU4X, a similar enum approach was not taken because:
//!
//! 1. Feature-gating the enum branches gets complex across crates.
//! 2. Without feature gating, users need to carry Serde code even if they're not using it,
//!    violating one of the core value propositions of ICU4X.
//! 3. We could reduce the number of constructors from 4 to 2 but not to 1, so the educational
//!    benefit is limited.
//!
//! [`DataProvider`]: crate::DataProvider
//! [`BufferProvider`]: crate::buf::BufferProvider
//! [`AnyProvider`]: crate::any::AnyProvider
//! [`FixedProvider`]: ../../icu_provider_adapters/fixed/struct.FixedProvider.html
//! [`ForkByMarkerProvider`]: ../../icu_provider_adapters/fork/struct.ForkByMarkerProvider.html
//! [`BlobDataProvider`]: ../../icu_provider_blob/struct.BlobDataProvider.html
//! [`StaticDataProvider`]: ../../icu_provider_blob/struct.StaticDataProvider.html
//! [`FsDataProvider`]: ../../icu_provider_blob/struct.FsDataProvider.html
//! [`ICU4XDataProvider`]: ../../icu_capi/provider/ffi/struct.ICU4XDataProvider.html

#[doc(hidden)] // macro
#[macro_export]
macro_rules! gen_any_buffer_unstable_docs {
    (ANY, $data:path) => {
        concat!(
            "A version of [`", stringify!($data), "`] that uses custom data ",
            "provided by an [`AnyProvider`](icu_provider::any::AnyProvider).\n\n",
            "[📚 Help choosing a constructor](icu_provider::constructors)",
        )
    };
    (BUFFER, $data:path) => {
        concat!(
            "A version of [`", stringify!($data), "`] that uses custom data ",
            "provided by a [`BufferProvider`](icu_provider::buf::BufferProvider).\n\n",
            "✨ *Enabled with the `serde` feature.*\n\n",
            "[📚 Help choosing a constructor](icu_provider::constructors)",
        )
    };
    (UNSTABLE, $data:path) => {
        concat!(
            "A version of [`", stringify!($data), "`] that uses custom data ",
            "provided by a [`DataProvider`](icu_provider::DataProvider).\n\n",
            "[📚 Help choosing a constructor](icu_provider::constructors)\n\n",
            "<div class=\"stab unstable\">⚠️ The bounds on <tt>provider</tt> may change over time, including in SemVer minor releases.</div>"
        )
    };
}

/// Usage:
///
/// ```rust,ignore
/// gen_any_buffer_data_constructors!((locale, options: FooOptions) -> error: DataError,
///    /// Some docs
///   functions: [try_new, try_new_with_any_provider, try_new_with_buffer_provider, try_new_unstable]
/// );
/// ```
///
/// `functions` can be omitted if using standard names. If `locale` is omitted, the method will not take a locale. You can specify any number
/// of options arguments, including zero.
///
/// By default the macro will generate a `try_new`. If you wish to skip it, write `try_new: skip`
///
/// Errors can be specified as `error: SomeError` or `result: SomeType`, where `error` will get it wrapped in `Result<Self, SomeError>`.
#[allow(clippy::crate_in_macro_def)] // by convention each crate's data provider is `crate::provider::Baked`
#[doc(hidden)] // macro
#[macro_export]
macro_rules! gen_any_buffer_data_constructors {
    // Allow people to omit the functions
    (($($args:tt)*) -> $error_kind:ident: $error_ty:ty, $(#[$doc:meta])*) => {
        $crate::gen_any_buffer_data_constructors!(
            ($($args)*) -> $error_kind: $error_ty,
            $(#[$doc])*
            functions: [
                try_new,
                try_new_with_any_provider,
                try_new_with_buffer_provider,
                try_new_unstable,
                Self,
            ]
        );
    };
    // Allow people to specify errors instead of results
    (($($args:tt)*) -> error: $error_ty:path, $(#[$doc:meta])* functions: [$baked:ident$(: $baked_cmd:ident)?, $any:ident, $buffer:ident, $unstable:ident $(, $struct:ident)? $(,)?]) => {
        $crate::gen_any_buffer_data_constructors!(
            ($($args)*) -> result: Result<Self, $error_ty>,
            $(#[$doc])*
            functions: [
                $baked$(: $baked_cmd)?,
                $any,
                $buffer,
                $unstable
                $(, $struct)?
            ]
        );
    };

    // locale shorthand
    ((locale, $($args:tt)*) -> result: $result_ty:ty, $(#[$doc:meta])* functions: [$baked:ident$(: $baked_cmd:ident)?, $any:ident, $buffer:ident, $unstable:ident $(, $struct:ident)? $(,)?]) => {
        $crate::gen_any_buffer_data_constructors!(
            (locale: &$crate::DataLocale, $($args)*) -> result: $result_ty,
            $(#[$doc])*
            functions: [
                $baked$(: $baked_cmd)?,
                $any,
                $buffer,
                $unstable
                $(, $struct)?
            ]
        );
    };
    ((locale) -> result: $result_ty:ty, $(#[$doc:meta])* functions: [$baked:ident$(: $baked_cmd:ident)?, $any:ident, $buffer:ident, $unstable:ident $(, $struct:ident)? $(,)?]) => {
        $crate::gen_any_buffer_data_constructors!(
            (locale: &$crate::DataLocale) -> result: $result_ty,
            $(#[$doc])*
            functions: [
                $baked$(: $baked_cmd)?,
                $any,
                $buffer,
                $unstable
                $(, $struct)?
            ]
        );
    };


    (($($options_arg:ident: $options_ty:ty),*) -> result: $result_ty:ty, $(#[$doc:meta])* functions: [$baked:ident, $any:ident, $buffer:ident, $unstable:ident $(, $struct:ident)? $(,)?]) => {
        #[cfg(feature = "compiled_data")]
        $(#[$doc])*
        ///
        /// ✨ *Enabled with the `compiled_data` Cargo feature.*
        ///
        /// [📚 Help choosing a constructor](icu_provider::constructors)
        pub fn $baked($($options_arg: $options_ty),* ) -> $result_ty {
            $($struct :: )? $unstable(&crate::provider::Baked $(, $options_arg)* )
        }

        $crate::gen_any_buffer_data_constructors!(
            ($($options_arg: $options_ty),*) -> result: $result_ty,
            $(#[$doc])*
            functions: [
                $baked: skip,
                $any,
                $buffer,
                $unstable
                $(, $struct)?
            ]
        );
    };
    (($($options_arg:ident: $options_ty:ty),*) -> result: $result_ty:ty, $(#[$doc:meta])* functions: [$baked:ident: skip, $any:ident, $buffer:ident, $unstable:ident $(, $struct:ident)? $(,)?]) => {
        #[doc = $crate::gen_any_buffer_unstable_docs!(ANY, $($struct ::)? $baked)]
        pub fn $any(provider: &(impl $crate::any::AnyProvider + ?Sized) $(, $options_arg: $options_ty)* ) -> $result_ty {
            use $crate::any::AsDowncastingAnyProvider;
            $($struct :: )? $unstable(&provider.as_downcasting()  $(, $options_arg)* )
        }
        #[cfg(feature = "serde")]
        #[doc = $crate::gen_any_buffer_unstable_docs!(BUFFER, $($struct ::)? $baked)]
        pub fn $buffer(provider: &(impl $crate::buf::BufferProvider + ?Sized) $(, $options_arg: $options_ty)* ) -> $result_ty {
            use $crate::buf::AsDeserializingBufferProvider;
            $($struct :: )? $unstable(&provider.as_deserializing()  $(, $options_arg)* )
        }
    };
}