spire_core/dataset/
data.rs1use 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, READ_BATCH};
10
11pub struct Data<T> {
13 inner: Arc<dyn DynDataset<T>>,
14}
15
16impl<T: Send + 'static> Data<T> {
17 #[must_use]
19 pub fn new(dataset: impl Dataset<T>) -> Self {
20 Self {
21 inner: Arc::new(dataset),
22 }
23 }
24
25 pub async fn write(&self, item: T) -> Result<()> {
31 self.inner.write(item).await
32 }
33
34 pub async fn write_all(&self, items: impl IntoIterator<Item = T>) -> Result<()> {
40 let items: Vec<T> = items.into_iter().collect();
41 if items.is_empty() {
42 return Ok(());
43 }
44 self.inner.write_batch(items).await
45 }
46
47 pub async fn read(&self) -> Result<Option<T>> {
53 self.inner.read().await
54 }
55
56 pub async fn read_batch(&self, max: usize) -> Result<Vec<T>> {
62 self.inner.read_batch(max).await
63 }
64
65 pub async fn read_all(&self) -> Result<Vec<T>> {
71 let mut items = Vec::with_capacity(self.size_hint().unwrap_or_default());
72 loop {
73 let batch = self.inner.read_batch(READ_BATCH).await?;
74 let last = batch.len() < READ_BATCH;
75 items.extend(batch);
76 if last {
77 return Ok(items);
78 }
79 }
80 }
81
82 pub fn into_stream(self) -> impl Stream<Item = Result<T>> + Send {
87 stream::try_unfold(self, |data| async move {
88 let item = data.read().await?;
89 Ok(item.map(|item| (item, data)))
90 })
91 }
92
93 pub fn into_batches(self, max: usize) -> impl Stream<Item = Result<Vec<T>>> + Send {
96 let max = max.max(1);
97 stream::try_unfold((self, false), move |(data, drained)| async move {
98 if drained {
99 return Ok(None);
100 }
101 let batch = data.read_batch(max).await?;
102 if batch.is_empty() {
103 return Ok(None);
104 }
105 let drained = batch.len() < max;
106 Ok(Some((batch, (data, drained))))
107 })
108 }
109
110 #[must_use]
112 pub fn size_hint(&self) -> Option<usize> {
113 self.inner.size_hint()
114 }
115}
116
117impl<T> Clone for Data<T> {
118 fn clone(&self) -> Self {
119 Self {
120 inner: self.inner.clone(),
121 }
122 }
123}
124
125impl<T> fmt::Debug for Data<T> {
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 f.debug_struct("Data")
128 .field("type", &std::any::type_name::<T>())
129 .finish_non_exhaustive()
130 }
131}
132
133impl<T: Send + 'static> Dataset<T> for Data<T> {
134 fn write(&self, item: T) -> impl Future<Output = Result<()>> + Send {
135 self.inner.write(item)
136 }
137
138 fn write_batch(&self, items: Vec<T>) -> impl Future<Output = Result<()>> + Send {
139 self.inner.write_batch(items)
140 }
141
142 fn read(&self) -> impl Future<Output = Result<Option<T>>> + Send {
143 self.inner.read()
144 }
145
146 fn read_batch(&self, max: usize) -> impl Future<Output = Result<Vec<T>>> + Send {
147 self.inner.read_batch(max)
148 }
149
150 fn size_hint(&self) -> Option<usize> {
151 self.inner.size_hint()
152 }
153}
154
155trait DynDataset<T>: Send + Sync + 'static {
157 fn write(&self, item: T) -> BoxFuture<'_, Result<()>>;
158 fn write_batch(&self, items: Vec<T>) -> BoxFuture<'_, Result<()>>;
159 fn read(&self) -> BoxFuture<'_, Result<Option<T>>>;
160 fn read_batch(&self, max: usize) -> BoxFuture<'_, Result<Vec<T>>>;
161 fn size_hint(&self) -> Option<usize>;
162}
163
164impl<T, D> DynDataset<T> for D
165where
166 T: Send + 'static,
167 D: Dataset<T>,
168{
169 fn write(&self, item: T) -> BoxFuture<'_, Result<()>> {
170 Dataset::write(self, item).boxed()
171 }
172
173 fn write_batch(&self, items: Vec<T>) -> BoxFuture<'_, Result<()>> {
174 Dataset::write_batch(self, items).boxed()
175 }
176
177 fn read(&self) -> BoxFuture<'_, Result<Option<T>>> {
178 Dataset::read(self).boxed()
179 }
180
181 fn read_batch(&self, max: usize) -> BoxFuture<'_, Result<Vec<T>>> {
182 Dataset::read_batch(self, max).boxed()
183 }
184
185 fn size_hint(&self) -> Option<usize> {
186 Dataset::size_hint(self)
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use futures::TryStreamExt;
193
194 use super::*;
195 use crate::dataset::InMemDataset;
196
197 #[tokio::test]
198 async fn batch_and_stream() {
199 let data = Data::new(InMemDataset::new());
200 data.write_all([1, 2, 3]).await.unwrap();
201 assert_eq!(data.size_hint(), Some(3));
202 assert_eq!(data.read().await.unwrap(), Some(1));
203 assert_eq!(
204 data.clone()
205 .into_stream()
206 .try_collect::<Vec<_>>()
207 .await
208 .unwrap(),
209 [2, 3]
210 );
211 assert_eq!(data.read_all().await.unwrap(), Vec::<i32>::new());
212 }
213
214 #[tokio::test]
215 async fn batches_stream_until_the_dataset_is_empty() {
216 let data = Data::new((1..=5).collect::<InMemDataset<u32>>());
217 let batches: Vec<Vec<u32>> = data.clone().into_batches(2).try_collect().await.unwrap();
218 assert_eq!(batches, [vec![1, 2], vec![3, 4], vec![5]]);
219
220 data.write_all([6, 7]).await.unwrap();
222 let batches: Vec<Vec<u32>> = data.clone().into_batches(2).try_collect().await.unwrap();
223 assert_eq!(batches, [vec![6, 7]]);
224 let batches: Vec<Vec<u32>> = data.into_batches(2).try_collect().await.unwrap();
225 assert_eq!(batches, Vec::<Vec<u32>>::new());
226 }
227}