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}