hex_patch/app/files/
filesystem.rs

1use std::{error::Error, path::Path};
2
3use crate::app::ssh::connection::Connection;
4
5use super::path;
6
7pub enum FileSystem {
8    Local {
9        path: String,
10    },
11    Remote {
12        path: String,
13        connection: Connection,
14    },
15}
16
17impl FileSystem {
18    pub fn new_local(path: &str) -> Result<Self, Box<dyn Error>> {
19        Ok(Self::Local {
20            path: Path::new(path)
21                .canonicalize()
22                .map(|path| path.to_string_lossy().to_string())?,
23        })
24    }
25
26    pub fn new_remote(
27        path: &str,
28        connection_str: &str,
29        password: Option<&str>,
30    ) -> Result<Self, Box<dyn Error>> {
31        let connection = Connection::new(connection_str, password)?;
32        Ok(Self::Remote {
33            path: connection.canonicalize(path)?,
34            connection,
35        })
36    }
37
38    pub fn separator(&self) -> char {
39        match self {
40            Self::Local { .. } => std::path::MAIN_SEPARATOR,
41            Self::Remote { connection, .. } => connection.separator(),
42        }
43    }
44
45    pub fn pwd(&self) -> &str {
46        match self {
47            Self::Local { path } | Self::Remote { path, .. } => path,
48        }
49    }
50
51    pub fn cd(&mut self, path: &str) {
52        match self {
53            Self::Local { path: current }
54            | Self::Remote {
55                path: current,
56                connection: _,
57            } => *current = path.into(),
58        }
59    }
60
61    pub fn ls(&self, path: &str) -> Result<Vec<String>, Box<dyn Error>> {
62        let mut ret = match self {
63            Self::Local { .. } => {
64                let dir = std::fs::read_dir(path)?;
65                let mut ret = Vec::new();
66                for f in dir {
67                    let f = f?;
68                    ret.push(f.path().to_string_lossy().to_string())
69                }
70                Ok(ret)
71            }
72            Self::Remote { connection, .. } => connection.ls(path),
73        }?;
74        if path::parent(path).is_some() {
75            ret.insert(0, path::join(path, "..", self.separator()));
76        }
77        Ok(ret)
78    }
79
80    pub fn read(&self, path: &str) -> Result<Vec<u8>, Box<dyn Error>> {
81        match self {
82            Self::Local { .. } => Ok(std::fs::read(path)?),
83            Self::Remote { connection, .. } => connection.read(path),
84        }
85    }
86
87    pub fn mkdirs(&self, path: &str) -> Result<(), Box<dyn Error>> {
88        match self {
89            Self::Local { .. } => std::fs::create_dir_all(path)?,
90            Self::Remote { connection, .. } => connection.mkdirs(path)?,
91        }
92        Ok(())
93    }
94
95    pub fn create(&self, path: &str) -> Result<(), Box<dyn Error>> {
96        match self {
97            Self::Local { .. } => {
98                std::fs::File::create(path)?;
99            }
100            Self::Remote { connection, .. } => {
101                connection.create(path)?;
102            }
103        }
104        Ok(())
105    }
106
107    pub fn write(&self, path: &str, data: &[u8]) -> Result<(), Box<dyn Error>> {
108        match self {
109            Self::Local { .. } => Ok(std::fs::write(path, data)?),
110            Self::Remote { connection, .. } => connection.write(path, data),
111        }
112    }
113
114    pub fn is_file(&self, path: &str) -> bool {
115        match self {
116            Self::Local { .. } => Path::new(path).is_file(),
117            Self::Remote { connection, .. } => connection.is_file(path),
118        }
119    }
120
121    pub fn is_dir(&self, path: &str) -> bool {
122        match self {
123            Self::Local { .. } => Path::new(path).is_dir(),
124            Self::Remote { connection, .. } => connection.is_dir(path),
125        }
126    }
127
128    pub fn canonicalize(&self, path: &str) -> Result<String, Box<dyn Error>> {
129        match self {
130            Self::Local { .. } => Ok(Path::new(path)
131                .canonicalize()
132                .map(|path| path.to_string_lossy().to_string())?),
133            Self::Remote { connection, .. } => connection.canonicalize(path),
134        }
135    }
136}
137
138impl Default for FileSystem {
139    fn default() -> Self {
140        Self::Local { path: "./".into() }
141    }
142}