icu_provider_blob/
blob_data_provider.rs1use crate::blob_schema::BlobSchema;
6use icu_provider::buf::BufferFormat;
7use icu_provider::prelude::*;
8use icu_provider::Cart;
9use icu_provider::DynamicDryDataProvider;
10use yoke::*;
11
12#[derive(Clone)]
83pub struct BlobDataProvider {
84 pub(crate) data: Yoke<BlobSchema<'static>, Option<Cart>>,
85}
86
87impl core::fmt::Debug for BlobDataProvider {
88 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
89 f.debug_struct("BlobDataProvider")
90 .field("data", &"[...]")
91 .finish()
92 }
93}
94
95impl BlobDataProvider {
96 #[cfg(feature = "alloc")]
100 pub fn try_new_from_blob(blob: alloc::boxed::Box<[u8]>) -> Result<Self, DataError> {
101 Ok(Self {
102 data: Cart::try_make_yoke(blob, |bytes| {
103 BlobSchema::deserialize_and_check(&mut postcard::Deserializer::from_bytes(bytes))
104 })?,
105 })
106 }
107
108 pub fn try_new_from_static_blob(blob: &'static [u8]) -> Result<Self, DataError> {
111 Ok(Self {
112 data: Yoke::new_owned(BlobSchema::deserialize_and_check(
113 &mut postcard::Deserializer::from_bytes(blob),
114 )?),
115 })
116 }
117
118 #[doc(hidden)] pub fn internal_is_using_bigger_format(&self) -> bool {
120 matches!(self.data.get(), BlobSchema::V003Bigger(..))
121 }
122}
123
124impl DynamicDataProvider<BufferMarker> for BlobDataProvider {
125 fn load_data(
126 &self,
127 marker: DataMarkerInfo,
128 req: DataRequest,
129 ) -> Result<DataResponse<BufferMarker>, DataError> {
130 let payload: Yoke<(&[u8], Option<u64>), Option<Cart>> = self
131 .data
132 .try_map_project_cloned(|blob, _| blob.load(marker, req))?;
133 let mut metadata = DataResponseMetadata::default();
134 metadata.buffer_format = Some(BufferFormat::Postcard1);
135 metadata.checksum = payload.get().1;
136 Ok(DataResponse {
137 metadata,
138 payload: DataPayload::from_yoked_buffer(payload.map_project(|(bytes, _), _| bytes)),
139 })
140 }
141}
142
143impl DynamicDryDataProvider<BufferMarker> for BlobDataProvider {
144 fn dry_load_data(
145 &self,
146 marker: DataMarkerInfo,
147 req: DataRequest,
148 ) -> Result<DataResponseMetadata, DataError> {
149 self.data.get().load(marker, req)?;
150 let mut metadata = DataResponseMetadata::default();
151 metadata.buffer_format = Some(BufferFormat::Postcard1);
152 Ok(metadata)
153 }
154}
155
156#[cfg(feature = "alloc")]
158impl IterableDynamicDataProvider<BufferMarker> for BlobDataProvider {
159 fn iter_ids_for_marker(
160 &self,
161 marker: DataMarkerInfo,
162 ) -> Result<alloc::collections::BTreeSet<DataIdentifierCow<'_>>, DataError> {
163 self.data.get().iter_ids(marker)
164 }
165}
166
167#[cfg(test)]
168mod test {
169 use super::*;
170 use crate::export::*;
171 use icu_provider::export::*;
172 use icu_provider::hello_world::*;
173
174 icu_provider::data_marker!(HelloSingletonV1, HelloSingleton, is_singleton = true);
175 #[derive(Clone, Copy, yoke::Yokeable, zerofrom::ZeroFrom)]
176 pub struct HelloSingleton;
177
178 #[test]
179 fn test_empty() {
180 let mut blob: Vec<u8> = Vec::new();
181
182 {
183 let mut exporter = BlobExporter::new_with_sink(Box::new(&mut blob));
184
185 exporter
186 .flush(HelloWorldV1::INFO, Default::default())
187 .unwrap();
188
189 exporter.close().unwrap();
190 }
191
192 let provider = BlobDataProvider::try_new_from_blob(blob.into()).unwrap();
193
194 assert!(
195 matches!(
196 provider.load_data(HelloWorldV1::INFO, Default::default()),
197 Err(DataError {
198 kind: DataErrorKind::IdentifierNotFound,
199 ..
200 })
201 ),
202 "Empty blob test"
203 );
204 }
205
206 #[test]
207 fn test_singleton() {
208 let mut blob: Vec<u8> = Vec::new();
209
210 {
211 let mut exporter = BlobExporter::new_with_sink(Box::new(&mut blob));
212
213 exporter
214 .flush(HelloSingletonV1::INFO, Default::default())
215 .unwrap();
216
217 exporter.close().unwrap();
218 }
219
220 let provider = BlobDataProvider::try_new_from_blob(blob.into()).unwrap();
221
222 assert!(
223 matches!(
224 provider.load_data(
225 HelloSingletonV1::INFO,
226 DataRequest {
227 id: DataIdentifierBorrowed::for_locale(
228 &icu_locale_core::langid!("de").into()
229 ),
230 ..Default::default()
231 }
232 ),
233 Err(DataError {
234 kind: DataErrorKind::InvalidRequest,
235 ..
236 })
237 ),
238 "Singleton blob test"
239 );
240
241 assert!(
242 matches!(
243 provider.load_data(HelloSingletonV1::INFO, Default::default()),
244 Err(DataError {
245 kind: DataErrorKind::IdentifierNotFound,
246 ..
247 })
248 ),
249 "Singleton blob test"
250 );
251 }
252}