any_storage/
any.rs

1use std::borrow::Cow;
2use std::io::Result;
3use std::pin::Pin;
4use std::task::{Context, Poll};
5
6use futures::StreamExt;
7
8#[derive(Clone, Debug, derive_more::From)]
9#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
10#[cfg_attr(feature = "serde", serde(tag = "type", rename_all = "kebab-case"))]
11pub enum AnyStoreConfig {
12    Http(crate::http::HttpStoreConfig),
13    Local(crate::local::LocalStoreConfig),
14    PCloud(crate::pcloud::PCloudStoreConfig),
15}
16
17impl AnyStoreConfig {
18    /// Create a [`AnyStore`] based on the configuration
19    pub fn build(&self) -> std::io::Result<AnyStore> {
20        match self {
21            Self::Http(inner) => inner.build().map(AnyStore::Http),
22            Self::Local(inner) => inner.build().map(AnyStore::Local),
23            Self::PCloud(inner) => inner.build().map(AnyStore::PCloud),
24        }
25    }
26}
27
28#[derive(Clone, Debug, derive_more::From)]
29pub enum AnyStore {
30    Http(crate::http::HttpStore),
31    Local(crate::local::LocalStore),
32    PCloud(crate::pcloud::PCloudStore),
33}
34
35impl crate::Store for AnyStore {
36    type File = AnyStoreFile;
37    type Directory = AnyStoreDirectory;
38
39    async fn root(&self) -> Result<Self::Directory> {
40        match self {
41            Self::Http(inner) => inner.root().await.map(AnyStoreDirectory::Http),
42            Self::Local(inner) => inner.root().await.map(AnyStoreDirectory::Local),
43            Self::PCloud(inner) => inner.root().await.map(AnyStoreDirectory::PCloud),
44        }
45    }
46
47    async fn get_dir<P: Into<std::path::PathBuf>>(&self, path: P) -> Result<Self::Directory> {
48        match self {
49            Self::Http(inner) => inner.get_dir(path).await.map(AnyStoreDirectory::Http),
50            Self::Local(inner) => inner.get_dir(path).await.map(AnyStoreDirectory::Local),
51            Self::PCloud(inner) => inner.get_dir(path).await.map(AnyStoreDirectory::PCloud),
52        }
53    }
54
55    async fn get_file<P: Into<std::path::PathBuf>>(&self, path: P) -> Result<Self::File> {
56        match self {
57            Self::Http(inner) => inner.get_file(path).await.map(AnyStoreFile::Http),
58            Self::Local(inner) => inner.get_file(path).await.map(AnyStoreFile::Local),
59            Self::PCloud(inner) => inner.get_file(path).await.map(AnyStoreFile::PCloud),
60        }
61    }
62}
63
64#[derive(Debug, derive_more::From)]
65pub enum AnyStoreFile {
66    Http(crate::http::HttpStoreFile),
67    Local(crate::local::LocalStoreFile),
68    PCloud(crate::pcloud::PCloudStoreFile),
69}
70
71impl crate::StoreFile for AnyStoreFile {
72    type FileReader = AnyStoreFileReader;
73    type FileWriter = AnyStoreFileWriter;
74    type Metadata = AnyStoreFileMetadata;
75
76    async fn exists(&self) -> Result<bool> {
77        match self {
78            Self::Http(inner) => inner.exists().await,
79            Self::Local(inner) => inner.exists().await,
80            Self::PCloud(inner) => inner.exists().await,
81        }
82    }
83
84    fn filename(&self) -> Option<Cow<'_, str>> {
85        match self {
86            Self::Http(inner) => inner.filename(),
87            Self::Local(inner) => inner.filename(),
88            Self::PCloud(inner) => inner.filename(),
89        }
90    }
91
92    async fn metadata(&self) -> Result<Self::Metadata> {
93        match self {
94            Self::Http(inner) => inner.metadata().await.map(AnyStoreFileMetadata::Http),
95            Self::Local(inner) => inner.metadata().await.map(AnyStoreFileMetadata::Local),
96            Self::PCloud(inner) => inner.metadata().await.map(AnyStoreFileMetadata::PCloud),
97        }
98    }
99
100    async fn read<R: std::ops::RangeBounds<u64>>(&self, range: R) -> Result<Self::FileReader> {
101        match self {
102            Self::Http(inner) => inner.read(range).await.map(AnyStoreFileReader::Http),
103            Self::Local(inner) => inner.read(range).await.map(AnyStoreFileReader::Local),
104            Self::PCloud(inner) => inner.read(range).await.map(AnyStoreFileReader::PCloud),
105        }
106    }
107
108    async fn write(&self, options: crate::WriteOptions) -> Result<Self::FileWriter> {
109        match self {
110            Self::Http(inner) => inner.write(options).await.map(AnyStoreFileWriter::Http),
111            Self::Local(inner) => inner.write(options).await.map(AnyStoreFileWriter::Local),
112            Self::PCloud(inner) => inner.write(options).await.map(AnyStoreFileWriter::PCloud),
113        }
114    }
115}
116
117#[derive(Debug)]
118pub enum AnyStoreFileReader {
119    Http(crate::http::HttpStoreFileReader),
120    Local(crate::local::LocalStoreFileReader),
121    PCloud(crate::pcloud::PCloudStoreFileReader),
122}
123
124impl tokio::io::AsyncRead for AnyStoreFileReader {
125    fn poll_read(
126        self: Pin<&mut Self>,
127        cx: &mut Context<'_>,
128        buf: &mut tokio::io::ReadBuf<'_>,
129    ) -> Poll<Result<()>> {
130        let this = self.get_mut();
131        match this {
132            Self::Http(inner) => Pin::new(inner).poll_read(cx, buf),
133            Self::Local(inner) => Pin::new(inner).poll_read(cx, buf),
134            Self::PCloud(inner) => Pin::new(inner).poll_read(cx, buf),
135        }
136    }
137}
138
139impl crate::StoreFileReader for AnyStoreFileReader {}
140
141#[derive(Clone, Debug, derive_more::From)]
142pub enum AnyStoreFileMetadata {
143    Http(crate::http::HttpStoreFileMetadata),
144    Local(crate::local::LocalStoreFileMetadata),
145    PCloud(crate::pcloud::PCloudStoreFileMetadata),
146}
147
148impl crate::StoreMetadata for AnyStoreFileMetadata {
149    fn created(&self) -> u64 {
150        match self {
151            Self::Http(inner) => inner.created(),
152            Self::Local(inner) => inner.created(),
153            Self::PCloud(inner) => inner.created(),
154        }
155    }
156
157    fn modified(&self) -> u64 {
158        match self {
159            Self::Http(inner) => inner.modified(),
160            Self::Local(inner) => inner.modified(),
161            Self::PCloud(inner) => inner.modified(),
162        }
163    }
164
165    fn size(&self) -> u64 {
166        match self {
167            Self::Http(inner) => inner.size(),
168            Self::Local(inner) => inner.size(),
169            Self::PCloud(inner) => inner.size(),
170        }
171    }
172}
173
174#[derive(Debug)]
175pub enum AnyStoreFileWriter {
176    Http(crate::NoopFileWriter),
177    Local(crate::local::LocalStoreFileWriter),
178    PCloud(crate::pcloud::PCloudStoreFileWriter),
179}
180
181impl tokio::io::AsyncWrite for AnyStoreFileWriter {
182    fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
183        let this = self.get_mut();
184
185        match this {
186            Self::Http(inner) => Pin::new(inner).poll_write(cx, buf),
187            Self::Local(inner) => Pin::new(inner).poll_write(cx, buf),
188            Self::PCloud(inner) => Pin::new(inner).poll_write(cx, buf),
189        }
190    }
191
192    fn poll_flush(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Result<()>> {
193        let this = self.get_mut();
194
195        match this {
196            Self::Http(inner) => Pin::new(inner).poll_flush(cx),
197            Self::Local(inner) => Pin::new(inner).poll_flush(cx),
198            Self::PCloud(inner) => Pin::new(inner).poll_flush(cx),
199        }
200    }
201
202    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
203        let this = self.get_mut();
204
205        match this {
206            Self::Http(inner) => Pin::new(inner).poll_shutdown(cx),
207            Self::Local(inner) => Pin::new(inner).poll_shutdown(cx),
208            Self::PCloud(inner) => Pin::new(inner).poll_shutdown(cx),
209        }
210    }
211}
212
213impl crate::StoreFileWriter for AnyStoreFileWriter {}
214
215#[derive(Debug, derive_more::From)]
216pub enum AnyStoreDirectory {
217    Http(crate::http::HttpStoreDirectory),
218    Local(crate::local::LocalStoreDirectory),
219    PCloud(crate::pcloud::PCloudStoreDirectory),
220}
221
222impl crate::StoreDirectory for AnyStoreDirectory {
223    type Entry = AnyStoreEntry;
224    type Reader = AnyStoreDirectoryReader;
225
226    async fn exists(&self) -> Result<bool> {
227        match self {
228            Self::Http(inner) => inner.exists().await,
229            Self::Local(inner) => inner.exists().await,
230            Self::PCloud(inner) => inner.exists().await,
231        }
232    }
233
234    async fn read(&self) -> Result<Self::Reader> {
235        match self {
236            Self::Http(inner) => inner.read().await.map(AnyStoreDirectoryReader::Http),
237            Self::Local(inner) => inner.read().await.map(AnyStoreDirectoryReader::Local),
238            Self::PCloud(inner) => inner.read().await.map(AnyStoreDirectoryReader::PCloud),
239        }
240    }
241}
242
243/// Type alias for entries in the store, which can be files or directories.
244pub type AnyStoreEntry = crate::Entry<AnyStoreFile, AnyStoreDirectory>;
245
246impl From<crate::http::HttpStoreEntry> for AnyStoreEntry {
247    fn from(value: crate::http::HttpStoreEntry) -> Self {
248        match value {
249            crate::Entry::File(file) => crate::Entry::File(file.into()),
250            crate::Entry::Directory(directory) => crate::Entry::Directory(directory.into()),
251        }
252    }
253}
254
255impl From<crate::local::LocalStoreEntry> for AnyStoreEntry {
256    fn from(value: crate::local::LocalStoreEntry) -> Self {
257        match value {
258            crate::Entry::File(file) => crate::Entry::File(file.into()),
259            crate::Entry::Directory(directory) => crate::Entry::Directory(directory.into()),
260        }
261    }
262}
263
264impl From<crate::pcloud::PCloudStoreEntry> for AnyStoreEntry {
265    fn from(value: crate::pcloud::PCloudStoreEntry) -> Self {
266        match value {
267            crate::Entry::File(file) => crate::Entry::File(file.into()),
268            crate::Entry::Directory(directory) => crate::Entry::Directory(directory.into()),
269        }
270    }
271}
272
273#[derive(Debug)]
274pub enum AnyStoreDirectoryReader {
275    Http(crate::http::HttpStoreDirectoryReader),
276    Local(crate::local::LocalStoreDirectoryReader),
277    PCloud(crate::pcloud::PCloudStoreDirectoryReader),
278}
279
280fn from_poll_entry<E: Into<AnyStoreEntry>>(
281    item: Poll<Option<Result<E>>>,
282) -> Poll<Option<Result<AnyStoreEntry>>> {
283    match item {
284        Poll::Ready(Some(Ok(inner))) => Poll::Ready(Some(Ok(inner.into()))),
285        Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err(err))),
286        Poll::Ready(None) => Poll::Ready(None),
287        Poll::Pending => Poll::Pending,
288    }
289}
290
291impl futures::Stream for AnyStoreDirectoryReader {
292    type Item = Result<AnyStoreEntry>;
293
294    /// Polls for the next directory entry.
295    ///
296    /// This function is used to asynchronously retrieve the next entry in the
297    /// directory.
298    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
299        match self.get_mut() {
300            Self::Http(inner) => from_poll_entry(inner.poll_next_unpin(cx)),
301            Self::Local(inner) => from_poll_entry(inner.poll_next_unpin(cx)),
302            Self::PCloud(inner) => from_poll_entry(inner.poll_next_unpin(cx)),
303        }
304    }
305}
306
307impl crate::StoreDirectoryReader<AnyStoreEntry> for AnyStoreDirectoryReader {}