etc/
fs.rs

1//! file system implementation
2use crate::{Error, Etc, Meta};
3use std::{convert::AsRef, fs, ops::FnOnce, path::PathBuf};
4
5/// mock file system
6pub trait FileSystem: Meta {
7    /// remove current dir or file
8    fn drain(&self) -> Result<(), Error> {
9        let path = self.real_path()?;
10
11        if path.is_dir() {
12            fs::remove_dir_all(path)?;
13        } else {
14            fs::remove_file(path)?;
15        }
16
17        Ok(())
18    }
19
20    /// opens a file in write-only mode.
21    fn back(&self) -> Result<Etc, Error> {
22        Ok(Etc::from(self.base()?))
23    }
24
25    /// entry of a file
26    fn entry<F>(&self, name: &str, f: F) -> Result<(), Error>
27    where
28        F: FnOnce(Etc),
29    {
30        let mut path = self.real_path()?;
31        path.push(name);
32
33        f(path.into());
34        Ok(())
35    }
36
37    /// find target
38    fn find(&self, src: &str) -> Result<PathBuf, Error> {
39        for f in fs::read_dir(self.real_path()?)? {
40            let path = f?.path();
41            if let Some(s) = path.file_name() {
42                if src == s {
43                    return Ok(path);
44                } else if path.is_dir() {
45                    let source: Etc = path.into();
46                    let res = FileSystem::find(&source, src);
47                    if res.is_ok() {
48                        return res;
49                    }
50                }
51            }
52        }
53
54        Err(Error::Custom(format!("error: {} not found", src)))
55    }
56
57    /// find all target
58    fn find_all(&self, src: &str, res: &mut Vec<PathBuf>) -> Result<(), Error> {
59        for f in fs::read_dir(self.real_path()?)? {
60            let path = f?.path();
61            if let Some(s) = path.file_name() {
62                if src == s {
63                    res.push(path);
64                } else if path.is_dir() {
65                    let source: Etc = path.into();
66                    FileSystem::find_all(&source, src, res)?;
67                }
68            }
69        }
70
71        Ok(())
72    }
73
74    /// list sources
75    fn ls(&self) -> Result<Vec<String>, Error> {
76        let mut res = vec![];
77        for f in fs::read_dir(self.real_path()?)? {
78            if let Some(name) = f?.path().file_name() {
79                if let Ok(string) = name.to_os_string().into_string() {
80                    res.push(string);
81                } else {
82                    return Err(Error::Custom(format!(
83                        "error: convert OsString {:?} failed",
84                        name,
85                    )));
86                }
87            } else {
88                return Err(Error::Custom(format!(
89                    "error: ls {} failed",
90                    self.real_path()?.to_string_lossy()
91                )));
92            }
93        }
94
95        Ok(res)
96    }
97
98    /// create dir under root
99    fn mkdir<P>(&self, path: P) -> Result<(), Error>
100    where
101        P: AsRef<str>,
102    {
103        let mut dir = self.real_path()?;
104        dir.push(path.as_ref());
105        if !dir.exists() {
106            Ok(fs::create_dir_all(dir)?)
107        } else {
108            Ok(())
109        }
110    }
111
112    /// opens a file in write-only mode.
113    fn open(&self, name: &str) -> Result<Etc, Error> {
114        let mut path = self.real_path()?;
115        path.push(name);
116
117        Ok(path.into())
118    }
119
120    /// remove dir or file
121    fn rm(&self, path: &str) -> Result<(), Error> {
122        let base = self.real_path();
123
124        // file doesn't exist, so don't need to remove
125        if base.is_err() {
126            return Ok(());
127        }
128
129        let mut full = base?;
130        full.push(path);
131
132        if full.is_dir() {
133            fs::remove_dir_all(full)?;
134        } else {
135            fs::remove_file(full)?;
136        }
137
138        Ok(())
139    }
140}
141
142impl<T> FileSystem for T where T: Meta {}