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