Skip to main content

icu_provider/export/
mod.rs

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 contains types required to export ICU4X data via the `icu_provider_export` crate.
6//! End users should not need to consume anything in this module.
7//!
8//! This module is enabled with the `export` Cargo feature.
9
10mod payload;
11
12#[doc(hidden)] // macro
13pub use payload::ExportBox;
14pub use payload::ExportMarker;
15
16use crate::prelude::*;
17use alloc::collections::BTreeSet;
18
19/// An object capable of exporting data payloads in some form.
20pub trait DataExporter: Sync {
21    /// Save a `payload` corresponding to the given marker and locale.
22    ///
23    /// Takes non-mut self as it can be called concurrently.
24    fn put_payload(
25        &self,
26        marker: DataMarkerInfo,
27        id: DataIdentifierBorrowed,
28        payload: &DataPayload<ExportMarker>,
29    ) -> Result<(), DataError>;
30
31    /// Function called for singleton markers.
32    ///
33    /// Takes non-mut self as it can be called concurrently.
34    fn flush_singleton(
35        &self,
36        marker: DataMarkerInfo,
37        payload: &DataPayload<ExportMarker>,
38        metadata: FlushMetadata,
39    ) -> Result<(), DataError> {
40        self.put_payload(marker, Default::default(), payload)?;
41        self.flush(marker, metadata)
42    }
43
44    /// Function called after a non-singleton marker has been fully enumerated.
45    ///
46    /// Takes non-mut self as it can be called concurrently.
47    fn flush(&self, _marker: DataMarkerInfo, _metadata: FlushMetadata) -> Result<(), DataError> {
48        Ok(())
49    }
50
51    /// This function has to be called before the object is dropped (after all
52    /// markers have been fully dumped). This conceptually takes ownership, so
53    /// clients *may not* interact with this object after close has been called.
54    fn close(&mut self) -> Result<ExporterCloseMetadata, DataError> {
55        Ok(ExporterCloseMetadata::default())
56    }
57}
58
59#[derive(Debug, Default)]
60#[allow(clippy::exhaustive_structs)] // newtype
61/// Contains information about a successful export.
62pub struct ExporterCloseMetadata(pub Option<Box<dyn core::any::Any>>);
63
64/// Metadata for [`DataExporter::flush`]
65#[non_exhaustive]
66#[derive(Debug, Copy, Clone, Default)]
67pub struct FlushMetadata {
68    /// Whether the data was generated in such a way that a [`DryDataProvider`] implementation
69    /// makes sense.
70    pub supports_dry_provider: bool,
71    /// The checksum to return with this data marker.
72    pub checksum: Option<u64>,
73}
74
75impl DataExporter for Box<dyn DataExporter> {
76    fn put_payload(
77        &self,
78        marker: DataMarkerInfo,
79        id: DataIdentifierBorrowed,
80        payload: &DataPayload<ExportMarker>,
81    ) -> Result<(), DataError> {
82        (**self).put_payload(marker, id, payload)
83    }
84
85    fn flush_singleton(
86        &self,
87        marker: DataMarkerInfo,
88        payload: &DataPayload<ExportMarker>,
89        metadata: FlushMetadata,
90    ) -> Result<(), DataError> {
91        (**self).flush_singleton(marker, payload, metadata)
92    }
93
94    fn flush(&self, marker: DataMarkerInfo, metadata: FlushMetadata) -> Result<(), DataError> {
95        (**self).flush(marker, metadata)
96    }
97
98    fn close(&mut self) -> Result<ExporterCloseMetadata, DataError> {
99        (**self).close()
100    }
101}
102
103/// A [`DynamicDataProvider`] that can be used for exporting data.
104///
105/// Use [`make_exportable_provider`] to implement this.
106pub trait ExportableProvider: IterableDynamicDataProvider<ExportMarker> + Sync {
107    /// Returns the set of supported markers
108    fn supported_markers(&self) -> BTreeSet<DataMarkerInfo>;
109}
110
111impl ExportableProvider for Box<dyn ExportableProvider> {
112    fn supported_markers(&self) -> BTreeSet<DataMarkerInfo> {
113        (**self).supported_markers()
114    }
115}
116
117/// This macro can be used on a data provider to allow it to be exported by `ExportDriver`.
118///
119/// Data generation 'compiles' data by using this data provider (which usually translates data from
120/// different sources and doesn't have to be efficient) to generate data structs, and then writing
121/// them to an efficient format like `BlobDataProvider` or `BakedDataProvider`. The requirements
122/// for `make_exportable_provider` are:
123/// * The data struct has to implement [`serde::Serialize`](::serde::Serialize) and [`databake::Bake`]
124/// * The provider needs to implement [`IterableDataProvider`] for all specified [`DataMarker`]s.
125///   This allows the generating code to know which [`DataIdentifierCow`]s to export.
126#[macro_export]
127#[doc(hidden)] // macro
128macro_rules! __make_exportable_provider {
129    ($provider:ty, [ $($(#[$cfg:meta])? $struct_m:ty),+, ]) => {
130        impl $crate::export::ExportableProvider for $provider {
131            fn supported_markers(&self) -> alloc::collections::BTreeSet<$crate::DataMarkerInfo> {
132                alloc::collections::BTreeSet::from_iter([
133                    $(
134                        $(#[$cfg])?
135                        <$struct_m>::INFO,
136                    )+
137                ])
138            }
139        }
140
141        $crate::dynutil::impl_dynamic_data_provider!(
142            $provider,
143            [ $($(#[$cfg])? $struct_m),+, ],
144            $crate::export::ExportMarker
145        );
146
147        $crate::dynutil::impl_iterable_dynamic_data_provider!(
148            $provider,
149            [ $($(#[$cfg])? $struct_m),+, ],
150            $crate::export::ExportMarker
151        );
152    };
153}
154#[doc(inline)]
155pub use __make_exportable_provider as make_exportable_provider;
156
157/// A `DataExporter` that forks to multiple `DataExporter`s.
158#[derive(Default)]
159pub struct MultiExporter(Vec<Box<dyn DataExporter>>);
160
161impl MultiExporter {
162    /// Creates a `MultiExporter` for the given exporters.
163    pub const fn new(exporters: Vec<Box<dyn DataExporter>>) -> Self {
164        Self(exporters)
165    }
166}
167
168impl core::fmt::Debug for MultiExporter {
169    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
170        f.debug_struct("MultiExporter")
171            .field("0", &format!("vec[len = {}]", self.0.len()))
172            .finish()
173    }
174}
175
176impl DataExporter for MultiExporter {
177    fn put_payload(
178        &self,
179        marker: DataMarkerInfo,
180        id: DataIdentifierBorrowed,
181        payload: &DataPayload<ExportMarker>,
182    ) -> Result<(), DataError> {
183        self.0
184            .iter()
185            .try_for_each(|e| e.put_payload(marker, id, payload))
186    }
187
188    fn flush_singleton(
189        &self,
190        marker: DataMarkerInfo,
191        payload: &DataPayload<ExportMarker>,
192        metadata: FlushMetadata,
193    ) -> Result<(), DataError> {
194        self.0
195            .iter()
196            .try_for_each(|e| e.flush_singleton(marker, payload, metadata))
197    }
198
199    fn flush(&self, marker: DataMarkerInfo, metadata: FlushMetadata) -> Result<(), DataError> {
200        self.0.iter().try_for_each(|e| e.flush(marker, metadata))
201    }
202
203    fn close(&mut self) -> Result<ExporterCloseMetadata, DataError> {
204        Ok(ExporterCloseMetadata(Some(Box::new(
205            self.0.iter_mut().try_fold(vec![], |mut m, e| {
206                m.push(e.close()?.0);
207                Ok::<_, DataError>(m)
208            })?,
209        ))))
210    }
211}