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}