1use std::ffi::OsString;
2use std::io::{Read, Result, Seek, Write};
3use std::path::{Path, PathBuf};
4use std::pin::Pin;
5use std::time::SystemTime;
6
7use derivative::Derivative;
8use futures::{Future, TryStreamExt};
9use rsfs_tokio::unix_ext::{GenFSExt, PermissionsExt};
10use rsfs_tokio::{DirEntry, File, FileType, GenFS, Metadata, OpenOptions};
11use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, AsyncWrite, AsyncWriteExt};
12pub type InMemoryUnixFS = rsfs_tokio::mem::unix::FS;
13
14use crate::{
16 FloppyDirBuilder, FloppyDirEntry, FloppyDisk, FloppyDiskUnixExt, FloppyFile, FloppyFileType,
17 FloppyMetadata, FloppyOpenOptions, FloppyPermissions, FloppyReadDir, FloppyUnixMetadata,
18 FloppyUnixPermissions,
19};
20
21#[derive(Derivative)]
22#[derivative(Debug)]
23pub struct MemFloppyDisk {
24 fs: InMemoryUnixFS,
25}
26
27impl MemFloppyDisk {
28 #[allow(clippy::new_without_default)]
29 pub fn new() -> Self {
30 Self {
31 fs: InMemoryUnixFS::new(),
32 }
33 }
34}
35
36#[async_trait::async_trait]
37impl<'a> FloppyDisk<'a> for MemFloppyDisk {
38 type DirBuilder = MemDirBuilder<'a>;
39 type DirEntry = MemDirEntry;
40 type File = MemFile;
41 type FileType = MemFileType;
42 type Metadata = MemMetadata;
43 type OpenOptions = MemOpenOptions;
44 type Permissions = MemPermissions;
45 type ReadDir = MemReadDir;
46
47 async fn canonicalize<P: AsRef<Path> + Send>(&self, path: P) -> Result<PathBuf> {
48 self.fs.canonicalize(path).await
49 }
50
51 async fn copy<P: AsRef<Path> + Send>(&self, from: P, to: P) -> Result<u64> {
52 self.fs.copy(from, to).await
53 }
54
55 async fn create_dir<P: AsRef<Path> + Send>(&self, path: P) -> Result<()> {
56 self.fs.create_dir(path).await
57 }
58
59 async fn create_dir_all<P: AsRef<Path> + Send>(&self, path: P) -> Result<()> {
60 self.fs.create_dir_all(path).await
61 }
62
63 async fn hard_link<P: AsRef<Path> + Send>(&self, _src: P, _dst: P) -> Result<()> {
64 unimplemented!("hard links are not yet supported")
65 }
66
67 async fn metadata<P: AsRef<Path> + Send>(&self, path: P) -> Result<Self::Metadata> {
68 let metadata = self.fs.metadata(path).await?;
69 Ok(Self::Metadata { metadata })
70 }
71
72 async fn read<P: AsRef<Path> + Send>(&self, path: P) -> Result<Vec<u8>> {
73 let mut file = self.fs.open_file(path).await?;
74 let file_len = file.metadata().await?.len() as usize;
75 let mut buffer = vec![0u8; file_len];
76 let read = file.read(&mut buffer).await?;
77 debug_assert!(read <= buffer.len());
78 Ok(buffer)
79 }
80
81 async fn read_dir<P: AsRef<Path> + Send>(&self, path: P) -> Result<Self::ReadDir> {
82 self.fs.read_dir(path).await.map(MemReadDir::new)
83 }
84
85 async fn read_link<P: AsRef<Path> + Send>(&self, path: P) -> Result<PathBuf> {
86 self.fs.read_link(path).await
87 }
88
89 async fn read_to_string<P: AsRef<Path> + Send>(&self, path: P) -> Result<String> {
90 let mut file = self.fs.open_file(path).await?;
91 let file_len = file.metadata().await?.len() as usize;
92 let mut buffer = String::with_capacity(file_len);
93 file.read_to_string(&mut buffer).await?;
94 Ok(buffer)
95 }
96
97 async fn remove_dir<P: AsRef<Path> + Send>(&self, path: P) -> Result<()> {
98 self.fs.remove_dir(path).await
99 }
100
101 async fn remove_dir_all<P: AsRef<Path> + Send>(&self, path: P) -> Result<()> {
102 self.fs.remove_dir_all(path).await
103 }
104
105 async fn remove_file<P: AsRef<Path> + Send>(&self, path: P) -> Result<()> {
106 self.fs.remove_file(path).await
107 }
108
109 async fn rename<P: AsRef<Path> + Send>(&self, from: P, to: P) -> Result<()> {
110 self.fs.rename(from, to).await
111 }
112
113 async fn set_permissions<P: AsRef<Path> + Send>(
114 &self,
115 path: P,
116 perm: Self::Permissions,
117 ) -> Result<()> {
118 self.fs
119 .set_permissions(path, rsfs_tokio::mem::Permissions::from_mode(perm.mode()))
120 .await
121 }
122
123 async fn symlink<P: AsRef<Path> + Send>(&self, src: P, dst: P) -> Result<()> {
124 self.fs.symlink(src, dst).await
125 }
126
127 async fn symlink_metadata<P: AsRef<Path> + Send>(&self, path: P) -> Result<Self::Metadata> {
128 self.fs
129 .symlink_metadata(path)
130 .await
131 .map(|metadata| Self::Metadata { metadata })
132 }
133
134 async fn try_exists<P: AsRef<Path> + Send>(&self, path: P) -> Result<bool> {
135 Ok(self.fs.metadata(path).await.is_ok())
136 }
137
138 async fn write<P: AsRef<Path> + Send>(
139 &self,
140 path: P,
141 contents: impl AsRef<[u8]> + Send,
142 ) -> Result<()> {
143 let mut file = self.fs.create_file(path).await?;
144 let contents = contents.as_ref();
145 file.write_all(contents).await?;
146 Ok(())
147 }
148
149 fn new_dir_builder(&'a self) -> Self::DirBuilder {
150 MemDirBuilder {
151 fs: self,
152 recursive: false,
153 #[cfg(unix)]
154 mode: 0o777,
155 }
156 }
157}
158
159#[async_trait::async_trait]
160impl FloppyDiskUnixExt for MemFloppyDisk {
161 async fn chown<P: Into<PathBuf> + Send>(&self, path: P, uid: u32, gid: u32) -> Result<()> {
162 self.fs.set_ownership(path.into(), uid, gid).await
163 }
164}
165
166#[derive(Derivative)]
167#[derivative(Debug)]
168pub struct MemFile {
169 file: rsfs_tokio::mem::unix::File,
170}
171
172#[async_trait::async_trait]
173impl<'a> FloppyFile<'a, MemFloppyDisk> for MemFile {
174 async fn sync_all(&mut self) -> Result<()> {
175 Ok(())
176 }
177
178 async fn sync_data(&mut self) -> Result<()> {
179 Ok(())
180 }
181
182 async fn set_len(&mut self, size: u64) -> Result<()> {
183 self.file.set_len(size).await
184 }
185
186 async fn metadata(&self) -> Result<<MemFloppyDisk as FloppyDisk>::Metadata> {
187 Ok(MemMetadata {
188 metadata: self.file.metadata().await?,
189 })
190 }
191
192 async fn try_clone(&'a self) -> Result<Box<Self>> {
193 Ok(Box::new(Self {
194 file: self.file.try_clone().await?,
195 }))
196 }
197
198 async fn set_permissions(
199 &self,
200 perm: <MemFloppyDisk as FloppyDisk>::Permissions,
201 ) -> Result<()> {
202 self.file
203 .set_permissions(rsfs_tokio::mem::Permissions::from_mode(perm.mode()))
204 .await
205 }
206
207 async fn permissions(&self) -> Result<<MemFloppyDisk as FloppyDisk>::Permissions> {
208 Ok(MemPermissions {
209 mode: self.file.metadata().await?.permissions().mode(),
210 })
211 }
212}
213
214impl AsyncSeek for MemFile {
215 fn start_seek(mut self: Pin<&mut Self>, position: std::io::SeekFrom) -> Result<()> {
216 let mut this = self.as_mut();
217 let file = Pin::new(&mut this.file);
218 file.start_seek(position)
219 }
220
221 fn poll_complete(
222 mut self: Pin<&mut Self>,
223 cx: &mut std::task::Context<'_>,
224 ) -> std::task::Poll<Result<u64>> {
225 let mut this = self.as_mut();
226 let file = Pin::new(&mut this.file);
227 file.poll_complete(cx)
228 }
229}
230
231impl AsyncRead for MemFile {
232 fn poll_read(
233 mut self: Pin<&mut Self>,
234 cx: &mut std::task::Context<'_>,
235 buf: &mut tokio::io::ReadBuf<'_>,
236 ) -> std::task::Poll<Result<()>> {
237 let mut this = self.as_mut();
238 let file = Pin::new(&mut this.file);
239 file.poll_read(cx, buf)
240 }
241}
242
243impl AsyncWrite for MemFile {
244 fn poll_write(
245 mut self: Pin<&mut Self>,
246 cx: &mut std::task::Context<'_>,
247 buf: &[u8],
248 ) -> std::task::Poll<Result<usize>> {
249 let mut this = self.as_mut();
250 let file = Pin::new(&mut this.file);
251 file.poll_write(cx, buf)
252 }
253
254 fn poll_flush(
255 mut self: Pin<&mut Self>,
256 cx: &mut std::task::Context<'_>,
257 ) -> std::task::Poll<Result<()>> {
258 let mut this = self.as_mut();
259 let file = Pin::new(&mut this.file);
260 file.poll_flush(cx)
261 }
262
263 fn poll_shutdown(
264 mut self: Pin<&mut Self>,
265 cx: &mut std::task::Context<'_>,
266 ) -> std::task::Poll<Result<()>> {
267 let mut this = self.as_mut();
268 let file = Pin::new(&mut this.file);
269 file.poll_shutdown(cx)
270 }
271}
272
273impl Read for MemFile {
274 fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
275 run_here(async { self.file.read(buf).await })
276 }
277}
278
279impl Write for MemFile {
280 fn write(&mut self, buf: &[u8]) -> Result<usize> {
281 run_here(async { self.file.write(buf).await })
282 }
283
284 fn flush(&mut self) -> Result<()> {
285 run_here(async { self.file.flush().await })
286 }
287}
288
289impl Seek for MemFile {
290 fn seek(&mut self, pos: std::io::SeekFrom) -> Result<u64> {
291 run_here(async { self.file.seek(pos).await })
292 }
293}
294
295#[derive(Clone, Debug, PartialEq, Eq)]
296pub struct MemPermissions {
297 mode: u32,
298}
299
300impl FloppyPermissions for MemPermissions {
301 fn readonly(&self) -> bool {
302 self.mode & 0o222 == 0
303 }
304
305 fn set_readonly(&mut self, readonly: bool) {
306 if readonly {
307 self.mode &= !0o222;
308 } else {
309 self.mode |= 0o222;
310 }
311 }
312}
313
314impl FloppyUnixPermissions for MemPermissions {
315 fn mode(&self) -> u32 {
316 self.mode
317 }
318
319 fn set_mode(&mut self, mode: u32) {
320 self.mode = mode;
321 }
322
323 fn from_mode(mode: u32) -> Self {
324 Self { mode }
325 }
326}
327
328#[derive(Debug)]
329pub struct MemMetadata {
330 metadata: rsfs_tokio::mem::unix::Metadata,
331}
332
333#[async_trait::async_trait]
334impl<'a> FloppyMetadata<'a, MemFloppyDisk> for MemMetadata {
335 fn file_type(&self) -> <MemFloppyDisk as FloppyDisk>::FileType {
336 MemFileType(self.metadata.file_type())
337 }
338
339 fn is_dir(&self) -> bool {
340 self.metadata.is_dir()
341 }
342
343 fn is_file(&self) -> bool {
344 self.metadata.is_file()
345 }
346
347 fn is_symlink(&self) -> bool {
348 self.metadata.file_type().is_symlink()
349 }
350
351 fn len(&self) -> u64 {
352 self.metadata.len()
353 }
354
355 fn permissions(&self) -> <MemFloppyDisk as FloppyDisk>::Permissions {
356 MemPermissions {
357 mode: self.metadata.permissions().mode(),
358 }
359 }
360
361 fn modified(&self) -> Result<SystemTime> {
362 self.metadata.modified()
363 }
364
365 fn accessed(&self) -> Result<SystemTime> {
366 self.metadata.accessed()
367 }
368
369 fn created(&self) -> Result<SystemTime> {
370 self.metadata.created()
371 }
372}
373
374impl FloppyUnixMetadata for MemMetadata {
375 fn uid(&self) -> Result<u32> {
376 self.metadata.uid()
377 }
378
379 fn gid(&self) -> Result<u32> {
380 self.metadata.gid()
381 }
382}
383
384#[derive(Debug)]
385pub struct MemFileType(#[doc(hidden)] rsfs_tokio::mem::unix::FileType);
386
387impl FloppyFileType for MemFileType {
388 fn is_dir(&self) -> bool {
389 self.0.is_dir()
390 }
391
392 fn is_file(&self) -> bool {
393 self.0.is_file()
394 }
395
396 fn is_symlink(&self) -> bool {
397 self.0.is_symlink()
398 }
399}
400
401#[derive(Debug)]
402pub struct MemReadDir {
403 read_dir: rsfs_tokio::mem::unix::ReadDir,
404}
405
406impl MemReadDir {
407 fn new(read_dir: rsfs_tokio::mem::unix::ReadDir) -> Self {
408 Self { read_dir }
409 }
410}
411
412#[async_trait::async_trait]
413impl<'a> FloppyReadDir<'a, MemFloppyDisk> for MemReadDir {
414 async fn next_entry(&mut self) -> Result<Option<<MemFloppyDisk as FloppyDisk>::DirEntry>> {
415 match self.read_dir.try_next().await {
416 Ok(Some(Some(entry))) => Ok(Some(MemDirEntry { entry })),
417 Ok(Some(None)) => Ok(None),
418 Ok(None) => Ok(None),
419 Err(e) => Err(e),
420 }
421 }
422}
423
424#[derive(Debug)]
425pub struct MemDirEntry {
426 entry: rsfs_tokio::mem::unix::DirEntry,
427}
428
429#[async_trait::async_trait]
430impl<'a> FloppyDirEntry<'a, MemFloppyDisk> for MemDirEntry {
431 fn path(&self) -> PathBuf {
432 self.entry.path()
433 }
434 fn file_name(&self) -> OsString {
435 self.entry.file_name()
436 }
437 async fn metadata(&self) -> Result<<MemFloppyDisk as FloppyDisk>::Metadata> {
438 Ok(MemMetadata {
439 metadata: self.entry.metadata().await?,
440 })
441 }
442 async fn file_type(&self) -> Result<<MemFloppyDisk as FloppyDisk>::FileType> {
443 Ok(MemFileType(self.entry.file_type().await?))
444 }
445
446 #[cfg(unix)]
447 fn ino(&self) -> u64 {
448 unimplemented!("not currently supported")
449 }
450}
451
452#[derive(Debug)]
453pub struct MemDirBuilder<'a> {
454 fs: &'a MemFloppyDisk,
455 recursive: bool,
456 #[cfg(unix)]
457 mode: u32,
458}
459
460#[async_trait::async_trait]
461impl FloppyDirBuilder for MemDirBuilder<'_> {
462 fn recursive(&mut self, recursive: bool) -> &mut Self {
463 self.recursive = recursive;
464 self
465 }
466
467 async fn create<P: AsRef<Path> + Send>(&self, path: P) -> Result<()> {
468 if self.recursive {
469 self.fs.create_dir_all(path).await
470 } else {
471 self.fs.create_dir(path).await
472 }
473 }
474
475 #[cfg(unix)]
476 fn mode(&mut self, mode: u32) -> &mut Self {
477 self.mode = mode;
478 self
479 }
480}
481
482#[derive(Debug, Copy, Clone)]
483pub struct MemOpenOptions {
484 read: bool,
485 write: bool,
486 append: bool,
487 truncate: bool,
488 create: bool,
489 create_new: bool,
490}
491
492#[async_trait::async_trait]
493impl<'a> FloppyOpenOptions<'a, MemFloppyDisk> for MemOpenOptions {
494 fn new() -> Self {
495 Self {
496 read: false,
497 write: false,
498 append: false,
499 truncate: false,
500 create: false,
501 create_new: false,
502 }
503 }
504
505 fn read(mut self, read: bool) -> Self {
506 self.read = read;
507 self
508 }
509
510 fn write(mut self, write: bool) -> Self {
511 self.write = write;
512 self
513 }
514
515 fn append(mut self, append: bool) -> Self {
516 self.append = append;
517 self
518 }
519
520 fn truncate(mut self, truncate: bool) -> Self {
521 self.truncate = truncate;
522 self
523 }
524
525 fn create(mut self, create: bool) -> Self {
526 self.create = create;
527 self
528 }
529
530 fn create_new(mut self, create_new: bool) -> Self {
531 self.create_new = create_new;
532 self
533 }
534
535 async fn open<P: AsRef<Path> + Send>(
536 &self,
537 disk: &'a MemFloppyDisk,
538 path: P,
539 ) -> Result<<MemFloppyDisk as FloppyDisk<'a>>::File> {
540 let mut options = disk.fs.new_openopts();
541 options.read(self.read);
542 options.write(self.write);
543 options.append(self.append);
544 options.truncate(self.truncate);
545 options.create(self.create);
546 options.create_new(self.create_new);
547 let file = options.open(path).await?;
548 Ok(MemFile { file })
549 }
550}
551
552fn run_here<F: Future>(fut: F) -> F::Output {
553 match tokio::runtime::Handle::try_current() {
554 Ok(handle) => {
555 let _guard = handle.enter();
558 futures::executor::block_on(fut)
559 }
560
561 Err(_) => {
562 let rt = tokio::runtime::Builder::new_current_thread()
563 .build()
564 .unwrap();
565
566 rt.block_on(fut)
567 }
568 }
569}
570
571#[cfg(test)]
572mod tests {
573 use super::*;
574 use crate::*;
575 use std::io::Result;
576
577 #[tokio::test]
578 async fn test_mem_floppy_disk() -> Result<()> {
579 let fs = MemFloppyDisk::new();
580 fs.write("/test.txt", "asdf").await?;
581 assert_eq!("asdf", fs.read_to_string("/test.txt").await?);
582
583 Ok(())
584 }
585
586 #[tokio::test]
608 async fn test_copy() -> Result<()> {
609 let fs = MemFloppyDisk::new();
610 fs.write("/test.txt", "asdf").await?;
611 fs.copy("/test.txt", "/test2.txt").await?;
612 assert_eq!("asdf", fs.read_to_string("/test2.txt").await?);
613
614 Ok(())
615 }
616
617 #[tokio::test]
618 async fn test_create_dir() -> Result<()> {
619 let fs = MemFloppyDisk::new();
620 fs.create_dir("/test").await?;
621 let metadata = fs.metadata("/test").await?;
622 assert!(metadata.is_dir());
623
624 Ok(())
625 }
626
627 #[tokio::test]
628 async fn test_create_dir_all() -> Result<()> {
629 let fs = MemFloppyDisk::new();
630 fs.create_dir_all("/test/a/b/c").await?;
631 let metadata = fs.metadata("/test/a/b/c").await?;
632 assert!(metadata.is_dir());
633
634 Ok(())
635 }
636
637 #[tokio::test]
648 async fn test_metadata() -> Result<()> {
649 let fs = MemFloppyDisk::new();
650 fs.write("/test.txt", "asdf").await?;
651 let metadata = fs.metadata("/test.txt").await?;
652 assert!(metadata.is_file());
653 assert_eq!(4, metadata.len());
654
655 Ok(())
656 }
657
658 #[tokio::test]
659 async fn test_read() -> Result<()> {
660 let fs = MemFloppyDisk::new();
661 fs.write("/test.txt", "asdf").await?;
662 let buf = fs.read("/test.txt").await?;
663 assert_eq!(b"asdf", buf.as_slice());
664
665 Ok(())
666 }
667
668 #[tokio::test]
669 async fn test_read_dir() -> Result<()> {
670 let fs = MemFloppyDisk::new();
671 fs.write("/test.txt", "asdf").await?;
672 fs.create_dir("/test").await?;
673 let mut entries = fs.read_dir("/").await?;
674 let entry = entries.next_entry().await?;
675 assert!(entry.is_some());
676 let entry = entry.unwrap();
677 assert_eq!("test", entry.file_name().to_str().unwrap());
678 assert!(entry.file_type().await?.is_dir());
679
680 let entry = entries.next_entry().await?;
681 assert!(entry.is_some());
682 let entry = entry.unwrap();
683 assert_eq!("test.txt", entry.file_name().to_str().unwrap());
684 assert!(entry.file_type().await?.is_file());
685
686 assert!(entries.next_entry().await?.is_none());
687
688 Ok(())
689 }
690
691 #[tokio::test]
692 async fn test_read_link() -> Result<()> {
693 let fs = MemFloppyDisk::new();
694 fs.write("/test.txt", "asdf").await?;
695 fs.symlink("/test.txt", "/test2.txt").await?;
696 let s = fs.read_link("/test2.txt").await?;
697 assert_eq!(PathBuf::from("/test.txt"), s);
698
699 Ok(())
700 }
701
702 #[tokio::test]
703 async fn test_read_to_string() -> Result<()> {
704 let fs = MemFloppyDisk::new();
705 fs.write("/test.txt", "asdf").await?;
706 let s = fs.read_to_string("/test.txt").await?;
707 assert_eq!("asdf", s);
708
709 Ok(())
710 }
711
712 #[tokio::test]
713 async fn test_remove_dir() -> Result<()> {
714 let fs = MemFloppyDisk::new();
715 fs.create_dir("/test").await?;
716 fs.remove_dir("/test").await?;
717 assert!(fs.metadata("/test").await.is_err());
718
719 Ok(())
720 }
721
722 #[tokio::test]
723 async fn test_remove_dir_all() -> Result<()> {
724 let fs = MemFloppyDisk::new();
725 fs.create_dir_all("/test/a/b/c").await?;
726 fs.remove_dir_all("/test").await?;
727 assert!(fs.metadata("/test").await.is_err());
728 assert!(fs.metadata("/test/a").await.is_err());
729 assert!(fs.metadata("/test/a/b").await.is_err());
730 assert!(fs.metadata("/test/a/b/c").await.is_err());
731
732 Ok(())
733 }
734
735 #[tokio::test]
736 async fn test_remove_file() -> Result<()> {
737 let fs = MemFloppyDisk::new();
738 fs.write("/test.txt", "asdf").await?;
739 fs.remove_file("/test.txt").await?;
740 assert!(fs.metadata("/test.txt").await.is_err());
741
742 Ok(())
743 }
744
745 #[tokio::test]
746 async fn test_rename() -> Result<()> {
747 let fs = MemFloppyDisk::new();
748 fs.write("/test.txt", "asdf").await?;
749 fs.rename("/test.txt", "/test2.txt").await?;
750 assert!(fs.metadata("/test.txt").await.is_err());
751 assert_eq!("asdf", fs.read_to_string("/test2.txt").await?);
752
753 Ok(())
754 }
755
756 #[tokio::test]
757 async fn test_set_permissions() -> Result<()> {
758 let fs = MemFloppyDisk::new();
759 fs.write("/test.txt", "asdf").await?;
760 fs.set_permissions("/test.txt", MemPermissions::from_mode(0o777))
761 .await?;
762 let metadata = fs.metadata("/test.txt").await?;
763 assert_eq!(0o777, metadata.permissions().mode());
764
765 Ok(())
766 }
767
768 #[tokio::test]
769 async fn test_symlink_metadata() -> Result<()> {
770 let fs = MemFloppyDisk::new();
771 fs.write("/test.txt", "asdf").await?;
772 fs.symlink("/test.txt", "/test2.txt").await?;
773 let metadata = fs.symlink_metadata("/test2.txt").await?;
774 assert!(metadata.is_symlink());
775
776 Ok(())
777 }
778
779 #[tokio::test]
780 async fn test_symlink() -> Result<()> {
781 let fs = MemFloppyDisk::new();
782 fs.write("/test.txt", "asdf").await?;
783 fs.symlink("/test.txt", "/test2.txt").await?;
784 let s = fs.read_link("/test2.txt").await?;
785 assert_eq!(PathBuf::from("/test.txt"), s);
786
787 Ok(())
788 }
789
790 #[tokio::test]
791 async fn test_write() -> Result<()> {
792 let fs = MemFloppyDisk::new();
793 fs.write("/test.txt", "asdf").await?;
794 assert_eq!("asdf", fs.read_to_string("/test.txt").await?);
795
796 Ok(())
797 }
798
799 #[tokio::test]
800 async fn test_dir_builder() -> Result<()> {
801 let fs = MemFloppyDisk::new();
802 {
803 let mut builder = fs.new_dir_builder();
804 builder.recursive(true);
805 builder.create("/test/a/b/c").await?;
806
807 assert!(fs.metadata("/test").await?.is_dir());
808 assert!(fs.metadata("/test/a").await?.is_dir());
809 assert!(fs.metadata("/test/a/b").await?.is_dir());
810 assert!(fs.metadata("/test/a/b/c").await?.is_dir());
811 }
812
813 {
814 let mut builder = fs.new_dir_builder();
815 builder.recursive(false);
816 let res = builder.create("/test2/a/b/c").await;
817 assert!(res.is_err());
818 }
819
820 Ok(())
821 }
822}