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