floppy_disk/
mem.rs

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
14// TODO: DirBuilder, OpenOptions
15use 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            // TODO: This is evil
556            // Adapted from https://stackoverflow.com/questions/66035290
557            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    // FIXME
587    // #[tokio::test]
588    // async fn test_canonicalize() -> Result<()> {
589    //     let fs = MemFloppyDisk::new();
590
591    //     assert_eq!(PathBuf::from("/"), fs.canonicalize("/").await?);
592    //     assert_eq!(PathBuf::from("/"), fs.canonicalize("/.").await?);
593    //     assert_eq!(PathBuf::from("/"), fs.canonicalize("/..").await?);
594    //     assert_eq!(PathBuf::from("/"), fs.canonicalize("/../..").await?);
595    //     assert_eq!(PathBuf::from("a"), fs.canonicalize("a").await?);
596    //     assert_eq!(PathBuf::from("a"), fs.canonicalize("a/.").await?);
597    //     assert_eq!(PathBuf::from("/a"), fs.canonicalize("/a/.").await?);
598    //     assert_eq!(PathBuf::from("/a"), fs.canonicalize("/a/../a").await?);
599    //     assert_eq!(
600    //         PathBuf::from("/"),
601    //         fs.canonicalize("/usr/bin/../../../../../../..").await?
602    //     );
603
604    //     Ok(())
605    // }
606
607    #[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]
638    // async fn test_hard_link() -> Result<()> {
639    //     let mut fs = MemFloppyDisk::new();
640    //     fs.write("/test.txt", "asdf").await?;
641    //     fs.hard_link("/test.txt", "/test2.txt").await?;
642    //     assert_eq!("asdf", fs.read_to_string("/test2.txt").await?);
643
644    //     Ok(())
645    // }
646
647    #[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}