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