• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 //! �� *This module documents ICU4X constructor signatures.*
6 //!
7 //! One of the key differences between ICU4X and its parent projects, ICU4C and ICU4J, is in how
8 //! it deals with locale data.
9 //!
10 //! In ICU4X, data can always be explicitly passed to any function that requires data.
11 //! This enables ICU4X to achieve the following value propositions:
12 //!
13 //! 1. Configurable data sources (machine-readable data file, baked into code, JSON, etc).
14 //! 2. Dynamic data loading at runtime (load data on demand).
15 //! 3. Reduced overhead and code size (data is resolved locally at each call site).
16 //! 4. Explicit support for multiple ICU4X instances sharing data.
17 //!
18 //! However, as manual data management can be tedious, ICU4X also has a `compiled_data`
19 //! default Cargo feature that includes data and makes ICU4X work out-of-the box.
20 //!
21 //! Subsequently, there are 3 versions of all Rust ICU4X functions that use data:
22 //!
23 //! 1. `*`
24 //! 2. `*_unstable`
25 //! 3. `*_with_buffer_provider`
26 //!
27 //! # Which constructor should I use?
28 //!
29 //! ## When to use `*`
30 //!
31 //! If you don't want to customize data at runtime (i.e. if you don't care about code size,
32 //! updating your data, etc.) you can use the `compiled_data` Cargo feature and don't have to think
33 //! about where your data comes from.
34 //!
35 //! These constructors are sometimes `const` functions, this way Rust can most effectively optimize
36 //! your usage of ICU4X.
37 //!
38 //! ## When to use `*_unstable`
39 //!
40 //! Use this constructor if your data provider implements the [`DataProvider`] trait for all
41 //! data structs in *current and future* ICU4X versions. Examples:
42 //!
43 //! 1. `BakedDataProvider` generated for the specific ICU4X minor version
44 //! 2. Anything with a _blanket_ [`DataProvider`] impl
45 //!
46 //! Since the exact set of bounds may change at any time, including in minor SemVer releases,
47 //! it is the client's responsibility to guarantee that the requirement is upheld.
48 //!
49 //! ## When to use `*_with_buffer_provider`
50 //!
51 //! Use this constructor if your data originates as byte buffers that need to be deserialized.
52 //! All such providers should implement [`BufferProvider`]. Examples:
53 //!
54 //! 1. [`BlobDataProvider`]
55 //! 2. [`FsDataProvider`]
56 //! 3. [`ForkByMarkerProvider`] between two providers implementing [`BufferProvider`]
57 //!
58 //! Please note that you must enable the `serde` Cargo feature on each crate in which you use the
59 //! `*_with_buffer_provider` constructor.
60 //!
61 //! # Data Versioning Policy
62 //!
63 //! The `*_with_buffer_provider` functions will succeed to compile and
64 //! run if given a data provider supporting all of the markers required for the object being
65 //! constructed, either the current or any previous version within the same SemVer major release.
66 //! For example, if a data file is built to support FooFormatter version 1.1, then FooFormatter
67 //! version 1.2 will be able to read the same data file. Likewise, backwards-compatible markers can
68 //! always be included by `icu_provider_export` to support older library versions.
69 //!
70 //! The `*_unstable` functions are only guaranteed to work on data built for the exact same minor version
71 //! of ICU4X. The advantage of the `*_unstable` functions is that they result in the smallest code
72 //! size and allow for automatic data slicing when `BakedDataProvider` is used. However, the type
73 //! bounds of this function may change over time, breaking SemVer guarantees. These functions
74 //! should therefore only be used when you have full control over your data lifecycle at compile
75 //! time.
76 //!
77 //! # Data Providers Over FFI
78 //!
79 //! Over FFI, there is only one data provider type: [`ICU4XDataProvider`]. Internally, it is an
80 //! `enum` between`dyn `[`BufferProvider`] and a unit compiled data variant.
81 //!
82 //! To control for code size, there are two Cargo features, `compiled_data` and `buffer_provider`,
83 //! that enable the corresponding items in the enum.
84 //!
85 //! In Rust ICU4X, a similar enum approach was not taken because:
86 //!
87 //! 1. Feature-gating the enum branches gets complex across crates.
88 //! 2. Without feature gating, users need to carry Serde code even if they're not using it,
89 //!    violating one of the core value propositions of ICU4X.
90 //! 3. We could reduce the number of constructors from 4 to 2 but not to 1, so the educational
91 //!    benefit is limited.
92 //!
93 //! [`DataProvider`]: crate::DataProvider
94 //! [`BufferProvider`]: crate::buf::BufferProvider
95 //! [`FixedProvider`]: ../../icu_provider_adapters/fixed/struct.FixedProvider.html
96 //! [`ForkByMarkerProvider`]: ../../icu_provider_adapters/fork/struct.ForkByMarkerProvider.html
97 //! [`BlobDataProvider`]: ../../icu_provider_blob/struct.BlobDataProvider.html
98 //! [`StaticDataProvider`]: ../../icu_provider_blob/struct.StaticDataProvider.html
99 //! [`FsDataProvider`]: ../../icu_provider_blob/struct.FsDataProvider.html
100 //! [`ICU4XDataProvider`]: ../../icu_capi/provider/ffi/struct.ICU4XDataProvider.html
101 
102 #[doc(hidden)] // macro
103 #[macro_export]
104 macro_rules! gen_buffer_unstable_docs {
105     (BUFFER, $data:path) => {
106         concat!(
107             "A version of [`", stringify!($data), "`] that uses custom data ",
108             "provided by a [`BufferProvider`](icu_provider::buf::BufferProvider).\n\n",
109             "✨ *Enabled with the `serde` feature.*\n\n",
110             "[�� Help choosing a constructor](icu_provider::constructors)",
111         )
112     };
113     (UNSTABLE, $data:path) => {
114         concat!(
115             "A version of [`", stringify!($data), "`] that uses custom data ",
116             "provided by a [`DataProvider`](icu_provider::DataProvider).\n\n",
117             "[�� Help choosing a constructor](icu_provider::constructors)\n\n",
118             "<div class=\"stab unstable\">⚠️ The bounds on <tt>provider</tt> may change over time, including in SemVer minor releases.</div>"
119         )
120     };
121 }
122 
123 /// Usage:
124 ///
125 /// ```rust,ignore
126 /// gen_buffer_data_constructors!((locale, options: FooOptions) -> error: DataError,
127 ///    /// Some docs
128 ///   functions: [try_new, try_new_with_buffer_provider, try_new_unstable]
129 /// );
130 /// ```
131 ///
132 /// `functions` can be omitted if using standard names. If `locale` is omitted, the method will not take a locale. You can specify any number
133 /// of options arguments, including zero.
134 ///
135 /// By default the macro will generate a `try_new`. If you wish to skip it, write `try_new: skip`
136 ///
137 /// Errors can be specified as `error: SomeError` or `result: SomeType`, where `error` will get it wrapped in `Result<Self, SomeError>`.
138 #[allow(clippy::crate_in_macro_def)] // by convention each crate's data provider is `crate::provider::Baked`
139 #[doc(hidden)] // macro
140 #[macro_export]
141 macro_rules! gen_buffer_data_constructors {
142     // Allow people to omit the functions
143     (($($args:tt)*) -> $error_kind:ident: $error_ty:ty, $(#[$doc:meta])*) => {
144         $crate::gen_buffer_data_constructors!(
145             ($($args)*) -> $error_kind: $error_ty,
146             $(#[$doc])*
147             functions: [
148                 try_new,
149                 try_new_with_buffer_provider,
150                 try_new_unstable,
151                 Self,
152             ]
153         );
154     };
155     // Allow people to specify errors instead of results
156     (($($args:tt)*) -> error: $error_ty:path, $(#[$doc:meta])* functions: [$baked:ident$(: $baked_cmd:ident)?, $buffer:ident, $unstable:ident $(, $struct:ident)? $(,)?]) => {
157         $crate::gen_buffer_data_constructors!(
158             ($($args)*) -> result: Result<Self, $error_ty>,
159             $(#[$doc])*
160             functions: [
161                 $baked$(: $baked_cmd)?,
162                 $buffer,
163                 $unstable
164                 $(, $struct)?
165             ]
166         );
167     };
168 
169     // locale shorthand
170     ((locale, $($args:tt)*) -> result: $result_ty:ty, $(#[$doc:meta])* functions: [$baked:ident$(: $baked_cmd:ident)?, $buffer:ident, $unstable:ident $(, $struct:ident)? $(,)?]) => {
171         $crate::gen_buffer_data_constructors!(
172             (locale: &$crate::DataLocale, $($args)*) -> result: $result_ty,
173             $(#[$doc])*
174             functions: [
175                 $baked$(: $baked_cmd)?,
176                 $buffer,
177                 $unstable
178                 $(, $struct)?
179             ]
180         );
181     };
182     ((locale) -> result: $result_ty:ty, $(#[$doc:meta])* functions: [$baked:ident$(: $baked_cmd:ident)?, $buffer:ident, $unstable:ident $(, $struct:ident)? $(,)?]) => {
183         $crate::gen_buffer_data_constructors!(
184             (locale: &$crate::DataLocale) -> result: $result_ty,
185             $(#[$doc])*
186             functions: [
187                 $baked$(: $baked_cmd)?,
188                 $buffer,
189                 $unstable
190                 $(, $struct)?
191             ]
192         );
193     };
194 
195 
196     (($($options_arg:ident: $options_ty:ty),*) -> result: $result_ty:ty, $(#[$doc:meta])* functions: [$baked:ident, $buffer:ident, $unstable:ident $(, $struct:ident)? $(,)?]) => {
197         #[cfg(feature = "compiled_data")]
198         $(#[$doc])*
199         ///
200         /// ✨ *Enabled with the `compiled_data` Cargo feature.*
201         ///
202         /// [�� Help choosing a constructor](icu_provider::constructors)
203         pub fn $baked($($options_arg: $options_ty),* ) -> $result_ty {
204             $($struct :: )? $unstable(&crate::provider::Baked $(, $options_arg)* )
205         }
206 
207         $crate::gen_buffer_data_constructors!(
208             ($($options_arg: $options_ty),*) -> result: $result_ty,
209             $(#[$doc])*
210             functions: [
211                 $baked: skip,
212                 $buffer,
213                 $unstable
214                 $(, $struct)?
215             ]
216         );
217     };
218     (($($options_arg:ident: $options_ty:ty),*) -> result: $result_ty:ty, $(#[$doc:meta])* functions: [$baked:ident: skip, $buffer:ident, $unstable:ident $(, $struct:ident)? $(,)?]) => {
219         #[cfg(feature = "serde")]
220         #[doc = $crate::gen_buffer_unstable_docs!(BUFFER, $($struct ::)? $baked)]
221         pub fn $buffer(provider: &(impl $crate::buf::BufferProvider + ?Sized) $(, $options_arg: $options_ty)* ) -> $result_ty {
222             use $crate::buf::AsDeserializingBufferProvider;
223             $($struct :: )? $unstable(&provider.as_deserializing()  $(, $options_arg)* )
224         }
225     };
226 }
227