1use std::collections::HashSet;
10use std::fs::copy;
11use std::fs::create_dir;
12use std::fs::create_dir_all;
13use std::fs::read_dir;
14use std::fs::remove_dir;
15use std::fs::remove_file;
16use std::fs::symlink_metadata;
17use std::io::Error;
18use std::io::ErrorKind;
19use std::io::Result;
20use std::path::Path;
21use std::path::PathBuf;
22
23fn recursively_copy_with_path_bufs(src_path_buf: &mut PathBuf, dst_path_buf: &mut PathBuf) -> Result<()>
24{
25 let metadata = symlink_metadata(src_path_buf.as_path())?;
26 if metadata.is_dir() {
27 match create_dir(dst_path_buf.as_path()) {
28 Ok(()) => (),
29 Err(err) if err.kind() == ErrorKind::AlreadyExists => (),
30 Err(err) => return Err(err),
31 }
32 let canon_src_path_buf = src_path_buf.canonicalize()?;
33 let canon_dst_path_buf = dst_path_buf.canonicalize()?;
34 if canon_src_path_buf == canon_dst_path_buf {
35 return Ok(());
36 }
37 if canon_dst_path_buf.starts_with(canon_src_path_buf) {
38 remove_dir(dst_path_buf.as_path())?;
39 return Err(Error::new(ErrorKind::Other, "destination directory can't be in source directory"));
40 }
41 let entries = read_dir(src_path_buf.as_path())?;
42 for entry in entries {
43 let tmp_entry = entry?;
44 src_path_buf.push(tmp_entry.file_name());
45 dst_path_buf.push(tmp_entry.file_name());
46 recursively_copy_with_path_bufs(src_path_buf, dst_path_buf)?;
47 dst_path_buf.pop();
48 src_path_buf.pop();
49 }
50 } else if metadata.is_file() {
51 let canon_src_path_buf = src_path_buf.canonicalize()?;
52 match dst_path_buf.canonicalize() {
53 Ok(canon_dst_path_buf) => {
54 if canon_src_path_buf == canon_dst_path_buf {
55 return Ok(());
56 }
57 },
58 Err(err) if err.kind() == ErrorKind::NotFound => (),
59 Err(err) => return Err(err),
60 }
61 copy(src_path_buf.as_path(), dst_path_buf.as_path())?;
62 } else if metadata.is_symlink() {
63 return Err(Error::new(ErrorKind::Other, "can't copy symbolic link"));
64 } else {
65 return Err(Error::new(ErrorKind::Other, "can't copy device file"));
66 }
67 Ok(())
68}
69
70pub fn recursively_copy<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> Result<()>
78{
79 let mut src_path_buf = PathBuf::from(src.as_ref());
80 let mut dst_path_buf = PathBuf::from(dst.as_ref());
81 let mut dst_parent_path_buf = dst_path_buf.clone();
82 dst_parent_path_buf.pop();
83 if dst_parent_path_buf != PathBuf::from("") {
84 create_dir_all(dst_parent_path_buf)?;
85 }
86 recursively_copy_with_path_bufs(&mut src_path_buf, &mut dst_path_buf)
87}
88
89fn recursively_remove_with_path_buf(path_buf: &mut PathBuf, is_force: bool) -> Result<()>
90{
91 let metadata = match symlink_metadata(path_buf.as_path()) {
92 Ok(tmp_metadata) => tmp_metadata,
93 Err(err) if is_force && err.kind() == ErrorKind::NotFound => return Ok(()),
94 Err(err) => return Err(err),
95 };
96 if metadata.is_dir() {
97 let entries = match read_dir(path_buf.as_path()) {
98 Ok(tmp_entries) => tmp_entries,
99 Err(err) if is_force && err.kind() == ErrorKind::NotFound => return Ok(()),
100 Err(err) => return Err(err),
101 };
102 for entry in entries {
103 let tmp_entry = entry?;
104 path_buf.push(tmp_entry.file_name());
105 recursively_remove_with_path_buf(path_buf, is_force)?;
106 path_buf.pop();
107 }
108 match remove_dir(path_buf.as_path()) {
109 Ok(()) => (),
110 Err(err) if is_force && err.kind() == ErrorKind::NotFound => (),
111 Err(err) => return Err(err),
112 }
113 } else {
114 match remove_file(path_buf.as_path()) {
115 Ok(()) => (),
116 Err(err) if is_force && err.kind() == ErrorKind::NotFound => (),
117 Err(err) => return Err(err),
118 }
119 }
120 Ok(())
121}
122
123pub fn recursively_remove<P: AsRef<Path>>(path: P, is_force: bool) -> Result<()>
128{
129 let mut path_buf = PathBuf::from(path.as_ref());
130 recursively_remove_with_path_buf(&mut path_buf, is_force)
131}
132
133fn get_dir_paths_and_paths_in_dir(path: &Path, suffix_path_buf: &mut PathBuf, dir_paths: &mut HashSet<PathBuf>, paths: &mut HashSet<PathBuf>, path_vec: &mut Option<Vec<PathBuf>>, ignored_paths: &HashSet<PathBuf>, depth: Option<usize>) -> Result<()>
134{
135 let mut path_buf = PathBuf::from(path);
136 if suffix_path_buf != &PathBuf::from("") {
137 path_buf.push(suffix_path_buf.as_path());
138 }
139 let metadata = match symlink_metadata(path_buf.as_path()) {
140 Ok(tmp_metadata) => tmp_metadata,
141 Err(err) if err.kind() == ErrorKind::NotFound => return Ok(()),
142 Err(err) => return Err(err),
143 };
144 if metadata.is_dir() && depth.map(|d| d > 0).unwrap_or(true) {
145 match read_dir(path_buf.as_path()) {
146 Ok(entries) => {
147 dir_paths.insert(suffix_path_buf.clone());
148 for entry in entries {
149 let tmp_entry = entry?;
150 suffix_path_buf.push(tmp_entry.file_name());
151 get_dir_paths_and_paths_in_dir(path, suffix_path_buf, dir_paths, paths, path_vec, ignored_paths, depth.map(|d| d - 1))?;
152 suffix_path_buf.pop();
153 }
154 },
155 Err(err) if err.kind() == ErrorKind::NotFound => (),
156 Err(err) => return Err(err),
157 }
158 } else {
159 if !ignored_paths.contains(suffix_path_buf) {
160 paths.insert(suffix_path_buf.clone());
161 match path_vec {
162 Some(path_vec) => path_vec.push(suffix_path_buf.clone()),
163 None => (),
164 }
165 }
166 }
167 Ok(())
168}
169
170pub fn conflicts<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q, ignored_paths: &HashSet<PathBuf>, depth: Option<usize>) -> Result<(Vec<PathBuf>, Vec<PathBuf>)>
176{
177 let mut paths: Option<Vec<PathBuf>> = Some(Vec::new());
178 let mut src_dir_paths: HashSet<PathBuf> = HashSet::new();
179 let mut src_paths: HashSet<PathBuf> = HashSet::new();
180 let mut src_suffix_path_buf = PathBuf::from("");
181 get_dir_paths_and_paths_in_dir(src.as_ref(), &mut src_suffix_path_buf, &mut src_dir_paths, &mut src_paths, &mut paths, &HashSet::new(), depth)?;
182 let mut dst_dir_paths: HashSet<PathBuf> = HashSet::new();
183 let mut dst_paths: HashSet<PathBuf> = HashSet::new();
184 let mut dst_suffix_path_buf = PathBuf::from("");
185 get_dir_paths_and_paths_in_dir(dst.as_ref(), &mut dst_suffix_path_buf, &mut dst_dir_paths, &mut dst_paths, &mut None, ignored_paths, depth)?;
186 let mut conflict_paths: Vec<PathBuf> = src_dir_paths.intersection(&dst_paths).map(|p| p.clone()).collect();
187 let mut conflict_paths2: Vec<PathBuf> = src_paths.intersection(&dst_dir_paths).map(|p| p.clone()).collect();
188 let mut conflict_paths3: Vec<PathBuf> = src_paths.intersection(&dst_paths).map(|p| p.clone()).collect();
189 conflict_paths.append(&mut conflict_paths2);
190 conflict_paths.append(&mut conflict_paths3);
191 Ok((conflict_paths, paths.unwrap_or(Vec::new())))
192}
193
194fn get_paths_in_dir(path: &Path, suffix_path_buf: &mut PathBuf, paths: &mut Vec<PathBuf>, depth: Option<usize>) -> Result<()>
195{
196 let mut path_buf = PathBuf::from(path);
197 if suffix_path_buf != &PathBuf::from("") {
198 path_buf.push(suffix_path_buf.as_path());
199 }
200 let metadata = match symlink_metadata(path_buf.as_path()) {
201 Ok(tmp_metadata) => tmp_metadata,
202 Err(err) if err.kind() == ErrorKind::NotFound => return Ok(()),
203 Err(err) => return Err(err),
204 };
205 if metadata.is_dir() && depth.map(|d| d > 0).unwrap_or(true) {
206 match read_dir(path_buf.as_path()) {
207 Ok(entries) => {
208 for entry in entries {
209 let tmp_entry = entry?;
210 suffix_path_buf.push(tmp_entry.file_name());
211 get_paths_in_dir(path, suffix_path_buf, paths, depth.map(|d| d - 1))?;
212 suffix_path_buf.pop();
213 }
214 },
215 Err(err) if err.kind() == ErrorKind::NotFound => (),
216 Err(err) => return Err(err),
217 }
218 } else {
219 paths.push(suffix_path_buf.clone());
220 }
221 Ok(())
222}
223
224pub fn paths_in_dir<P: AsRef<Path>>(path: P, depth: Option<usize>) -> Result<Vec<PathBuf>>
226{
227 let mut paths: Vec<PathBuf> = Vec::new();
228 let mut suffix_path_buf = PathBuf::from("");
229 get_paths_in_dir(path.as_ref(), &mut suffix_path_buf, &mut paths, depth)?;
230 Ok(paths)
231}
232
233pub fn recursively_copy_paths_in_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q, paths: &[PathBuf]) -> Result<()>
237{
238 for suffix_path_buf in paths {
239 let mut src_path_buf = PathBuf::from(src.as_ref());
240 src_path_buf.push(suffix_path_buf.as_path());
241 let mut dst_path_buf = PathBuf::from(dst.as_ref());
242 dst_path_buf.push(suffix_path_buf.as_path());
243 recursively_copy(src_path_buf.as_path(), dst_path_buf.as_path())?;
244 }
245 Ok(())
246}
247
248pub fn recursively_remove_paths_in_dir<P: AsRef<Path>>(path: P, paths: &[PathBuf], is_force: bool) -> Result<()>
252{
253 for suffix_path_buf in paths {
254 let mut path_buf = PathBuf::from(path.as_ref());
255 path_buf.push(suffix_path_buf.as_path());
256 recursively_remove(path_buf.as_path(), is_force)?;
257 let mut tmp_suffix_path_buf = suffix_path_buf.clone();
258 if tmp_suffix_path_buf != PathBuf::from("") {
259 tmp_suffix_path_buf.pop();
260 while tmp_suffix_path_buf != PathBuf::from("") {
261 let mut dir_path_buf = PathBuf::from(path.as_ref());
262 dir_path_buf.push(tmp_suffix_path_buf.as_path());
263 match remove_dir(dir_path_buf.as_path()) {
264 Ok(()) => (),
265 Err(_) => break,
266 }
267 tmp_suffix_path_buf.pop();
268 }
269 }
270 }
271 Ok(())
272}
273
274pub fn only_one_dir_in_dir<P: AsRef<Path>>(path: P) -> Result<Option<PathBuf>>
277{
278 let entries = read_dir(path)?;
279 let mut is_first = true;
280 let mut path_buf: Option<PathBuf> = None;
281 for entry in entries {
282 let tmp_entry = entry?;
283 if is_first && tmp_entry.metadata()?.is_dir() {
284 path_buf = Some(tmp_entry.path());
285 } else {
286 path_buf = None;
287 }
288 is_first = false;
289 }
290 Ok(path_buf)
291}
292
293#[cfg(test)]
294mod tests;