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