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 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
243pub 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 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 {}