fs_extra/
dir.rs

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