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 = path.as_ref();
269 let metadata: 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> = Vec::new();
446 let path: &Path = path.as_ref();
447 if !path.is_dir() {
448 err!("Path does not directory", ErrorKind::InvalidFolder);
449 }
450 for entry: Result in read_dir(&path)? {
451 let entry: DirEntry = entry?;
452 let path: PathBuf = entry.path();
453 let metadata: Metadata = entry.metadata()?;
454 let item: HashMap = get_details_entry_with_meta(path, &config, metadata)?;
455 items.push(item);
456 }
457 let mut base: HashMap = HashMap::new();
458 if config.contains(&DirEntryAttr::BaseInfo) {
459 base = get_details_entry(&path, &config)?;
460 }
461 Ok(LsResult { items, base })
462}
463
464/// Creates a new, empty directory at the provided path.
465///
466/// This function takes to arguments:
467///
468/// * `path` - Path to new directory.
469///
470/// * `erase` - If set true and folder exist, then folder will be erased.
471///
472/// #Errors
473///
474/// This function will return an error in the following situations,
475/// but is not limited to just these cases:
476///
477/// * User lacks permissions to create directory at `path`.
478///
479/// * `path` already exists if `erase` set false.
480///
481/// #Examples
482///
483/// ```rust,ignore
484/// extern crate fs_extra;
485/// use fs_extra::dir::create;
486///
487/// create("dir", false); // create directory
488/// ```
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 = 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: u64 = 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}
1399