1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//! file system implementation
use crate::{Error, Etc, Meta};
use std::{convert::AsRef, fs, ops::FnOnce, path::PathBuf};

/// mock file system
pub trait FileSystem: Meta {
    /// remove current dir or file
    fn drain(&self) -> Result<(), Error> {
        let path = self.real_path()?;

        if path.is_dir() {
            fs::remove_dir_all(path)?;
        } else {
            fs::remove_file(path)?;
        }

        Ok(())
    }

    /// opens a file in write-only mode.
    fn back(&self) -> Result<Etc, Error> {
        Ok(Etc::from(self.base()?))
    }

    /// entry of a file
    fn entry<F>(&self, name: &str, f: F) -> Result<(), Error>
    where
        F: FnOnce(Etc),
    {
        let mut path = self.real_path()?;
        path.push(name);

        f(path.into());
        Ok(())
    }

    /// find target
    fn find(&self, src: &str) -> Result<PathBuf, Error> {
        for f in fs::read_dir(self.real_path()?)? {
            let path = f?.path();
            if let Some(s) = path.file_name() {
                if src == s {
                    return Ok(path);
                } else if path.is_dir() {
                    let source: Etc = path.into();
                    let res = FileSystem::find(&source, src);
                    if res.is_ok() {
                        return res;
                    }
                }
            }
        }

        Err(Error::Custom(format!("error: {} not found", src)))
    }

    /// find all target
    fn find_all(&self, src: &str, res: &mut Vec<PathBuf>) -> Result<(), Error> {
        for f in fs::read_dir(self.real_path()?)? {
            let path = f?.path();
            if let Some(s) = path.file_name() {
                if src == s {
                    res.push(path);
                } else if path.is_dir() {
                    let source: Etc = path.into();
                    FileSystem::find_all(&source, src, res)?;
                }
            }
        }

        Ok(())
    }

    /// list sources
    fn ls(&self) -> Result<Vec<String>, Error> {
        let mut res = vec![];
        for f in fs::read_dir(self.real_path()?)? {
            if let Some(name) = f?.path().file_name() {
                if let Ok(string) = name.to_os_string().into_string() {
                    res.push(string);
                } else {
                    return Err(Error::Custom(format!(
                        "error: convert OsString {:?} failed",
                        name,
                    )));
                }
            } else {
                return Err(Error::Custom(format!(
                    "error: ls {} failed",
                    self.real_path()?.to_string_lossy()
                )));
            }
        }

        Ok(res)
    }

    /// create dir under root
    fn mkdir<P>(&self, path: P) -> Result<(), Error>
    where
        P: AsRef<str>,
    {
        let mut dir = self.real_path()?;
        dir.push(path.as_ref());
        if !dir.exists() {
            Ok(fs::create_dir_all(dir)?)
        } else {
            Ok(())
        }
    }

    /// opens a file in write-only mode.
    fn open(&self, name: &str) -> Result<Etc, Error> {
        let mut path = self.real_path()?;
        path.push(name);

        Ok(path.into())
    }

    /// remove dir or file
    fn rm(&self, path: &str) -> Result<(), Error> {
        let base = self.real_path();

        // file doesn't exist, so don't need to remove
        if base.is_err() {
            return Ok(());
        }

        let mut full = base?;
        full.push(path);

        if full.is_dir() {
            fs::remove_dir_all(full)?;
        } else {
            fs::remove_file(full)?;
        }

        Ok(())
    }
}

impl<T> FileSystem for T where T: Meta {}