vortex_io/filesystem/
prefix.rs1use std::sync::Arc;
5
6use async_trait::async_trait;
7use futures::StreamExt;
8use futures::stream::BoxStream;
9use vortex_error::VortexResult;
10
11use crate::VortexReadAt;
12use crate::filesystem::FileListing;
13use crate::filesystem::FileSystem;
14use crate::filesystem::FileSystemRef;
15
16#[derive(Debug)]
21pub struct PrefixFileSystem {
22 inner: FileSystemRef,
23 prefix: String,
24}
25
26impl PrefixFileSystem {
27 pub fn new(inner: FileSystemRef, prefix: String) -> Self {
28 let prefix = format!("{}/", prefix.trim_matches('/'));
30 Self { inner, prefix }
31 }
32}
33
34#[async_trait]
35impl FileSystem for PrefixFileSystem {
36 fn list(&self, prefix: &str) -> BoxStream<'_, VortexResult<FileListing>> {
37 let full_prefix = format!("{}{}", self.prefix, prefix.trim_start_matches('/'));
38
39 let strip_prefix = self.prefix.clone();
40 self.inner
41 .list(&full_prefix)
42 .map(move |result| {
43 result.map(|mut listing| {
44 listing.path = listing
45 .path
46 .strip_prefix(&strip_prefix)
47 .unwrap_or(&listing.path)
48 .to_string();
49 listing
50 })
51 })
52 .boxed()
53 }
54
55 async fn head(&self, path: &str) -> VortexResult<Option<FileListing>> {
56 let full_path = format!("{}{}", self.prefix, path.trim_start_matches('/'));
57 Ok(self.inner.head(&full_path).await?.map(|mut listing| {
58 listing.path = listing
59 .path
60 .strip_prefix(&self.prefix)
61 .unwrap_or(&listing.path)
62 .to_string();
63 listing
64 }))
65 }
66
67 async fn open_read(&self, path: &str) -> VortexResult<Arc<dyn VortexReadAt>> {
68 self.inner
69 .open_read(&format!("{}{}", self.prefix, path.trim_start_matches('/')))
70 .await
71 }
72
73 async fn delete(&self, path: &str) -> VortexResult<()> {
74 self.inner
75 .delete(&format!("{}{}", self.prefix, path.trim_start_matches('/')))
76 .await
77 }
78}
79
80impl dyn FileSystem + 'static {
81 pub fn with_prefix(self: Arc<Self>, prefix: String) -> FileSystemRef {
83 Arc::new(PrefixFileSystem::new(self, prefix))
84 }
85}