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