shared/cap/
mod.rs

1use cap_std::fs::{Metadata, OpenOptions};
2use std::{
3    collections::VecDeque,
4    path::{Path, PathBuf},
5    sync::Arc,
6};
7use tokio::sync::RwLock;
8pub use utils::{AsyncReadDir, AsyncWalkDir, ReadDir, WalkDir};
9
10mod utils;
11
12#[derive(Debug, Clone)]
13pub struct CapFilesystem {
14    pub base_path: Arc<PathBuf>,
15    pub(super) inner: Arc<RwLock<Option<Arc<cap_std::fs::Dir>>>>,
16}
17
18impl CapFilesystem {
19    pub async fn async_new(base_path: PathBuf) -> Result<Self, std::io::Error> {
20        let base_path = Arc::new(base_path);
21
22        let inner = tokio::task::spawn_blocking({
23            let base_path = base_path.clone();
24
25            move || cap_std::fs::Dir::open_ambient_dir(&*base_path, cap_std::ambient_authority())
26        })
27        .await??;
28
29        Ok(Self {
30            base_path,
31            inner: Arc::new(RwLock::new(Some(Arc::new(inner)))),
32        })
33    }
34
35    pub fn new(base_path: PathBuf) -> Result<Self, std::io::Error> {
36        let base_path = Arc::new(base_path);
37
38        let inner = cap_std::fs::Dir::open_ambient_dir(&*base_path, cap_std::ambient_authority())?;
39
40        Ok(Self {
41            base_path,
42            inner: Arc::new(RwLock::new(Some(Arc::new(inner)))),
43        })
44    }
45
46    pub fn new_uninitialized(base_path: PathBuf) -> Self {
47        Self {
48            base_path: Arc::new(base_path),
49            inner: Arc::new(RwLock::new(None)),
50        }
51    }
52
53    #[inline]
54    pub async fn is_uninitialized(&self) -> bool {
55        self.inner.read().await.is_none()
56    }
57
58    #[inline]
59    pub async fn async_get_inner(&self) -> Result<Arc<cap_std::fs::Dir>, anyhow::Error> {
60        let inner = self.inner.read().await;
61
62        inner
63            .clone()
64            .ok_or_else(|| anyhow::anyhow!("filesystem not initialized"))
65    }
66
67    #[inline]
68    pub fn get_inner(&self) -> Result<Arc<cap_std::fs::Dir>, anyhow::Error> {
69        let inner = self.inner.blocking_read();
70
71        inner
72            .clone()
73            .ok_or_else(|| anyhow::anyhow!("filesystem not initialized"))
74    }
75
76    #[inline]
77    pub fn resolve_path(path: &Path) -> PathBuf {
78        let mut result = PathBuf::new();
79
80        for component in path.components() {
81            match component {
82                std::path::Component::ParentDir => {
83                    if !result.as_os_str().is_empty()
84                        && result.components().next_back() != Some(std::path::Component::RootDir)
85                    {
86                        result.pop();
87                    }
88                }
89                _ => {
90                    result.push(component);
91                }
92            }
93        }
94
95        result
96    }
97
98    #[inline]
99    pub fn relative_path(&self, path: &Path) -> PathBuf {
100        Self::resolve_path(if let Ok(path) = path.strip_prefix(&*self.base_path) {
101            path
102        } else if let Ok(path) = path.strip_prefix("/") {
103            path
104        } else {
105            path
106        })
107    }
108
109    pub async fn async_create_dir_all(&self, path: impl AsRef<Path>) -> Result<(), anyhow::Error> {
110        let path = self.relative_path(path.as_ref());
111
112        let inner = self.async_get_inner().await?;
113        tokio::task::spawn_blocking(move || inner.create_dir_all(path)).await??;
114
115        Ok(())
116    }
117
118    pub fn create_dir_all(&self, path: impl AsRef<Path>) -> Result<(), anyhow::Error> {
119        let path = self.relative_path(path.as_ref());
120
121        let inner = self.get_inner()?;
122        inner.create_dir_all(path)?;
123
124        Ok(())
125    }
126
127    pub async fn async_create_dir(&self, path: impl AsRef<Path>) -> Result<(), anyhow::Error> {
128        let path = self.relative_path(path.as_ref());
129
130        let inner = self.async_get_inner().await?;
131        tokio::task::spawn_blocking(move || inner.create_dir(path)).await??;
132
133        Ok(())
134    }
135
136    pub fn create_dir(&self, path: impl AsRef<Path>) -> Result<(), anyhow::Error> {
137        let path = self.relative_path(path.as_ref());
138
139        let inner = self.get_inner()?;
140        inner.create_dir(path)?;
141
142        Ok(())
143    }
144
145    pub async fn async_remove_dir(&self, path: impl AsRef<Path>) -> Result<(), anyhow::Error> {
146        let path = self.relative_path(path.as_ref());
147
148        let inner = self.async_get_inner().await?;
149        tokio::task::spawn_blocking(move || inner.remove_dir(path)).await??;
150
151        Ok(())
152    }
153
154    pub fn remove_dir(&self, path: impl AsRef<Path>) -> Result<(), anyhow::Error> {
155        let path = self.relative_path(path.as_ref());
156
157        let inner = self.get_inner()?;
158        inner.remove_dir(path)?;
159
160        Ok(())
161    }
162
163    pub async fn async_remove_dir_all(&self, path: impl AsRef<Path>) -> Result<(), anyhow::Error> {
164        let path = self.relative_path(path.as_ref());
165
166        let inner = self.async_get_inner().await?;
167        tokio::task::spawn_blocking(move || inner.remove_dir_all(path)).await??;
168
169        Ok(())
170    }
171
172    pub fn remove_dir_all(&self, path: impl AsRef<Path>) -> Result<(), anyhow::Error> {
173        let path = self.relative_path(path.as_ref());
174
175        let inner = self.get_inner()?;
176        inner.remove_dir_all(path)?;
177
178        Ok(())
179    }
180
181    pub async fn async_remove_file(&self, path: impl AsRef<Path>) -> Result<(), anyhow::Error> {
182        let path = self.relative_path(path.as_ref());
183
184        let inner = self.async_get_inner().await?;
185        tokio::task::spawn_blocking(move || inner.remove_file(path)).await??;
186
187        Ok(())
188    }
189
190    pub fn remove_file(&self, path: impl AsRef<Path>) -> Result<(), anyhow::Error> {
191        let path = self.relative_path(path.as_ref());
192
193        let inner = self.get_inner()?;
194        inner.remove_file(path)?;
195
196        Ok(())
197    }
198
199    pub async fn async_rename(
200        &self,
201        from: impl AsRef<Path>,
202        to_dir: &CapFilesystem,
203        to: impl AsRef<Path>,
204    ) -> Result<(), anyhow::Error> {
205        let from = self.relative_path(from.as_ref());
206        let to = self.relative_path(to.as_ref());
207
208        let inner = self.async_get_inner().await?;
209        let to_inner = to_dir.async_get_inner().await?;
210        tokio::task::spawn_blocking(move || inner.rename(from, &to_inner, to)).await??;
211
212        Ok(())
213    }
214
215    pub fn rename(
216        &self,
217        from: impl AsRef<Path>,
218        to_dir: &CapFilesystem,
219        to: impl AsRef<Path>,
220    ) -> Result<(), anyhow::Error> {
221        let from = self.relative_path(from.as_ref());
222        let to = self.relative_path(to.as_ref());
223
224        let inner = self.get_inner()?;
225        let to_inner = to_dir.get_inner()?;
226        inner.rename(from, &to_inner, to)?;
227
228        Ok(())
229    }
230
231    pub async fn async_metadata(&self, path: impl AsRef<Path>) -> Result<Metadata, anyhow::Error> {
232        let path = self.relative_path(path.as_ref());
233
234        let metadata = if path.components().next().is_none() {
235            cap_std::fs::Metadata::from_just_metadata(tokio::fs::metadata(&*self.base_path).await?)
236        } else {
237            let inner = self.async_get_inner().await?;
238
239            tokio::task::spawn_blocking(move || inner.metadata(path)).await??
240        };
241
242        Ok(metadata)
243    }
244
245    pub fn metadata(&self, path: impl AsRef<Path>) -> Result<Metadata, anyhow::Error> {
246        let path = self.relative_path(path.as_ref());
247
248        let metadata = if path.components().next().is_none() {
249            cap_std::fs::Metadata::from_just_metadata(std::fs::metadata(&*self.base_path)?)
250        } else {
251            let inner = self.get_inner()?;
252
253            inner.metadata(path)?
254        };
255
256        Ok(metadata)
257    }
258
259    pub async fn async_symlink_metadata(
260        &self,
261        path: impl AsRef<Path>,
262    ) -> Result<Metadata, anyhow::Error> {
263        let path = self.relative_path(path.as_ref());
264
265        let metadata = if path.components().next().is_none() {
266            cap_std::fs::Metadata::from_just_metadata(
267                tokio::fs::symlink_metadata(&*self.base_path).await?,
268            )
269        } else {
270            let inner = self.async_get_inner().await?;
271
272            tokio::task::spawn_blocking(move || inner.symlink_metadata(path)).await??
273        };
274
275        Ok(metadata)
276    }
277
278    pub fn symlink_metadata(&self, path: impl AsRef<Path>) -> Result<Metadata, anyhow::Error> {
279        let path = self.relative_path(path.as_ref());
280
281        let metadata = if path.components().next().is_none() {
282            cap_std::fs::Metadata::from_just_metadata(std::fs::symlink_metadata(&*self.base_path)?)
283        } else {
284            let inner = self.get_inner()?;
285
286            inner.symlink_metadata(path)?
287        };
288
289        Ok(metadata)
290    }
291
292    pub async fn async_canonicalize(
293        &self,
294        path: impl AsRef<Path>,
295    ) -> Result<PathBuf, anyhow::Error> {
296        let path = self.relative_path(path.as_ref());
297        if path.components().next().is_none() {
298            return Ok(path);
299        }
300
301        let inner = self.async_get_inner().await?;
302        let canonicalized = tokio::task::spawn_blocking(move || inner.canonicalize(path)).await??;
303
304        Ok(canonicalized)
305    }
306
307    pub fn canonicalize(&self, path: impl AsRef<Path>) -> Result<PathBuf, anyhow::Error> {
308        let path = self.relative_path(path.as_ref());
309        if path.components().next().is_none() {
310            return Ok(path);
311        }
312
313        let inner = self.get_inner()?;
314        let canonicalized = inner.canonicalize(path)?;
315
316        Ok(canonicalized)
317    }
318
319    pub async fn async_read_link(&self, path: impl AsRef<Path>) -> Result<PathBuf, anyhow::Error> {
320        let path = self.relative_path(path.as_ref());
321
322        let inner = self.async_get_inner().await?;
323        let link = tokio::task::spawn_blocking(move || inner.read_link(path)).await??;
324
325        Ok(link)
326    }
327
328    pub fn read_link(&self, path: impl AsRef<Path>) -> Result<PathBuf, anyhow::Error> {
329        let path = self.relative_path(path.as_ref());
330
331        let inner = self.get_inner()?;
332        let link = inner.read_link(path)?;
333
334        Ok(link)
335    }
336
337    pub async fn async_read_link_contents(
338        &self,
339        path: impl AsRef<Path>,
340    ) -> Result<PathBuf, anyhow::Error> {
341        let path = self.relative_path(path.as_ref());
342
343        let inner = self.async_get_inner().await?;
344        let link_contents =
345            tokio::task::spawn_blocking(move || inner.read_link_contents(path)).await??;
346
347        Ok(link_contents)
348    }
349
350    pub fn read_link_contents(&self, path: impl AsRef<Path>) -> Result<PathBuf, anyhow::Error> {
351        let path = self.relative_path(path.as_ref());
352
353        let inner = self.get_inner()?;
354        let link_contents = inner.read_link_contents(path)?;
355
356        Ok(link_contents)
357    }
358
359    pub async fn async_read_to_string(
360        &self,
361        path: impl AsRef<Path>,
362    ) -> Result<String, anyhow::Error> {
363        let path = self.relative_path(path.as_ref());
364
365        let inner = self.async_get_inner().await?;
366        let content = tokio::task::spawn_blocking(move || inner.read_to_string(path)).await??;
367
368        Ok(content)
369    }
370
371    pub fn read_to_string(&self, path: impl AsRef<Path>) -> Result<String, anyhow::Error> {
372        let path = self.relative_path(path.as_ref());
373
374        let inner = self.get_inner()?;
375        let content = inner.read_to_string(path)?;
376
377        Ok(content)
378    }
379
380    pub async fn async_open(
381        &self,
382        path: impl AsRef<Path>,
383    ) -> Result<tokio::fs::File, anyhow::Error> {
384        let path = self.relative_path(path.as_ref());
385
386        let inner = self.async_get_inner().await?;
387        let file = tokio::task::spawn_blocking(move || inner.open(path)).await??;
388
389        Ok(tokio::fs::File::from_std(file.into_std()))
390    }
391
392    pub fn open(&self, path: impl AsRef<Path>) -> Result<std::fs::File, anyhow::Error> {
393        let path = self.relative_path(path.as_ref());
394
395        let inner = self.get_inner()?;
396        let file = inner.open(path)?;
397
398        Ok(file.into_std())
399    }
400
401    pub async fn async_open_with(
402        &self,
403        path: impl AsRef<Path>,
404        options: OpenOptions,
405    ) -> Result<tokio::fs::File, anyhow::Error> {
406        let path = self.relative_path(path.as_ref());
407
408        let inner = self.async_get_inner().await?;
409        let file = tokio::task::spawn_blocking(move || inner.open_with(path, &options)).await??;
410
411        Ok(tokio::fs::File::from_std(file.into_std()))
412    }
413
414    pub fn open_with(
415        &self,
416        path: impl AsRef<Path>,
417        options: OpenOptions,
418    ) -> Result<std::fs::File, anyhow::Error> {
419        let path = self.relative_path(path.as_ref());
420
421        let inner = self.get_inner()?;
422        let file = inner.open_with(path, &options)?;
423
424        Ok(file.into_std())
425    }
426
427    pub async fn async_write(
428        &self,
429        path: impl AsRef<Path>,
430        data: Vec<u8>,
431    ) -> Result<(), anyhow::Error> {
432        let path = self.relative_path(path.as_ref());
433
434        let inner = self.async_get_inner().await?;
435        tokio::task::spawn_blocking(move || inner.write(path, data)).await??;
436
437        Ok(())
438    }
439
440    pub fn write(&self, path: impl AsRef<Path>, data: Vec<u8>) -> Result<(), anyhow::Error> {
441        let path = self.relative_path(path.as_ref());
442
443        let inner = self.get_inner()?;
444        inner.write(path, data)?;
445
446        Ok(())
447    }
448
449    pub async fn async_create(
450        &self,
451        path: impl AsRef<Path>,
452    ) -> Result<tokio::fs::File, anyhow::Error> {
453        let path = self.relative_path(path.as_ref());
454
455        let inner = self.async_get_inner().await?;
456        let file = tokio::task::spawn_blocking(move || inner.create(path)).await??;
457
458        Ok(tokio::fs::File::from_std(file.into_std()))
459    }
460
461    pub fn create(&self, path: impl AsRef<Path>) -> Result<std::fs::File, anyhow::Error> {
462        let path = self.relative_path(path.as_ref());
463
464        let inner = self.get_inner()?;
465        let file = inner.create(path)?;
466
467        Ok(file.into_std())
468    }
469
470    pub async fn async_copy(
471        &self,
472        from: impl AsRef<Path>,
473        to_dir: &CapFilesystem,
474        to: impl AsRef<Path>,
475    ) -> Result<u64, anyhow::Error> {
476        let from = self.relative_path(from.as_ref());
477        let to = self.relative_path(to.as_ref());
478
479        let inner = self.async_get_inner().await?;
480        let to_inner = to_dir.async_get_inner().await?;
481        let bytes_copied =
482            tokio::task::spawn_blocking(move || inner.copy(from, &to_inner, to)).await??;
483
484        Ok(bytes_copied)
485    }
486
487    pub fn copy(
488        &self,
489        from: impl AsRef<Path>,
490        to_dir: &CapFilesystem,
491        to: impl AsRef<Path>,
492    ) -> Result<u64, anyhow::Error> {
493        let from = self.relative_path(from.as_ref());
494        let to = self.relative_path(to.as_ref());
495
496        let inner = self.get_inner()?;
497        let to_inner = to_dir.get_inner()?;
498        let bytes_copied = inner.copy(from, &to_inner, to)?;
499
500        Ok(bytes_copied)
501    }
502
503    pub async fn async_read_dir_all(
504        &self,
505        path: impl AsRef<Path>,
506    ) -> Result<Vec<String>, anyhow::Error> {
507        let mut read_dir = self.async_read_dir(path).await?;
508
509        let mut names = Vec::new();
510        while let Some(Ok((_, entry))) = read_dir.next_entry().await {
511            names.push(entry);
512        }
513
514        Ok(names)
515    }
516
517    pub fn read_dir_all(&self, path: impl AsRef<Path>) -> Result<Vec<String>, anyhow::Error> {
518        let mut read_dir = self.read_dir(path)?;
519
520        let mut names = Vec::new();
521        while let Some(Ok((_, entry))) = read_dir.next_entry() {
522            names.push(entry);
523        }
524
525        Ok(names)
526    }
527
528    pub async fn async_read_dir(
529        &self,
530        path: impl AsRef<Path>,
531    ) -> Result<AsyncReadDir, anyhow::Error> {
532        let path = self.relative_path(path.as_ref());
533
534        Ok(if path.components().next().is_none() {
535            AsyncReadDir::Tokio(utils::AsyncTokioReadDir(
536                tokio::fs::read_dir(&*self.base_path).await?,
537            ))
538        } else {
539            let inner = self.async_get_inner().await?;
540
541            AsyncReadDir::Cap(utils::AsyncCapReadDir(
542                Some(tokio::task::spawn_blocking(move || inner.read_dir(path)).await??),
543                Some(VecDeque::with_capacity(32)),
544            ))
545        })
546    }
547
548    pub fn read_dir(&self, path: impl AsRef<Path>) -> Result<ReadDir, anyhow::Error> {
549        let path = self.relative_path(path.as_ref());
550
551        Ok(if path.components().next().is_none() {
552            ReadDir::Std(utils::StdReadDir(std::fs::read_dir(&*self.base_path)?))
553        } else {
554            let inner = self.get_inner()?;
555
556            ReadDir::Cap(utils::CapReadDir(inner.read_dir(path)?))
557        })
558    }
559
560    pub async fn async_walk_dir(
561        &self,
562        path: impl AsRef<Path>,
563    ) -> Result<AsyncWalkDir<'_>, anyhow::Error> {
564        let path = self.relative_path(path.as_ref());
565
566        AsyncWalkDir::new(self.clone(), path).await
567    }
568
569    pub fn walk_dir(&self, path: impl AsRef<Path>) -> Result<WalkDir<'_>, anyhow::Error> {
570        let path = self.relative_path(path.as_ref());
571
572        WalkDir::new(self.clone(), path)
573    }
574}