Skip to main content

spire_core/dataset/
data.rs

1use std::fmt;
2use std::future::Future;
3use std::sync::Arc;
4
5use futures::future::BoxFuture;
6use futures::{FutureExt, Stream, stream};
7
8use crate::Result;
9use crate::dataset::Dataset;
10
11/// How many items [`Data::read_all`] takes at once.
12pub const READ_BATCH: usize = 1024;
13
14/// A cloneable, type-erased handle to a [`Dataset`].
15pub struct Data<T> {
16    inner: Arc<dyn DynDataset<T>>,
17}
18
19impl<T: Send + 'static> Data<T> {
20    /// Wraps `dataset`.
21    #[must_use]
22    pub fn new(dataset: impl Dataset<T>) -> Self {
23        Self {
24            inner: Arc::new(dataset),
25        }
26    }
27
28    /// Stores an item.
29    ///
30    /// # Errors
31    ///
32    /// Returns an error if the underlying dataset fails.
33    pub async fn write(&self, item: T) -> Result<()> {
34        self.inner.write(item).await
35    }
36
37    /// Stores every item of `items`, stopping at the first error.
38    ///
39    /// # Errors
40    ///
41    /// Returns an error if the underlying dataset fails.
42    pub async fn write_all(&self, items: impl IntoIterator<Item = T>) -> Result<()> {
43        for item in items {
44            self.inner.write(item).await?;
45        }
46        Ok(())
47    }
48
49    /// Takes the next item, or `None` if the dataset is empty.
50    ///
51    /// # Errors
52    ///
53    /// Returns an error if the underlying dataset fails.
54    pub async fn read(&self) -> Result<Option<T>> {
55        self.inner.read().await
56    }
57
58    /// Takes up to `max` items at once, see [`Dataset::read_batch`].
59    ///
60    /// # Errors
61    ///
62    /// Returns an error if the underlying dataset fails.
63    pub async fn read_batch(&self, max: usize) -> Result<Vec<T>> {
64        self.inner.read_batch(max).await
65    }
66
67    /// Takes every item until the dataset is empty, in batches of
68    /// [`READ_BATCH`] items.
69    ///
70    /// # Errors
71    ///
72    /// Returns an error if the underlying dataset fails.
73    pub async fn read_all(&self) -> Result<Vec<T>> {
74        let mut items = Vec::with_capacity(self.size_hint().unwrap_or_default());
75        loop {
76            let batch = self.inner.read_batch(READ_BATCH).await?;
77            let last = batch.len() < READ_BATCH;
78            items.extend(batch);
79            if last {
80                return Ok(items);
81            }
82        }
83    }
84
85    /// Returns a stream that takes items one at a time until the dataset is
86    /// empty, so it never takes an item it does not yield. To drain a large
87    /// dataset, [`into_batches`](Self::into_batches) takes fewer trips to
88    /// its storage.
89    pub fn into_stream(self) -> impl Stream<Item = Result<T>> + Send {
90        stream::try_unfold(self, |data| async move {
91            let item = data.read().await?;
92            Ok(item.map(|item| (item, data)))
93        })
94    }
95
96    /// Returns a stream that takes batches of up to `max` items, see
97    /// [`Dataset::read_batch`], until the dataset is empty.
98    pub fn into_batches(self, max: usize) -> impl Stream<Item = Result<Vec<T>>> + Send {
99        let max = max.max(1);
100        stream::try_unfold((self, false), move |(data, drained)| async move {
101            if drained {
102                return Ok(None);
103            }
104            let batch = data.read_batch(max).await?;
105            if batch.is_empty() {
106                return Ok(None);
107            }
108            let drained = batch.len() < max;
109            Ok(Some((batch, (data, drained))))
110        })
111    }
112
113    /// Returns the number of stored items, if known.
114    #[must_use]
115    pub fn size_hint(&self) -> Option<usize> {
116        self.inner.size_hint()
117    }
118}
119
120impl<T> Clone for Data<T> {
121    fn clone(&self) -> Self {
122        Self {
123            inner: self.inner.clone(),
124        }
125    }
126}
127
128impl<T> fmt::Debug for Data<T> {
129    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130        f.debug_struct("Data")
131            .field("type", &std::any::type_name::<T>())
132            .finish_non_exhaustive()
133    }
134}
135
136impl<T: Send + 'static> Dataset<T> for Data<T> {
137    fn write(&self, item: T) -> impl Future<Output = Result<()>> + Send {
138        self.inner.write(item)
139    }
140
141    fn read(&self) -> impl Future<Output = Result<Option<T>>> + Send {
142        self.inner.read()
143    }
144
145    fn read_batch(&self, max: usize) -> impl Future<Output = Result<Vec<T>>> + Send {
146        self.inner.read_batch(max)
147    }
148
149    fn size_hint(&self) -> Option<usize> {
150        self.inner.size_hint()
151    }
152}
153
154/// An object-safe [`Dataset`].
155trait DynDataset<T>: Send + Sync + 'static {
156    fn write(&self, item: T) -> BoxFuture<'_, Result<()>>;
157    fn read(&self) -> BoxFuture<'_, Result<Option<T>>>;
158    fn read_batch(&self, max: usize) -> BoxFuture<'_, Result<Vec<T>>>;
159    fn size_hint(&self) -> Option<usize>;
160}
161
162impl<T, D> DynDataset<T> for D
163where
164    T: Send + 'static,
165    D: Dataset<T>,
166{
167    fn write(&self, item: T) -> BoxFuture<'_, Result<()>> {
168        Dataset::write(self, item).boxed()
169    }
170
171    fn read(&self) -> BoxFuture<'_, Result<Option<T>>> {
172        Dataset::read(self).boxed()
173    }
174
175    fn read_batch(&self, max: usize) -> BoxFuture<'_, Result<Vec<T>>> {
176        Dataset::read_batch(self, max).boxed()
177    }
178
179    fn size_hint(&self) -> Option<usize> {
180        Dataset::size_hint(self)
181    }
182}
183
184#[cfg(test)]
185mod tests {
186    use futures::TryStreamExt;
187
188    use super::*;
189    use crate::dataset::InMemDataset;
190
191    #[tokio::test]
192    async fn batch_and_stream() {
193        let data = Data::new(InMemDataset::new());
194        data.write_all([1, 2, 3]).await.unwrap();
195        assert_eq!(data.size_hint(), Some(3));
196        assert_eq!(data.read().await.unwrap(), Some(1));
197        assert_eq!(
198            data.clone()
199                .into_stream()
200                .try_collect::<Vec<_>>()
201                .await
202                .unwrap(),
203            [2, 3]
204        );
205        assert_eq!(data.read_all().await.unwrap(), Vec::<i32>::new());
206    }
207
208    #[tokio::test]
209    async fn batches_stream_until_the_dataset_is_empty() {
210        let data = Data::new((1..=5).collect::<InMemDataset<u32>>());
211        let batches: Vec<Vec<u32>> = data.clone().into_batches(2).try_collect().await.unwrap();
212        assert_eq!(batches, [vec![1, 2], vec![3, 4], vec![5]]);
213
214        // A last full batch ends on the next, empty read.
215        data.write_all([6, 7]).await.unwrap();
216        let batches: Vec<Vec<u32>> = data.clone().into_batches(2).try_collect().await.unwrap();
217        assert_eq!(batches, [vec![6, 7]]);
218        let batches: Vec<Vec<u32>> = data.into_batches(2).try_collect().await.unwrap();
219        assert_eq!(batches, Vec::<Vec<u32>>::new());
220    }
221}