Skip to main content

shared/cap/
mod.rs

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