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