1 | use crate::error::*; |
2 | use std::collections::{HashMap, HashSet}; |
3 | use std::fs::{create_dir, create_dir_all, read_dir, remove_dir_all, Metadata}; |
4 | use std::path::{Path, PathBuf}; |
5 | use std::time::SystemTime; |
6 | |
7 | /// Options and flags which can be used to configure how a file will be copied or moved. |
8 | #[derive (Clone)] |
9 | pub struct CopyOptions { |
10 | /// Overwrite existing files if true (default: false). |
11 | pub overwrite: bool, |
12 | /// Skip existing files if true (default: false). |
13 | pub skip_exist: bool, |
14 | /// Buffer size that specifies the amount of bytes to be moved or copied before the progress handler is called. This only affects functions with progress handlers. (default: 64000) |
15 | pub buffer_size: usize, |
16 | /// Recursively copy a directory with a new name or place it inside the destination (default: false, same behaviors as cp -r on Unix) |
17 | pub copy_inside: bool, |
18 | /// Copy only contents without a creating a new folder in the destination folder (default: false). |
19 | pub content_only: bool, |
20 | /// Sets levels reading. Set 0 for read all directory folder (default: 0). |
21 | /// |
22 | /// Warning: Work only for copy operations! |
23 | pub depth: u64, |
24 | } |
25 | |
26 | impl CopyOptions { |
27 | /// Initialize struct CopyOptions with default value. |
28 | /// |
29 | /// ```rust,ignore |
30 | /// overwrite: false |
31 | /// |
32 | /// skip_exist: false |
33 | /// |
34 | /// buffer_size: 64000 // 64kb |
35 | /// |
36 | /// copy_inside: false |
37 | /// ``` |
38 | pub fn new() -> CopyOptions { |
39 | CopyOptions { |
40 | overwrite: false, |
41 | skip_exist: false, |
42 | buffer_size: 64000, // 64kb |
43 | copy_inside: false, |
44 | content_only: false, |
45 | depth: 0, |
46 | } |
47 | } |
48 | |
49 | /// Overwrite existing files if true. |
50 | pub fn overwrite(mut self, overwrite: bool) -> Self { |
51 | self.overwrite = overwrite; |
52 | self |
53 | } |
54 | |
55 | /// Skip existing files if true. |
56 | pub fn skip_exist(mut self, skip_exist: bool) -> Self { |
57 | self.skip_exist = skip_exist; |
58 | self |
59 | } |
60 | |
61 | /// Buffer size that specifies the amount of bytes to be moved or copied before the progress handler is called. This only affects functions with progress handlers. |
62 | pub fn buffer_size(mut self, buffer_size: usize) -> Self { |
63 | self.buffer_size = buffer_size; |
64 | self |
65 | } |
66 | |
67 | /// Recursively copy a directory with a new name or place it inside the destination (default: false, same behaviors as cp -r on Unix) |
68 | pub fn copy_inside(mut self, copy_inside: bool) -> Self { |
69 | self.copy_inside = copy_inside; |
70 | self |
71 | } |
72 | |
73 | /// Copy only contents without a creating a new folder in the destination folder. |
74 | pub fn content_only(mut self, content_only: bool) -> Self { |
75 | self.content_only = content_only; |
76 | self |
77 | } |
78 | |
79 | /// Sets levels reading. Set 0 for read all directory folder |
80 | pub fn depth(mut self, depth: u64) -> Self { |
81 | self.depth = depth; |
82 | self |
83 | } |
84 | } |
85 | |
86 | impl Default for CopyOptions { |
87 | fn default() -> Self { |
88 | CopyOptions::new() |
89 | } |
90 | } |
91 | |
92 | // Options and flags which can be used to configure how to read a directory. |
93 | #[derive (Clone, Default)] |
94 | pub struct DirOptions { |
95 | /// Sets levels reading. Set value 0 for read all directory folder. By default 0. |
96 | pub depth: u64, |
97 | } |
98 | |
99 | impl DirOptions { |
100 | /// Initialize struct DirOptions with default value. |
101 | pub fn new() -> DirOptions { |
102 | Default::default() |
103 | } |
104 | } |
105 | |
106 | /// A structure which include information about directory |
107 | pub struct DirContent { |
108 | /// Directory size in bytes. |
109 | pub dir_size: u64, |
110 | /// List all files directory and sub directories. |
111 | pub files: Vec<String>, |
112 | /// List all folders and sub folders directory. |
113 | pub directories: Vec<String>, |
114 | } |
115 | |
116 | /// A structure which include information about the current status of the copy or move directory. |
117 | pub struct TransitProcess { |
118 | /// Copied bytes on this time for folder |
119 | pub copied_bytes: u64, |
120 | /// All the bytes which should to copy or move (dir size). |
121 | pub total_bytes: u64, |
122 | /// Copied bytes on this time for file. |
123 | pub file_bytes_copied: u64, |
124 | /// Size current copied file. |
125 | pub file_total_bytes: u64, |
126 | /// Name current copied file. |
127 | pub file_name: String, |
128 | /// Transit state |
129 | pub state: TransitState, |
130 | } |
131 | |
132 | /// |
133 | #[derive (Hash, Eq, PartialEq, Clone)] |
134 | pub enum TransitState { |
135 | /// Standard state. |
136 | Normal, |
137 | /// Pause state when destination path exists. |
138 | Exists, |
139 | /// Pause state when current process does not have the permission to access from or to |
140 | /// path. |
141 | NoAccess, |
142 | } |
143 | |
144 | /// Available returns codes for user decide |
145 | pub enum TransitProcessResult { |
146 | /// Rewrite exist file or directory. |
147 | Overwrite, |
148 | /// Rewrite for all exist files or directories. |
149 | OverwriteAll, |
150 | /// Skip current problem file or directory. |
151 | Skip, |
152 | /// Skip for all problems file or directory. |
153 | SkipAll, |
154 | /// Retry current operation. |
155 | Retry, |
156 | /// Abort current operation. |
157 | Abort, |
158 | /// Continue execute process if process not have error and abort if process content error. |
159 | ContinueOrAbort, |
160 | } |
161 | |
162 | impl Clone for TransitProcess { |
163 | fn clone(&self) -> TransitProcess { |
164 | TransitProcess { |
165 | copied_bytes: self.copied_bytes, |
166 | total_bytes: self.total_bytes, |
167 | file_bytes_copied: self.file_bytes_copied, |
168 | file_total_bytes: self.file_total_bytes, |
169 | file_name: self.file_name.clone(), |
170 | state: self.state.clone(), |
171 | } |
172 | } |
173 | } |
174 | |
175 | /// Available attributes for get information about directory entry. |
176 | #[derive (Hash, Eq, PartialEq, Clone)] |
177 | pub enum DirEntryAttr { |
178 | /// Folder name or file name without extension. |
179 | Name, |
180 | /// File extension. |
181 | Ext, |
182 | /// Folder name or file name with extension. |
183 | FullName, |
184 | /// Path to file or directory. |
185 | Path, |
186 | /// Dos path to file or directory. |
187 | DosPath, |
188 | /// File size in bytes. |
189 | FileSize, |
190 | /// Size file or directory in bytes. |
191 | /// |
192 | /// `Attention!`: This operation very expensive and sometimes required additional rights. |
193 | Size, |
194 | /// Return whether entry is directory or not. |
195 | IsDir, |
196 | /// Return whether entry is file or not. |
197 | IsFile, |
198 | /// Last modification time for directory entry. |
199 | Modified, |
200 | /// Last access time for directory entry. |
201 | Accessed, |
202 | /// Created time for directory entry. |
203 | /// |
204 | /// `Attention!`: Not supported UNIX platform. |
205 | Created, |
206 | /// Return or not return base information target folder. |
207 | BaseInfo, |
208 | } |
209 | |
210 | /// Available types for directory entry. |
211 | pub enum DirEntryValue { |
212 | /// String type |
213 | String(String), |
214 | /// Boolean type |
215 | Boolean(bool), |
216 | /// SystemTime type |
217 | SystemTime(SystemTime), |
218 | /// u64 type |
219 | U64(u64), |
220 | } |
221 | |
222 | /// Result returned by the `ls` function. |
223 | pub struct LsResult { |
224 | /// Base folder target path |
225 | pub base: HashMap<DirEntryAttr, DirEntryValue>, |
226 | /// Collection directory entry with information. |
227 | pub items: Vec<HashMap<DirEntryAttr, DirEntryValue>>, |
228 | } |
229 | |
230 | /// Returned information about directory entry with information which you choose in config. |
231 | /// |
232 | /// This function takes to arguments: |
233 | /// |
234 | /// * `path` - Path to directory. |
235 | /// |
236 | /// * `config` - Set attributes which you want see inside return data. |
237 | /// |
238 | /// # Errors |
239 | /// |
240 | /// This function will return an error in the following situations, but is not limited to just |
241 | /// these cases: |
242 | /// |
243 | /// * This `path` does not exist. |
244 | /// * Invalid `path`. |
245 | /// * The current process does not have the permission to access `path`. |
246 | /// |
247 | /// #Examples |
248 | /// |
249 | /// ```rust,ignore |
250 | /// extern crate fs_extra; |
251 | /// use fs_extra::dir::{get_details_entry, DirEntryAttr}; |
252 | /// use std::collections::{HashMap, HashSet}; |
253 | /// |
254 | /// let mut config = HashSet::new(); |
255 | /// config.insert(DirEntryAttr::Name); |
256 | /// config.insert(DirEntryAttr::Size); |
257 | /// |
258 | /// let entry_info = get_details_entry("test" , &config); |
259 | /// assert_eq!(2, entry_info.len()); |
260 | /// ``` |
261 | pub fn get_details_entry<P>( |
262 | path: P, |
263 | config: &HashSet<DirEntryAttr>, |
264 | ) -> Result<HashMap<DirEntryAttr, DirEntryValue>> |
265 | where |
266 | P: AsRef<Path>, |
267 | { |
268 | let path: &Path = path.as_ref(); |
269 | let metadata: Metadata = path.metadata()?; |
270 | get_details_entry_with_meta(path, config, metadata) |
271 | } |
272 | |
273 | fn get_details_entry_with_meta<P>( |
274 | path: P, |
275 | config: &HashSet<DirEntryAttr>, |
276 | metadata: Metadata, |
277 | ) -> Result<HashMap<DirEntryAttr, DirEntryValue>> |
278 | where |
279 | P: AsRef<Path>, |
280 | { |
281 | let path = path.as_ref(); |
282 | let mut item = HashMap::new(); |
283 | if config.contains(&DirEntryAttr::Name) { |
284 | if metadata.is_dir() { |
285 | if let Some(file_name) = path.file_name() { |
286 | item.insert( |
287 | DirEntryAttr::Name, |
288 | DirEntryValue::String(file_name.to_os_string().into_string()?), |
289 | ); |
290 | } else { |
291 | item.insert(DirEntryAttr::Name, DirEntryValue::String(String::new())); |
292 | } |
293 | } else if let Some(file_stem) = path.file_stem() { |
294 | item.insert( |
295 | DirEntryAttr::Name, |
296 | DirEntryValue::String(file_stem.to_os_string().into_string()?), |
297 | ); |
298 | } else { |
299 | item.insert(DirEntryAttr::Name, DirEntryValue::String(String::new())); |
300 | } |
301 | } |
302 | if config.contains(&DirEntryAttr::Ext) { |
303 | if let Some(value) = path.extension() { |
304 | item.insert( |
305 | DirEntryAttr::Ext, |
306 | DirEntryValue::String(value.to_os_string().into_string()?), |
307 | ); |
308 | } else { |
309 | item.insert(DirEntryAttr::Ext, DirEntryValue::String(String::from("" ))); |
310 | } |
311 | } |
312 | if config.contains(&DirEntryAttr::FullName) { |
313 | if let Some(file_name) = path.file_name() { |
314 | item.insert( |
315 | DirEntryAttr::FullName, |
316 | DirEntryValue::String(file_name.to_os_string().into_string()?), |
317 | ); |
318 | } else { |
319 | item.insert(DirEntryAttr::FullName, DirEntryValue::String(String::new())); |
320 | } |
321 | } |
322 | if config.contains(&DirEntryAttr::Path) { |
323 | let mut result_path: PathBuf; |
324 | match path.canonicalize() { |
325 | Ok(new_path) => { |
326 | result_path = new_path; |
327 | } |
328 | Err(_) => { |
329 | if let Some(parent_path) = path.parent() { |
330 | if let Some(name) = path.file_name() { |
331 | result_path = parent_path.canonicalize()?; |
332 | result_path.push(name); |
333 | } else { |
334 | err!("Error get part name path" , ErrorKind::Other); |
335 | } |
336 | } else { |
337 | err!("Error get parent path" , ErrorKind::Other); |
338 | } |
339 | } |
340 | } |
341 | let mut path = result_path.as_os_str().to_os_string().into_string()?; |
342 | if path.find(" \\\\? \\" ) == Some(0) { |
343 | path = path[4..].to_string(); |
344 | } |
345 | item.insert(DirEntryAttr::Path, DirEntryValue::String(path)); |
346 | } |
347 | if config.contains(&DirEntryAttr::DosPath) { |
348 | let mut result_path: PathBuf; |
349 | match path.canonicalize() { |
350 | Ok(new_path) => { |
351 | result_path = new_path; |
352 | } |
353 | Err(_) => { |
354 | if let Some(parent_path) = path.parent() { |
355 | if let Some(name) = path.file_name() { |
356 | result_path = parent_path.canonicalize()?; |
357 | result_path.push(name); |
358 | } else { |
359 | err!("Error get part name path" , ErrorKind::Other); |
360 | } |
361 | } else { |
362 | err!("Error get parent path" , ErrorKind::Other); |
363 | } |
364 | } |
365 | } |
366 | let path = result_path.as_os_str().to_os_string().into_string()?; |
367 | item.insert(DirEntryAttr::DosPath, DirEntryValue::String(path)); |
368 | } |
369 | if config.contains(&DirEntryAttr::Size) { |
370 | item.insert(DirEntryAttr::Size, DirEntryValue::U64(get_size(&path)?)); |
371 | } |
372 | if config.contains(&DirEntryAttr::FileSize) { |
373 | item.insert(DirEntryAttr::FileSize, DirEntryValue::U64(metadata.len())); |
374 | } |
375 | if config.contains(&DirEntryAttr::IsDir) { |
376 | item.insert( |
377 | DirEntryAttr::IsDir, |
378 | DirEntryValue::Boolean(metadata.is_dir()), |
379 | ); |
380 | } |
381 | if config.contains(&DirEntryAttr::IsFile) { |
382 | item.insert( |
383 | DirEntryAttr::IsFile, |
384 | DirEntryValue::Boolean(metadata.is_file()), |
385 | ); |
386 | } |
387 | if config.contains(&DirEntryAttr::Modified) { |
388 | item.insert( |
389 | DirEntryAttr::Modified, |
390 | DirEntryValue::SystemTime(metadata.modified()?), |
391 | ); |
392 | } |
393 | if config.contains(&DirEntryAttr::Accessed) { |
394 | item.insert( |
395 | DirEntryAttr::Accessed, |
396 | DirEntryValue::SystemTime(metadata.accessed()?), |
397 | ); |
398 | } |
399 | if config.contains(&DirEntryAttr::Created) { |
400 | item.insert( |
401 | DirEntryAttr::Created, |
402 | DirEntryValue::SystemTime(metadata.created()?), |
403 | ); |
404 | } |
405 | Ok(item) |
406 | } |
407 | |
408 | /// Returns a collection of directory entries with attributes specifying the information that should be returned. |
409 | /// |
410 | /// This function takes to arguments: |
411 | /// |
412 | /// * `path` - Path to directory. |
413 | /// |
414 | /// * `config` - Set attributes which you want see in return data. |
415 | /// |
416 | /// # Errors |
417 | /// |
418 | /// This function will return an error in the following situations, but is not limited to just |
419 | /// these cases: |
420 | /// |
421 | /// * This `path` directory does not exist. |
422 | /// * Invalid `path`. |
423 | /// * The current process does not have the permission to access `path`. |
424 | /// |
425 | /// #Examples |
426 | /// |
427 | /// ```rust,ignore |
428 | /// extern crate fs_extra; |
429 | /// use fs_extra::dir::{ls, DirEntryAttr, LsResult}; |
430 | /// use std::collections::HashSet; |
431 | /// |
432 | /// let mut config = HashSet::new(); |
433 | /// config.insert(DirEntryAttr::Name); |
434 | /// config.insert(DirEntryAttr::Size); |
435 | /// config.insert(DirEntryAttr::BaseInfo); |
436 | /// |
437 | /// let result = ls("test" , &config); |
438 | /// assert_eq!(2, ls_result.items.len()); |
439 | /// assert_eq!(2, ls_result.base.len()); |
440 | /// ``` |
441 | pub fn ls<P>(path: P, config: &HashSet<DirEntryAttr>) -> Result<LsResult> |
442 | where |
443 | P: AsRef<Path>, |
444 | { |
445 | let mut items: Vec> = Vec::new(); |
446 | let path: &Path = path.as_ref(); |
447 | if !path.is_dir() { |
448 | err!("Path does not directory" , ErrorKind::InvalidFolder); |
449 | } |
450 | for entry: Result in read_dir(&path)? { |
451 | let entry: DirEntry = entry?; |
452 | let path: PathBuf = entry.path(); |
453 | let metadata: Metadata = entry.metadata()?; |
454 | let item: HashMap = get_details_entry_with_meta(path, &config, metadata)?; |
455 | items.push(item); |
456 | } |
457 | let mut base: HashMap = HashMap::new(); |
458 | if config.contains(&DirEntryAttr::BaseInfo) { |
459 | base = get_details_entry(&path, &config)?; |
460 | } |
461 | Ok(LsResult { items, base }) |
462 | } |
463 | |
464 | /// Creates a new, empty directory at the provided path. |
465 | /// |
466 | /// This function takes to arguments: |
467 | /// |
468 | /// * `path` - Path to new directory. |
469 | /// |
470 | /// * `erase` - If set true and folder exist, then folder will be erased. |
471 | /// |
472 | /// #Errors |
473 | /// |
474 | /// This function will return an error in the following situations, |
475 | /// but is not limited to just these cases: |
476 | /// |
477 | /// * User lacks permissions to create directory at `path`. |
478 | /// |
479 | /// * `path` already exists if `erase` set false. |
480 | /// |
481 | /// #Examples |
482 | /// |
483 | /// ```rust,ignore |
484 | /// extern crate fs_extra; |
485 | /// use fs_extra::dir::create; |
486 | /// |
487 | /// create("dir" , false); // create directory |
488 | /// ``` |
489 | pub fn create<P>(path: P, erase: bool) -> Result<()> |
490 | where |
491 | P: AsRef<Path>, |
492 | { |
493 | if erase && path.as_ref().exists() { |
494 | remove(&path)?; |
495 | } |
496 | Ok(create_dir(&path)?) |
497 | } |
498 | |
499 | /// Recursively create a directory and all of its parent components if they are missing. |
500 | /// |
501 | /// This function takes to arguments: |
502 | /// |
503 | /// * `path` - Path to new directory. |
504 | /// |
505 | /// * `erase` - If set true and folder exist, then folder will be erased. |
506 | /// |
507 | ///#Errors |
508 | /// |
509 | /// This function will return an error in the following situations, |
510 | /// but is not limited to just these cases: |
511 | /// |
512 | /// * User lacks permissions to create directory at `path`. |
513 | /// |
514 | /// * `path` already exists if `erase` set false. |
515 | /// |
516 | /// #Examples |
517 | /// |
518 | /// ```rust,ignore |
519 | /// extern crate fs_extra; |
520 | /// use fs_extra::dir::create_all; |
521 | /// |
522 | /// create_all("/some/dir" , false); // create directory some and dir |
523 | pub fn create_all<P>(path: P, erase: bool) -> Result<()> |
524 | where |
525 | P: AsRef<Path>, |
526 | { |
527 | if erase && path.as_ref().exists() { |
528 | remove(&path)?; |
529 | } |
530 | Ok(create_dir_all(&path)?) |
531 | } |
532 | |
533 | /// Copies the directory contents from one place to another using recursive method. |
534 | /// This function will also copy the permission bits of the original files to |
535 | /// destination files (not for directories). |
536 | /// |
537 | /// # Errors |
538 | /// |
539 | /// This function will return an error in the following situations, but is not limited to just |
540 | /// these cases: |
541 | /// |
542 | /// * This `from` path is not a directory. |
543 | /// * This `from` directory does not exist. |
544 | /// * Invalid folder name for `from` or `to`. |
545 | /// * The current process does not have the permission to access `from` or write `to`. |
546 | /// |
547 | /// # Example |
548 | /// ```rust,ignore |
549 | /// extern crate fs_extra; |
550 | /// use fs_extra::dir::copy; |
551 | /// |
552 | /// let options = CopyOptions::new(); //Initialize default values for CopyOptions |
553 | /// // options.mirror_copy = true; // To mirror copy the whole structure of the source directory |
554 | /// |
555 | /// |
556 | /// // copy source/dir1 to target/dir1 |
557 | /// copy("source/dir1" , "target/dir1" , &options)?; |
558 | /// |
559 | /// ``` |
560 | pub fn copy<P, Q>(from: P, to: Q, options: &CopyOptions) -> Result<u64> |
561 | where |
562 | P: AsRef<Path>, |
563 | Q: AsRef<Path>, |
564 | { |
565 | let from = from.as_ref(); |
566 | |
567 | if !from.exists() { |
568 | if let Some(msg) = from.to_str() { |
569 | let msg = format!("Path \"{}\" does not exist or you don't have access!" , msg); |
570 | err!(&msg, ErrorKind::NotFound); |
571 | } |
572 | err!( |
573 | "Path does not exist Or you don't have access!" , |
574 | ErrorKind::NotFound |
575 | ); |
576 | } |
577 | if !from.is_dir() { |
578 | if let Some(msg) = from.to_str() { |
579 | let msg = format!("Path \"{}\" is not a directory!" , msg); |
580 | err!(&msg, ErrorKind::InvalidFolder); |
581 | } |
582 | err!("Path is not a directory!" , ErrorKind::InvalidFolder); |
583 | } |
584 | let dir_name; |
585 | if let Some(val) = from.components().last() { |
586 | dir_name = val.as_os_str(); |
587 | } else { |
588 | err!("Invalid folder from" , ErrorKind::InvalidFolder); |
589 | } |
590 | let mut to: PathBuf = to.as_ref().to_path_buf(); |
591 | if (to.exists() || !options.copy_inside) && !options.content_only { |
592 | to.push(dir_name); |
593 | } |
594 | |
595 | let mut read_options = DirOptions::new(); |
596 | if options.depth > 0 { |
597 | read_options.depth = options.depth; |
598 | } |
599 | |
600 | let dir_content = get_dir_content2(from, &read_options)?; |
601 | for directory in dir_content.directories { |
602 | let tmp_to = Path::new(&directory).strip_prefix(from)?; |
603 | let dir = to.join(&tmp_to); |
604 | if !dir.exists() { |
605 | if options.copy_inside { |
606 | create_all(dir, false)?; |
607 | } else { |
608 | create(dir, false)?; |
609 | } |
610 | } |
611 | } |
612 | let mut result: u64 = 0; |
613 | for file in dir_content.files { |
614 | let to = to.to_path_buf(); |
615 | let tp = Path::new(&file).strip_prefix(from)?; |
616 | let path = to.join(&tp); |
617 | |
618 | let file_options = super::file::CopyOptions { |
619 | overwrite: options.overwrite, |
620 | skip_exist: options.skip_exist, |
621 | buffer_size: options.buffer_size, |
622 | }; |
623 | let mut result_copy: Result<u64>; |
624 | let mut work = true; |
625 | |
626 | while work { |
627 | result_copy = super::file::copy(&file, &path, &file_options); |
628 | match result_copy { |
629 | Ok(val) => { |
630 | result += val; |
631 | work = false; |
632 | } |
633 | Err(err) => { |
634 | let err_msg = err.to_string(); |
635 | err!(err_msg.as_str(), err.kind) |
636 | } |
637 | } |
638 | } |
639 | } |
640 | Ok(result) |
641 | } |
642 | |
643 | /// Return DirContent which contains information about directory: |
644 | /// |
645 | /// * Size of the directory in bytes. |
646 | /// * List of source paths of files in the directory (files inside subdirectories included too). |
647 | /// * List of source paths of all directories and subdirectories. |
648 | /// |
649 | /// # Errors |
650 | /// |
651 | /// This function will return an error in the following situations, but is not limited to just |
652 | /// these cases: |
653 | /// |
654 | /// * This `path` directory does not exist. |
655 | /// * Invalid `path`. |
656 | /// * The current process does not have the permission to access `path`. |
657 | /// |
658 | /// # Examples |
659 | /// ```rust,ignore |
660 | /// extern crate fs_extra; |
661 | /// use fs_extra::dir::get_dir_content; |
662 | /// |
663 | /// let dir_content = get_dir_content("dir" )?; |
664 | /// for directory in dir_content.directories { |
665 | /// println!("{}" , directory); // print directory path |
666 | /// } |
667 | /// ``` |
668 | /// |
669 | pub fn get_dir_content<P>(path: P) -> Result<DirContent> |
670 | where |
671 | P: AsRef<Path>, |
672 | { |
673 | let options: DirOptions = DirOptions::new(); |
674 | get_dir_content2(path, &options) |
675 | } |
676 | |
677 | /// Return DirContent which contains information about directory: |
678 | /// |
679 | /// * Size directory. |
680 | /// * List all files source directory(files subdirectories included too). |
681 | /// * List all directory and subdirectories source path. |
682 | /// |
683 | /// # Errors |
684 | /// |
685 | /// This function will return an error in the following situations, but is not limited to just |
686 | /// these cases: |
687 | /// |
688 | /// * This `path` directory does not exist. |
689 | /// * Invalid `path`. |
690 | /// * The current process does not have the permission to access `path`. |
691 | /// |
692 | /// # Examples |
693 | /// ```rust,ignore |
694 | /// extern crate fs_extra; |
695 | /// use fs_extra::dir::{DirOptions, get_dir_content2}; |
696 | /// |
697 | /// let mut options = DirOptions::new(); |
698 | /// options.depth = 3; // Get 3 levels of folder. |
699 | /// let dir_content = get_dir_content2("dir" , &options)?; |
700 | /// for directory in dir_content.directories { |
701 | /// println!("{}" , directory); // print directory path |
702 | /// } |
703 | /// ``` |
704 | /// |
705 | pub fn get_dir_content2<P>(path: P, options: &DirOptions) -> Result<DirContent> |
706 | where |
707 | P: AsRef<Path>, |
708 | { |
709 | let mut depth: u64 = 0; |
710 | if options.depth != 0 { |
711 | depth = options.depth + 1; |
712 | } |
713 | _get_dir_content(path, depth) |
714 | } |
715 | |
716 | fn _get_dir_content<P>(path: P, mut depth: u64) -> Result<DirContent> |
717 | where |
718 | P: AsRef<Path>, |
719 | { |
720 | let mut directories = Vec::new(); |
721 | let mut files = Vec::new(); |
722 | let mut dir_size; |
723 | let item = path.as_ref().to_str(); |
724 | if item.is_none() { |
725 | err!("Invalid path" , ErrorKind::InvalidPath); |
726 | } |
727 | let item = item.unwrap().to_string(); |
728 | |
729 | if path.as_ref().is_dir() { |
730 | dir_size = path.as_ref().metadata()?.len(); |
731 | directories.push(item); |
732 | if depth == 0 || depth > 1 { |
733 | if depth > 1 { |
734 | depth -= 1; |
735 | } |
736 | for entry in read_dir(&path)? { |
737 | let _path = entry?.path(); |
738 | |
739 | match _get_dir_content(_path, depth) { |
740 | Ok(items) => { |
741 | let mut _files = items.files; |
742 | let mut _directories = items.directories; |
743 | dir_size += items.dir_size; |
744 | files.append(&mut _files); |
745 | directories.append(&mut _directories); |
746 | } |
747 | Err(err) => return Err(err), |
748 | } |
749 | } |
750 | } |
751 | } else { |
752 | dir_size = path.as_ref().metadata()?.len(); |
753 | files.push(item); |
754 | } |
755 | Ok(DirContent { |
756 | dir_size, |
757 | files, |
758 | directories, |
759 | }) |
760 | } |
761 | |
762 | /// Returns the size of the file or directory in bytes.(!important: folders size not count) |
763 | /// |
764 | /// If used on a directory, this function will recursively iterate over every file and every |
765 | /// directory inside the directory. This can be very time consuming if used on large directories. |
766 | /// |
767 | /// Does not follow symlinks. |
768 | /// |
769 | /// # Errors |
770 | /// |
771 | /// This function will return an error in the following situations, but is not limited to just |
772 | /// these cases: |
773 | /// |
774 | /// * This `path` directory does not exist. |
775 | /// * Invalid `path`. |
776 | /// * The current process does not have the permission to access `path`. |
777 | /// |
778 | /// # Examples |
779 | /// ```rust,ignore |
780 | /// extern crate fs_extra; |
781 | /// use fs_extra::dir::get_size; |
782 | /// |
783 | /// let folder_size = get_size("dir" )?; |
784 | /// println!("{}" , folder_size); // print directory size in bytes |
785 | /// ``` |
786 | pub fn get_size<P>(path: P) -> Result<u64> |
787 | where |
788 | P: AsRef<Path>, |
789 | { |
790 | // Using `fs::symlink_metadata` since we don't want to follow symlinks, |
791 | // as we're calculating the exact size of the requested path itself. |
792 | let path_metadata = path.as_ref().symlink_metadata()?; |
793 | |
794 | let mut size_in_bytes = 0; |
795 | |
796 | if path_metadata.is_dir() { |
797 | for entry in read_dir(&path)? { |
798 | let entry = entry?; |
799 | // `DirEntry::metadata` does not follow symlinks (unlike `fs::metadata`), so in the |
800 | // case of symlinks, this is the size of the symlink itself, not its target. |
801 | let entry_metadata = entry.metadata()?; |
802 | |
803 | if entry_metadata.is_dir() { |
804 | // The size of the directory entry itself will be counted inside the `get_size()` call, |
805 | // so we intentionally don't also add `entry_metadata.len()` to the total here. |
806 | size_in_bytes += get_size(entry.path())?; |
807 | } else { |
808 | size_in_bytes += entry_metadata.len(); |
809 | } |
810 | } |
811 | } else { |
812 | size_in_bytes = path_metadata.len(); |
813 | } |
814 | |
815 | Ok(size_in_bytes) |
816 | } |
817 | |
818 | /// Copies the directory contents from one place to another using recursive method, |
819 | /// with information about progress. This function will also copy the |
820 | /// permission bits of the original files to destination files (not for directories). |
821 | /// |
822 | /// # Errors |
823 | /// |
824 | /// This function will return an error in the following situations, but is not limited to just |
825 | /// these cases: |
826 | /// |
827 | /// * This `from` path is not a directory. |
828 | /// * This `from` directory does not exist. |
829 | /// * Invalid folder name for `from` or `to`. |
830 | /// * The current process does not have the permission to access `from` or write `to`. |
831 | /// |
832 | /// # Example |
833 | /// ```rust,ignore |
834 | /// extern crate fs_extra; |
835 | /// use fs_extra::dir::copy; |
836 | /// |
837 | /// let options = CopyOptions::new(); //Initialize default values for CopyOptions |
838 | /// let handle = |process_info: TransitProcess| { |
839 | /// println!("{}" , process_info.total_bytes); |
840 | /// fs_extra::dir::TransitProcessResult::ContinueOrAbort |
841 | /// } |
842 | /// // copy source/dir1 to target/dir1 |
843 | /// copy_with_progress("source/dir1" , "target/dir1" , &options, handle)?; |
844 | /// |
845 | /// ``` |
846 | pub fn copy_with_progress<P, Q, F>( |
847 | from: P, |
848 | to: Q, |
849 | options: &CopyOptions, |
850 | mut progress_handler: F, |
851 | ) -> Result<u64> |
852 | where |
853 | P: AsRef<Path>, |
854 | Q: AsRef<Path>, |
855 | F: FnMut(TransitProcess) -> TransitProcessResult, |
856 | { |
857 | let from = from.as_ref(); |
858 | |
859 | if !from.exists() { |
860 | if let Some(msg) = from.to_str() { |
861 | let msg = format!("Path \"{}\" does not exist or you don't have access!" , msg); |
862 | err!(&msg, ErrorKind::NotFound); |
863 | } |
864 | err!( |
865 | "Path does not exist or you don't have access!" , |
866 | ErrorKind::NotFound |
867 | ); |
868 | } |
869 | |
870 | let mut to: PathBuf = to.as_ref().to_path_buf(); |
871 | if !from.is_dir() { |
872 | if let Some(msg) = from.to_str() { |
873 | let msg = format!("Path \"{}\" is not a directory!" , msg); |
874 | err!(&msg, ErrorKind::InvalidFolder); |
875 | } |
876 | err!("Path is not a directory!" , ErrorKind::InvalidFolder); |
877 | } |
878 | |
879 | let dir_name; |
880 | if let Some(val) = from.components().last() { |
881 | dir_name = val.as_os_str(); |
882 | } else { |
883 | err!("Invalid folder from" , ErrorKind::InvalidFolder); |
884 | } |
885 | if (to.exists() || !options.copy_inside) && !options.content_only { |
886 | to.push(dir_name); |
887 | } |
888 | |
889 | let mut read_options = DirOptions::new(); |
890 | if options.depth > 0 { |
891 | read_options.depth = options.depth; |
892 | } |
893 | |
894 | let dir_content = get_dir_content2(from, &read_options)?; |
895 | for directory in dir_content.directories { |
896 | let tmp_to = Path::new(&directory).strip_prefix(from)?; |
897 | let dir = to.join(&tmp_to); |
898 | if !dir.exists() { |
899 | if options.copy_inside { |
900 | create_all(dir, false)?; |
901 | } else { |
902 | create(dir, false)?; |
903 | } |
904 | } |
905 | } |
906 | |
907 | let mut result: u64 = 0; |
908 | let mut info_process = TransitProcess { |
909 | copied_bytes: 0, |
910 | total_bytes: dir_content.dir_size, |
911 | file_bytes_copied: 0, |
912 | file_total_bytes: 0, |
913 | file_name: String::new(), |
914 | state: TransitState::Normal, |
915 | }; |
916 | |
917 | let mut options = options.clone(); |
918 | for file in dir_content.files { |
919 | let mut to = to.to_path_buf(); |
920 | let tp = Path::new(&file).strip_prefix(from)?; |
921 | let path = to.join(&tp); |
922 | |
923 | let file_name = path.file_name(); |
924 | if file_name.is_none() { |
925 | err!("No file name" ); |
926 | } |
927 | let file_name = file_name.unwrap(); |
928 | to.push(file_name); |
929 | |
930 | let mut file_options = super::file::CopyOptions { |
931 | overwrite: options.overwrite, |
932 | skip_exist: options.skip_exist, |
933 | buffer_size: options.buffer_size, |
934 | }; |
935 | |
936 | if let Some(file_name) = file_name.to_str() { |
937 | info_process.file_name = file_name.to_string(); |
938 | } else { |
939 | err!("Invalid file name" , ErrorKind::InvalidFileName); |
940 | } |
941 | |
942 | info_process.file_bytes_copied = 0; |
943 | info_process.file_total_bytes = Path::new(&file).metadata()?.len(); |
944 | |
945 | let mut result_copy: Result<u64>; |
946 | let mut work = true; |
947 | let copied_bytes = result; |
948 | while work { |
949 | { |
950 | let _progress_handler = |info: super::file::TransitProcess| { |
951 | info_process.copied_bytes = copied_bytes + info.copied_bytes; |
952 | info_process.file_bytes_copied = info.copied_bytes; |
953 | progress_handler(info_process.clone()); |
954 | }; |
955 | |
956 | result_copy = |
957 | super::file::copy_with_progress(&file, &path, &file_options, _progress_handler); |
958 | } |
959 | match result_copy { |
960 | Ok(val) => { |
961 | result += val; |
962 | work = false; |
963 | } |
964 | Err(err) => match err.kind { |
965 | ErrorKind::AlreadyExists => { |
966 | let mut info_process = info_process.clone(); |
967 | info_process.state = TransitState::Exists; |
968 | let user_decide = progress_handler(info_process); |
969 | match user_decide { |
970 | TransitProcessResult::Overwrite => { |
971 | file_options.overwrite = true; |
972 | } |
973 | TransitProcessResult::OverwriteAll => { |
974 | file_options.overwrite = true; |
975 | options.overwrite = true; |
976 | } |
977 | TransitProcessResult::Skip => { |
978 | file_options.skip_exist = true; |
979 | } |
980 | TransitProcessResult::SkipAll => { |
981 | file_options.skip_exist = true; |
982 | options.skip_exist = true; |
983 | } |
984 | TransitProcessResult::Retry => {} |
985 | TransitProcessResult::ContinueOrAbort => { |
986 | let err_msg = err.to_string(); |
987 | err!(err_msg.as_str(), err.kind) |
988 | } |
989 | TransitProcessResult::Abort => { |
990 | let err_msg = err.to_string(); |
991 | err!(err_msg.as_str(), err.kind) |
992 | } |
993 | } |
994 | } |
995 | ErrorKind::PermissionDenied => { |
996 | let mut info_process = info_process.clone(); |
997 | info_process.state = TransitState::Exists; |
998 | let user_decide = progress_handler(info_process); |
999 | match user_decide { |
1000 | TransitProcessResult::Overwrite => { |
1001 | err!("Overwrite denied for this situation!" , ErrorKind::Other); |
1002 | } |
1003 | TransitProcessResult::OverwriteAll => { |
1004 | err!("Overwrite denied for this situation!" , ErrorKind::Other); |
1005 | } |
1006 | TransitProcessResult::Skip => { |
1007 | file_options.skip_exist = true; |
1008 | } |
1009 | TransitProcessResult::SkipAll => { |
1010 | file_options.skip_exist = true; |
1011 | options.skip_exist = true; |
1012 | } |
1013 | TransitProcessResult::Retry => {} |
1014 | TransitProcessResult::ContinueOrAbort => { |
1015 | let err_msg = err.to_string(); |
1016 | err!(err_msg.as_str(), err.kind) |
1017 | } |
1018 | TransitProcessResult::Abort => { |
1019 | let err_msg = err.to_string(); |
1020 | err!(err_msg.as_str(), err.kind) |
1021 | } |
1022 | } |
1023 | } |
1024 | _ => { |
1025 | let err_msg = err.to_string(); |
1026 | err!(err_msg.as_str(), err.kind) |
1027 | } |
1028 | }, |
1029 | } |
1030 | } |
1031 | } |
1032 | |
1033 | Ok(result) |
1034 | } |
1035 | |
1036 | /// Moves the directory contents from one place to another. |
1037 | /// This function will also copy the permission bits of the original files to |
1038 | /// destination files (not for directories). |
1039 | /// |
1040 | /// # Errors |
1041 | /// |
1042 | /// This function will return an error in the following situations, but is not limited to just |
1043 | /// these cases: |
1044 | /// |
1045 | /// * This `from` path is not a directory. |
1046 | /// * This `from` directory does not exist. |
1047 | /// * Invalid folder name for `from` or `to`. |
1048 | /// * The current process does not have the permission to access `from` or write `to`. |
1049 | /// |
1050 | /// # Example |
1051 | /// ```rust,ignore |
1052 | /// extern crate fs_extra; |
1053 | /// use fs_extra::dir::move_dir; |
1054 | /// |
1055 | /// let options = CopyOptions::new(); //Initialize default values for CopyOptions |
1056 | /// |
1057 | /// // move source/dir1 to target/dir1 |
1058 | /// move_dir("source/dir1" , "target/dir1" , &options)?; |
1059 | /// |
1060 | /// ``` |
1061 | pub fn move_dir<P, Q>(from: P, to: Q, options: &CopyOptions) -> Result<u64> |
1062 | where |
1063 | P: AsRef<Path>, |
1064 | Q: AsRef<Path>, |
1065 | { |
1066 | let mut is_remove = true; |
1067 | if options.skip_exist && to.as_ref().exists() && !options.overwrite { |
1068 | is_remove = false; |
1069 | } |
1070 | let from = from.as_ref(); |
1071 | |
1072 | if !from.exists() { |
1073 | if let Some(msg) = from.to_str() { |
1074 | let msg = format!("Path \"{}\" does not exist" , msg); |
1075 | err!(&msg, ErrorKind::NotFound); |
1076 | } |
1077 | err!( |
1078 | "Path does not exist or you don't have access!" , |
1079 | ErrorKind::NotFound |
1080 | ); |
1081 | } |
1082 | |
1083 | let mut to: PathBuf = to.as_ref().to_path_buf(); |
1084 | if !from.is_dir() { |
1085 | if let Some(msg) = from.to_str() { |
1086 | let msg = format!( |
1087 | "Path \"{}\" is not a directory or you don't have access!" , |
1088 | msg |
1089 | ); |
1090 | err!(&msg, ErrorKind::InvalidFolder); |
1091 | } |
1092 | err!( |
1093 | "Path is not a directory or you don't have access!" , |
1094 | ErrorKind::InvalidFolder |
1095 | ); |
1096 | } |
1097 | let dir_name; |
1098 | if let Some(val) = from.components().last() { |
1099 | dir_name = val.as_os_str(); |
1100 | } else { |
1101 | err!("Invalid folder from" , ErrorKind::InvalidFolder); |
1102 | } |
1103 | |
1104 | if (to.exists() || !options.copy_inside) && !options.content_only { |
1105 | to.push(dir_name); |
1106 | } |
1107 | let dir_content = get_dir_content(from)?; |
1108 | for directory in dir_content.directories { |
1109 | let tmp_to = Path::new(&directory).strip_prefix(from)?; |
1110 | let dir = to.join(&tmp_to); |
1111 | if !dir.exists() { |
1112 | if options.copy_inside { |
1113 | create_all(dir, false)?; |
1114 | } else { |
1115 | create(dir, false)?; |
1116 | } |
1117 | } |
1118 | } |
1119 | let mut result: u64 = 0; |
1120 | for file in dir_content.files { |
1121 | let to = to.to_path_buf(); |
1122 | let tp = Path::new(&file).strip_prefix(from)?; |
1123 | let path = to.join(&tp); |
1124 | |
1125 | let file_options = super::file::CopyOptions { |
1126 | overwrite: options.overwrite, |
1127 | skip_exist: options.skip_exist, |
1128 | buffer_size: options.buffer_size, |
1129 | }; |
1130 | |
1131 | let mut result_copy: Result<u64>; |
1132 | let mut work = true; |
1133 | while work { |
1134 | { |
1135 | result_copy = super::file::move_file(&file, &path, &file_options); |
1136 | match result_copy { |
1137 | Ok(val) => { |
1138 | result += val; |
1139 | work = false; |
1140 | } |
1141 | Err(err) => { |
1142 | let err_msg = err.to_string(); |
1143 | err!(err_msg.as_str(), err.kind) |
1144 | } |
1145 | } |
1146 | } |
1147 | } |
1148 | } |
1149 | if is_remove { |
1150 | remove(from)?; |
1151 | } |
1152 | |
1153 | Ok(result) |
1154 | } |
1155 | |
1156 | /// Moves the directory contents from one place to another with information about progress. |
1157 | /// This function will also copy the permission bits of the original files to |
1158 | /// destination files (not for directories). |
1159 | /// |
1160 | /// # Errors |
1161 | /// |
1162 | /// This function will return an error in the following situations, but is not limited to just |
1163 | /// these cases: |
1164 | /// |
1165 | /// * This `from` path is not a directory. |
1166 | /// * This `from` directory does not exist. |
1167 | /// * Invalid folder name for `from` or `to`. |
1168 | /// * The current process does not have the permission to access `from` or write `to`. |
1169 | /// |
1170 | /// # Example |
1171 | /// ```rust,ignore |
1172 | /// extern crate fs_extra; |
1173 | /// use fs_extra::dir::move_dir_with_progress; |
1174 | /// |
1175 | /// let options = CopyOptions::new(); //Initialize default values for CopyOptions |
1176 | /// let handle = |process_info: TransitProcess| { |
1177 | /// println!("{}" , process_info.total_bytes); |
1178 | /// fs_extra::dir::TransitProcessResult::ContinueOrAbort |
1179 | /// } |
1180 | /// |
1181 | /// // move source/dir1 to target/dir1 |
1182 | /// move_dir_with_progress("source/dir1" , "target/dir1" , &options, handle)?; |
1183 | /// |
1184 | /// ``` |
1185 | pub fn move_dir_with_progress<P, Q, F>( |
1186 | from: P, |
1187 | to: Q, |
1188 | options: &CopyOptions, |
1189 | mut progress_handler: F, |
1190 | ) -> Result<u64> |
1191 | where |
1192 | P: AsRef<Path>, |
1193 | Q: AsRef<Path>, |
1194 | F: FnMut(TransitProcess) -> TransitProcessResult, |
1195 | { |
1196 | let mut is_remove = true; |
1197 | if options.skip_exist && to.as_ref().exists() && !options.overwrite { |
1198 | is_remove = false; |
1199 | } |
1200 | let from = from.as_ref(); |
1201 | |
1202 | if !from.exists() { |
1203 | if let Some(msg) = from.to_str() { |
1204 | let msg = format!("Path \"{}\" does not exist or you don't have access!" , msg); |
1205 | err!(&msg, ErrorKind::NotFound); |
1206 | } |
1207 | err!( |
1208 | "Path does not exist or you don't have access!" , |
1209 | ErrorKind::NotFound |
1210 | ); |
1211 | } |
1212 | |
1213 | let mut to: PathBuf = to.as_ref().to_path_buf(); |
1214 | if !from.is_dir() { |
1215 | if let Some(msg) = from.to_str() { |
1216 | let msg = format!("Path \"{}\" is not a directory!" , msg); |
1217 | err!(&msg, ErrorKind::InvalidFolder); |
1218 | } |
1219 | err!("Path is not a directory!" , ErrorKind::InvalidFolder); |
1220 | } |
1221 | let dir_name; |
1222 | if let Some(val) = from.components().last() { |
1223 | dir_name = val.as_os_str(); |
1224 | } else { |
1225 | err!("Invalid folder from" , ErrorKind::InvalidFolder); |
1226 | } |
1227 | if !(options.content_only || options.copy_inside && !to.exists()) { |
1228 | to.push(dir_name); |
1229 | } |
1230 | |
1231 | let dir_content = get_dir_content(from)?; |
1232 | for directory in dir_content.directories { |
1233 | let tmp_to = Path::new(&directory).strip_prefix(from)?; |
1234 | let dir = to.join(&tmp_to); |
1235 | if !dir.exists() { |
1236 | if options.copy_inside { |
1237 | create_all(dir, false)?; |
1238 | } else { |
1239 | create(dir, false)?; |
1240 | } |
1241 | } |
1242 | } |
1243 | |
1244 | let mut result: u64 = 0; |
1245 | let mut info_process = TransitProcess { |
1246 | copied_bytes: 0, |
1247 | total_bytes: dir_content.dir_size, |
1248 | file_bytes_copied: 0, |
1249 | file_total_bytes: 0, |
1250 | file_name: String::new(), |
1251 | state: TransitState::Normal, |
1252 | }; |
1253 | |
1254 | let mut options = options.clone(); |
1255 | for file in dir_content.files { |
1256 | let mut to = to.to_path_buf(); |
1257 | let tp = Path::new(&file).strip_prefix(from)?; |
1258 | let path = to.join(&tp); |
1259 | |
1260 | let file_name = path.file_name(); |
1261 | if file_name.is_none() { |
1262 | err!("No file name" ); |
1263 | } |
1264 | let file_name = file_name.unwrap(); |
1265 | to.push(file_name); |
1266 | |
1267 | let mut file_options = super::file::CopyOptions { |
1268 | overwrite: options.overwrite, |
1269 | skip_exist: options.skip_exist, |
1270 | buffer_size: options.buffer_size, |
1271 | }; |
1272 | |
1273 | if let Some(file_name) = file_name.to_str() { |
1274 | info_process.file_name = file_name.to_string(); |
1275 | } else { |
1276 | err!("Invalid file name" , ErrorKind::InvalidFileName); |
1277 | } |
1278 | |
1279 | info_process.file_bytes_copied = 0; |
1280 | info_process.file_total_bytes = Path::new(&file).metadata()?.len(); |
1281 | |
1282 | let mut result_copy: Result<u64>; |
1283 | let mut work = true; |
1284 | let copied_bytes = result; |
1285 | while work { |
1286 | { |
1287 | let _progress_handler = |info: super::file::TransitProcess| { |
1288 | info_process.copied_bytes = copied_bytes + info.copied_bytes; |
1289 | info_process.file_bytes_copied = info.copied_bytes; |
1290 | progress_handler(info_process.clone()); |
1291 | }; |
1292 | |
1293 | result_copy = super::file::move_file_with_progress( |
1294 | &file, |
1295 | &path, |
1296 | &file_options, |
1297 | _progress_handler, |
1298 | ); |
1299 | } |
1300 | match result_copy { |
1301 | Ok(val) => { |
1302 | result += val; |
1303 | work = false; |
1304 | } |
1305 | Err(err) => match err.kind { |
1306 | ErrorKind::AlreadyExists => { |
1307 | let mut info_process = info_process.clone(); |
1308 | info_process.state = TransitState::Exists; |
1309 | let user_decide = progress_handler(info_process); |
1310 | match user_decide { |
1311 | TransitProcessResult::Overwrite => { |
1312 | file_options.overwrite = true; |
1313 | } |
1314 | TransitProcessResult::OverwriteAll => { |
1315 | file_options.overwrite = true; |
1316 | options.overwrite = true; |
1317 | } |
1318 | TransitProcessResult::Skip => { |
1319 | is_remove = false; |
1320 | file_options.skip_exist = true; |
1321 | } |
1322 | TransitProcessResult::SkipAll => { |
1323 | is_remove = false; |
1324 | file_options.skip_exist = true; |
1325 | options.skip_exist = true; |
1326 | } |
1327 | TransitProcessResult::Retry => {} |
1328 | TransitProcessResult::ContinueOrAbort => { |
1329 | let err_msg = err.to_string(); |
1330 | err!(err_msg.as_str(), err.kind) |
1331 | } |
1332 | TransitProcessResult::Abort => { |
1333 | let err_msg = err.to_string(); |
1334 | err!(err_msg.as_str(), err.kind) |
1335 | } |
1336 | } |
1337 | } |
1338 | ErrorKind::PermissionDenied => { |
1339 | let mut info_process = info_process.clone(); |
1340 | info_process.state = TransitState::Exists; |
1341 | let user_decide = progress_handler(info_process); |
1342 | match user_decide { |
1343 | TransitProcessResult::Overwrite => { |
1344 | err!("Overwrite denied for this situation!" , ErrorKind::Other); |
1345 | } |
1346 | TransitProcessResult::OverwriteAll => { |
1347 | err!("Overwrite denied for this situation!" , ErrorKind::Other); |
1348 | } |
1349 | TransitProcessResult::Skip => { |
1350 | is_remove = false; |
1351 | file_options.skip_exist = true; |
1352 | } |
1353 | TransitProcessResult::SkipAll => { |
1354 | file_options.skip_exist = true; |
1355 | options.skip_exist = true; |
1356 | } |
1357 | TransitProcessResult::Retry => {} |
1358 | TransitProcessResult::ContinueOrAbort => { |
1359 | let err_msg = err.to_string(); |
1360 | err!(err_msg.as_str(), err.kind) |
1361 | } |
1362 | TransitProcessResult::Abort => { |
1363 | let err_msg = err.to_string(); |
1364 | err!(err_msg.as_str(), err.kind) |
1365 | } |
1366 | } |
1367 | } |
1368 | _ => { |
1369 | let err_msg = err.to_string(); |
1370 | err!(err_msg.as_str(), err.kind) |
1371 | } |
1372 | }, |
1373 | } |
1374 | } |
1375 | } |
1376 | if is_remove { |
1377 | remove(from)?; |
1378 | } |
1379 | |
1380 | Ok(result) |
1381 | } |
1382 | |
1383 | /// Removes directory. |
1384 | /// |
1385 | /// # Example |
1386 | /// ```rust,ignore |
1387 | /// extern crate fs_extra; |
1388 | /// use fs_extra::dir::remove; |
1389 | /// |
1390 | /// remove("source/dir1" ); // remove dir1 |
1391 | /// ``` |
1392 | pub fn remove<P: AsRef<Path>>(path: P) -> Result<()> { |
1393 | if path.as_ref().exists() { |
1394 | Ok(remove_dir_all(path)?) |
1395 | } else { |
1396 | Ok(()) |
1397 | } |
1398 | } |
1399 | |