fs_extra/
lib.rs

1macro_rules! err {
2    ($text:expr, $kind:expr) => {
3        return Err(Error::new($kind, $text))
4    };
5
6    ($text:expr) => {
7        err!($text, ErrorKind::Other)
8    };
9}
10
11/// The error type for fs_extra operations on files and directories.
12pub mod error;
13/// This module includes additional methods for working with files.
14///
15/// One of the distinguishing features is receipt information
16/// about process work with files.
17///
18/// # Example
19/// ```rust,ignore
20/// use std::path::Path;
21/// use std::{thread, time};
22/// use std::sync::mpsc::{self, TryRecvError};
23///
24/// extern crate fs_extra;
25/// use fs_extra::file::*;
26/// use fs_extra::error::*;
27///
28/// fn example_copy() -> Result<()> {
29///     let path_from = Path::new("./temp");
30///     let path_to = path_from.join("out");
31///     let test_file = (path_from.join("test_file.txt"), path_to.join("test_file.txt"));
32///
33///
34///     fs_extra::dir::create_all(&path_from, true)?;
35///     fs_extra::dir::create_all(&path_to, true)?;
36///
37///     write_all(&test_file.0, "test_data")?;
38///     assert!(test_file.0.exists());
39///     assert!(!test_file.1.exists());
40///
41///
42///     let options = CopyOptions {
43///         buffer_size: 1,
44///         ..Default::default()
45///     }
46///     let (tx, rx) = mpsc::channel();
47///     thread::spawn(move || {
48///         let handler = |process_info: TransitProcess| {
49///             tx.send(process_info).unwrap();
50///             thread::sleep(time::Duration::from_millis(500));
51///         };
52///         copy_with_progress(&test_file.0, &test_file.1, &options, handler).unwrap();
53///         assert!(test_file.0.exists());
54///         assert!(test_file.1.exists());
55///
56///     });
57///     loop {
58///         match rx.try_recv() {
59///             Ok(process_info) => {
60///                 println!("{} of {} bytes",
61///                          process_info.copied_bytes,
62///                          process_info.total_bytes);
63///             }
64///             Err(TryRecvError::Disconnected) => {
65///                 println!("finished");
66///                 break;
67///             }
68///             Err(TryRecvError::Empty) => {}
69///         }
70///     }
71///     Ok(())
72///
73/// }
74///
75///
76/// fn main() {
77///     example_copy();
78/// }
79///
80/// ```
81pub mod file;
82
83/// This module includes additional methods for working with directories.
84///
85/// One of the additional features is information
86/// about process and recursion operations.
87///
88/// # Example
89/// ```rust,ignore
90/// use std::path::Path;
91/// use std::{thread, time};
92/// use std::sync::mpsc::{self, TryRecvError};
93///
94/// extern crate fs_extra;
95/// use fs_extra::dir::*;
96/// use fs_extra::error::*;
97///
98/// fn example_copy() -> Result<()> {
99///
100///     let path_from = Path::new("./temp");
101///     let path_to = path_from.join("out");
102///     let test_folder = path_from.join("test_folder");
103///     let dir = test_folder.join("dir");
104///     let sub = dir.join("sub");
105///     let file1 = dir.join("file1.txt");
106///     let file2 = sub.join("file2.txt");
107///
108///     create_all(&sub, true)?;
109///     create_all(&path_to, true)?;
110///     fs_extra::file::write_all(&file1, "content1")?;
111///     fs_extra::file::write_all(&file2, "content2")?;
112///
113///     assert!(dir.exists());
114///     assert!(sub.exists());
115///     assert!(file1.exists());
116///     assert!(file2.exists());
117///
118///
119///     let options = CopyOptions {
120///         buffer_size: 1,
121///         ..Default::default(),
122///     };
123///     let (tx, rx) = mpsc::channel();
124///     thread::spawn(move || {
125///         let handler = |process_info: TransitProcess| {
126///             tx.send(process_info).unwrap();
127///             thread::sleep(time::Duration::from_millis(500));
128///         };
129///         copy_with_progress(&test_folder, &path_to, &options, handler).unwrap();
130///     });
131///
132///     loop {
133///         match rx.try_recv() {
134///             Ok(process_info) => {
135///                 println!("{} of {} bytes",
136///                          process_info.copied_bytes,
137///                          process_info.total_bytes);
138///             }
139///             Err(TryRecvError::Disconnected) => {
140///                 println!("finished");
141///                 break;
142///             }
143///             Err(TryRecvError::Empty) => {}
144///         }
145///     }
146///     Ok(())
147///
148/// }
149/// fn main() {
150///     example_copy();
151/// }
152/// ```
153///
154pub mod dir;
155
156use crate::error::*;
157use std::path::Path;
158
159/// Copies a list of directories and files to another place recursively. This function will
160/// also copy the permission bits of the original files to destination files (not for
161/// directories).
162///
163/// # Errors
164///
165/// This function will return an error in the following situations, but is not limited to just
166/// these case:
167///
168/// * List `from` contains  file or directory does not exist.
169///
170/// * List `from` contains  file or directory with invalid name.
171///
172/// * The current process does not have the permission to access to file from `lists from` or
173/// `to`.
174///
175/// # Example
176///
177/// ```rust,ignore
178///  extern crate fs_extra;
179///  use fs_extra::dir::copy;
180///
181///  let options = dir::CopyOptions::new(); //Initialize default values for CopyOptions
182///
183///  // copy dir1 and file1.txt to target/dir1 and target/file1.txt
184///  let mut from_paths = Vec::new();
185///  from_paths.push("source/dir1");
186///  from_paths.push("source/file.txt");
187///  copy_items(&from_paths, "target", &options)?;
188/// ```
189///
190pub fn copy_items<P, Q>(from: &[P], to: Q, options: &dir::CopyOptions) -> Result<u64>
191where
192    P: AsRef<Path>,
193    Q: AsRef<Path>,
194{
195    let mut result: u64 = 0;
196    if options.content_only {
197        err!(
198            "Options 'content_only' not acccess for copy_items function",
199            ErrorKind::Other
200        );
201    }
202    for item in from {
203        let item = item.as_ref();
204        if item.is_dir() {
205            result += dir::copy(item, &to, options)?;
206        } else if let Some(file_name) = item.file_name() {
207            if let Some(file_name) = file_name.to_str() {
208                let file_options = file::CopyOptions {
209                    overwrite: options.overwrite,
210                    skip_exist: options.skip_exist,
211                    ..Default::default()
212                };
213                result += file::copy(item, to.as_ref().join(file_name), &file_options)?;
214            }
215        } else {
216            err!("Invalid file name", ErrorKind::InvalidFileName);
217        }
218    }
219
220    Ok(result)
221}
222
223/// A structure which includes information about the current status of copying or moving a directory.
224pub struct TransitProcess {
225    /// Already copied bytes
226    pub copied_bytes: u64,
227    /// All the bytes which should be copied or moved (dir size).
228    pub total_bytes: u64,
229    /// Copied bytes on this time for file.
230    pub file_bytes_copied: u64,
231    /// Size of currently copied file.
232    pub file_total_bytes: u64,
233    /// Name of currently copied file.
234    pub file_name: String,
235    /// Name of currently copied folder.
236    pub dir_name: String,
237    /// Transit state
238    pub state: dir::TransitState,
239}
240
241impl Clone for TransitProcess {
242    fn clone(&self) -> TransitProcess {
243        TransitProcess {
244            copied_bytes: self.copied_bytes,
245            total_bytes: self.total_bytes,
246            file_bytes_copied: self.file_bytes_copied,
247            file_total_bytes: self.file_total_bytes,
248            file_name: self.file_name.clone(),
249            dir_name: self.dir_name.clone(),
250            state: self.state.clone(),
251        }
252    }
253}
254
255/// Copies a list of directories and files to another place recursively, with
256/// information about progress. This function will also copy the permission bits of the
257/// original files to destination files (not for directories).
258///
259/// # Errors
260///
261/// This function will return an error in the following situations, but is not limited to just
262/// these case:
263///
264/// * List `from` contains  file or directory does not exist.
265///
266/// * List `from` contains  file or directory with invalid name.
267///
268/// * The current process does not have the permission to access to file from `lists from` or
269/// `to`.
270///
271/// # Example
272/// ```rust,ignore
273///
274///  extern crate fs_extra;
275///  use fs_extra::dir::copy;
276///
277///  let options = dir::CopyOptions::new(); //Initialize default values for CopyOptions
278///  let handle = |process_info: TransitProcess| {
279///     println!("{}", process_info.total_bytes);
280///     fs_extra::dir::TransitProcessResult::ContinueOrAbort
281///  }
282///  // copy dir1 and file1.txt to target/dir1 and target/file1.txt
283///  let mut from_paths = Vec::new();
284///  from_paths.push("source/dir1");
285///  from_paths.push("source/file.txt");
286///  copy_items_with_progress(&from_paths, "target", &options, handle)?;
287/// ```
288///
289pub fn copy_items_with_progress<P, Q, F>(
290    from: &[P],
291    to: Q,
292    options: &dir::CopyOptions,
293    mut progress_handler: F,
294) -> Result<u64>
295where
296    P: AsRef<Path>,
297    Q: AsRef<Path>,
298    F: FnMut(TransitProcess) -> dir::TransitProcessResult,
299{
300    if options.content_only {
301        err!(
302            "Options 'content_only' not access for copy_items_with_progress function",
303            ErrorKind::Other
304        );
305    }
306    let mut total_size = 0;
307    let mut list_paths = Vec::new();
308    for item in from {
309        let item = item.as_ref();
310        total_size += dir::get_size(item)?;
311        list_paths.push(item);
312    }
313
314    let mut result: u64 = 0;
315    let mut info_process = TransitProcess {
316        copied_bytes: 0,
317        total_bytes: total_size,
318        file_bytes_copied: 0,
319        file_total_bytes: 0,
320        file_name: String::new(),
321        dir_name: String::new(),
322        state: dir::TransitState::Normal,
323    };
324
325    let mut options = options.clone();
326    for item in list_paths {
327        if item.is_dir() {
328            if let Some(dir_name) = item.components().last() {
329                if let Ok(dir_name) = dir_name.as_os_str().to_os_string().into_string() {
330                    info_process.dir_name = dir_name;
331                } else {
332                    err!("Invalid folder from", ErrorKind::InvalidFolder);
333                }
334            } else {
335                err!("Invalid folder from", ErrorKind::InvalidFolder);
336            }
337
338            let copied_bytes = result;
339            let dir_options = options.clone();
340            let handler = |info: dir::TransitProcess| {
341                info_process.copied_bytes = copied_bytes + info.copied_bytes;
342                info_process.state = info.state;
343                let result = progress_handler(info_process.clone());
344                match result {
345                    dir::TransitProcessResult::OverwriteAll => options.overwrite = true,
346                    dir::TransitProcessResult::SkipAll => options.skip_exist = true,
347                    _ => {}
348                }
349                result
350            };
351            result += dir::copy_with_progress(item, &to, &dir_options, handler)?;
352        } else {
353            let mut file_options = file::CopyOptions {
354                overwrite: options.overwrite,
355                skip_exist: options.skip_exist,
356                buffer_size: options.buffer_size,
357            };
358
359            if let Some(file_name) = item.file_name() {
360                if let Some(file_name) = file_name.to_str() {
361                    info_process.file_name = file_name.to_string();
362                } else {
363                    err!("Invalid file name", ErrorKind::InvalidFileName);
364                }
365            } else {
366                err!("Invalid file name", ErrorKind::InvalidFileName);
367            }
368
369            info_process.file_bytes_copied = 0;
370            info_process.file_total_bytes = item.metadata()?.len();
371
372            let copied_bytes = result;
373            let file_name = to.as_ref().join(info_process.file_name.clone());
374            let mut work = true;
375
376            let mut result_copy: Result<u64>;
377            while work {
378                {
379                    let handler = |info: file::TransitProcess| {
380                        info_process.copied_bytes = copied_bytes + info.copied_bytes;
381                        info_process.file_bytes_copied = info.copied_bytes;
382                        progress_handler(info_process.clone());
383                    };
384                    result_copy =
385                        file::copy_with_progress(item, &file_name, &file_options, handler);
386                }
387                match result_copy {
388                    Ok(val) => {
389                        result += val;
390                        work = false;
391                    }
392                    Err(err) => match err.kind {
393                        ErrorKind::AlreadyExists => {
394                            let mut info_process = info_process.clone();
395                            info_process.state = dir::TransitState::Exists;
396                            let user_decide = progress_handler(info_process);
397                            match user_decide {
398                                dir::TransitProcessResult::Overwrite => {
399                                    file_options.overwrite = true;
400                                }
401                                dir::TransitProcessResult::OverwriteAll => {
402                                    file_options.overwrite = true;
403                                    options.overwrite = true;
404                                }
405                                dir::TransitProcessResult::Skip => {
406                                    file_options.skip_exist = true;
407                                }
408                                dir::TransitProcessResult::SkipAll => {
409                                    file_options.skip_exist = true;
410                                    options.skip_exist = true;
411                                }
412                                dir::TransitProcessResult::Retry => {}
413                                dir::TransitProcessResult::ContinueOrAbort => {
414                                    let err_msg = err.to_string();
415                                    err!(err_msg.as_str(), err.kind)
416                                }
417                                dir::TransitProcessResult::Abort => {
418                                    let err_msg = err.to_string();
419                                    err!(err_msg.as_str(), err.kind)
420                                }
421                            }
422                        }
423                        ErrorKind::PermissionDenied => {
424                            let mut info_process = info_process.clone();
425                            info_process.state = dir::TransitState::Exists;
426                            let user_decide = progress_handler(info_process);
427                            match user_decide {
428                                dir::TransitProcessResult::Overwrite => {
429                                    err!("Overwrite denied for this situation!", ErrorKind::Other);
430                                }
431                                dir::TransitProcessResult::OverwriteAll => {
432                                    err!("Overwrite denied for this situation!", ErrorKind::Other);
433                                }
434                                dir::TransitProcessResult::Skip => {
435                                    file_options.skip_exist = true;
436                                }
437                                dir::TransitProcessResult::SkipAll => {
438                                    file_options.skip_exist = true;
439                                    options.skip_exist = true;
440                                }
441                                dir::TransitProcessResult::Retry => {}
442                                dir::TransitProcessResult::ContinueOrAbort => {
443                                    let err_msg = err.to_string();
444                                    err!(err_msg.as_str(), err.kind)
445                                }
446                                dir::TransitProcessResult::Abort => {
447                                    let err_msg = err.to_string();
448                                    err!(err_msg.as_str(), err.kind)
449                                }
450                            }
451                        }
452                        _ => {
453                            let err_msg = err.to_string();
454                            err!(err_msg.as_str(), err.kind)
455                        }
456                    },
457                }
458            }
459        }
460    }
461
462    Ok(result)
463}
464
465/// Moves a list of directories and files to another place recursively. This function will
466/// also copy the permission bits of the original files to destination files (not for
467/// directories).
468///
469/// # Errors
470///
471/// This function will return an error in the following situations, but is not limited to just
472/// these case:
473///
474/// * List `from` contains  file or directory does not exist.
475///
476/// * List `from` contains  file or directory with invalid name.
477///
478/// * The current process does not have the permission to access to file from `lists from` or
479/// `to`.
480///
481/// # Example
482///
483/// ```rust,ignore
484///  extern crate fs_extra;
485///  use fs_extra::dir::copy;
486///
487///  let options = dir::CopyOptions::new(); //Initialize default values for CopyOptions
488///
489///  // move dir1 and file1.txt to target/dir1 and target/file1.txt
490///  let mut from_paths = Vec::new();
491///  from_paths.push("source/dir1");
492///  from_paths.push("source/file.txt");
493///  move_items(&from_paths, "target", &options)?;
494/// ```
495///
496pub fn move_items<P, Q>(from_items: &[P], to: Q, options: &dir::CopyOptions) -> Result<u64>
497where
498    P: AsRef<Path>,
499    Q: AsRef<Path>,
500{
501    if options.content_only {
502        err!(
503            "Options 'content_only' not access for move_items function",
504            ErrorKind::Other
505        );
506    }
507    let mut total_size = 0;
508    let mut list_paths = Vec::new();
509    for item in from_items {
510        let item = item.as_ref();
511        total_size += dir::get_size(item)?;
512        list_paths.push(item);
513    }
514
515    let mut result = 0;
516    let mut info_process = TransitProcess {
517        copied_bytes: 0,
518        total_bytes: total_size,
519        file_bytes_copied: 0,
520        file_total_bytes: 0,
521        file_name: String::new(),
522        dir_name: String::new(),
523        state: dir::TransitState::Normal,
524    };
525
526    for item in list_paths {
527        if item.is_dir() {
528            if let Some(dir_name) = item.components().last() {
529                if let Ok(dir_name) = dir_name.as_os_str().to_os_string().into_string() {
530                    info_process.dir_name = dir_name;
531                } else {
532                    err!("Invalid folder from", ErrorKind::InvalidFolder);
533                }
534            } else {
535                err!("Invalid folder from", ErrorKind::InvalidFolder);
536            }
537
538            result += dir::move_dir(item, &to, options)?;
539        } else {
540            let file_options = file::CopyOptions {
541                overwrite: options.overwrite,
542                skip_exist: options.skip_exist,
543                buffer_size: options.buffer_size,
544            };
545
546            if let Some(file_name) = item.file_name() {
547                if let Some(file_name) = file_name.to_str() {
548                    info_process.file_name = file_name.to_string();
549                } else {
550                    err!("Invalid file name", ErrorKind::InvalidFileName);
551                }
552            } else {
553                err!("Invalid file name", ErrorKind::InvalidFileName);
554            }
555
556            info_process.file_bytes_copied = 0;
557            info_process.file_total_bytes = item.metadata()?.len();
558
559            let file_name = to.as_ref().join(info_process.file_name.clone());
560            result += file::move_file(item, &file_name, &file_options)?;
561        }
562    }
563
564    Ok(result)
565}
566
567/// Moves a list of directories and files to another place recursively, with
568/// information about progress. This function will also copy the permission bits of the
569/// original files to destination files (not for directories).
570///
571/// # Errors
572///
573/// This function will return an error in the following situations, but is not limited to just
574/// these case:
575///
576/// * List `from` contains  file or directory does not exist.
577///
578/// * List `from` contains  file or directory with invalid name.
579///
580/// * The current process does not have the permission to access to file from `lists from` or
581/// `to`.
582///
583/// # Example
584///
585/// ```rust,ignore
586///  extern crate fs_extra;
587///  use fs_extra::dir::copy;
588///
589///  let options = dir::CopyOptions::new(); //Initialize default values for CopyOptions
590///  let handle = |process_info: TransitProcess| {
591///     println!("{}", process_info.total_bytes);
592///     fs_extra::dir::TransitProcessResult::ContinueOrAbort
593///  }
594///  // move dir1 and file1.txt to target/dir1 and target/file1.txt
595///  let mut from_paths = Vec::new();
596///  from_paths.push("source/dir1");
597///  from_paths.push("source/file.txt");
598///  move_items_with_progress(&from_paths, "target", &options, handle)?;
599/// ```
600///
601pub fn move_items_with_progress<P, Q, F>(
602    from_items: &[P],
603    to: Q,
604    options: &dir::CopyOptions,
605    mut progress_handler: F,
606) -> Result<u64>
607where
608    P: AsRef<Path>,
609    Q: AsRef<Path>,
610    F: FnMut(TransitProcess) -> dir::TransitProcessResult,
611{
612    if options.content_only {
613        err!(
614            "Options 'content_only' not access for move_items_with_progress function",
615            ErrorKind::Other
616        );
617    }
618    let mut total_size = 0;
619    let mut list_paths = Vec::new();
620    for item in from_items {
621        let item = item.as_ref();
622        total_size += dir::get_size(item)?;
623        list_paths.push(item);
624    }
625
626    let mut result = 0;
627    let mut info_process = TransitProcess {
628        copied_bytes: 0,
629        total_bytes: total_size,
630        file_bytes_copied: 0,
631        file_total_bytes: 0,
632        file_name: String::new(),
633        dir_name: String::new(),
634        state: dir::TransitState::Normal,
635    };
636    let mut options = options.clone();
637
638    for item in list_paths {
639        if item.is_dir() {
640            if let Some(dir_name) = item.components().last() {
641                if let Ok(dir_name) = dir_name.as_os_str().to_os_string().into_string() {
642                    info_process.dir_name = dir_name;
643                } else {
644                    err!("Invalid folder from", ErrorKind::InvalidFolder);
645                }
646            } else {
647                err!("Invalid folder from", ErrorKind::InvalidFolder);
648            }
649
650            let copied_bytes = result;
651            let dir_options = options.clone();
652            let handler = |info: dir::TransitProcess| {
653                info_process.copied_bytes = copied_bytes + info.copied_bytes;
654                info_process.state = info.state;
655                let result = progress_handler(info_process.clone());
656                match result {
657                    dir::TransitProcessResult::OverwriteAll => options.overwrite = true,
658                    dir::TransitProcessResult::SkipAll => options.skip_exist = true,
659                    _ => {}
660                }
661                result
662            };
663            result += dir::move_dir_with_progress(item, &to, &dir_options, handler)?;
664        } else {
665            let mut file_options = file::CopyOptions {
666                overwrite: options.overwrite,
667                skip_exist: options.skip_exist,
668                buffer_size: options.buffer_size,
669            };
670
671            if let Some(file_name) = item.file_name() {
672                if let Some(file_name) = file_name.to_str() {
673                    info_process.file_name = file_name.to_string();
674                } else {
675                    err!("Invalid file name", ErrorKind::InvalidFileName);
676                }
677            } else {
678                err!("Invalid file name", ErrorKind::InvalidFileName);
679            }
680
681            info_process.file_bytes_copied = 0;
682            info_process.file_total_bytes = item.metadata()?.len();
683
684            let copied_bytes = result;
685            let file_name = to.as_ref().join(info_process.file_name.clone());
686            let mut work = true;
687
688            let mut result_copy: Result<u64>;
689            while work {
690                {
691                    let handler = |info: file::TransitProcess| {
692                        info_process.copied_bytes = copied_bytes + info.copied_bytes;
693                        info_process.file_bytes_copied = info.copied_bytes;
694                        progress_handler(info_process.clone());
695                    };
696                    result_copy =
697                        file::move_file_with_progress(item, &file_name, &file_options, handler);
698                }
699                match result_copy {
700                    Ok(val) => {
701                        result += val;
702                        work = false;
703                    }
704                    Err(err) => match err.kind {
705                        ErrorKind::AlreadyExists => {
706                            let mut info_process = info_process.clone();
707                            info_process.state = dir::TransitState::Exists;
708                            let user_decide = progress_handler(info_process);
709                            match user_decide {
710                                dir::TransitProcessResult::Overwrite => {
711                                    file_options.overwrite = true;
712                                }
713                                dir::TransitProcessResult::OverwriteAll => {
714                                    file_options.overwrite = true;
715                                    options.overwrite = true;
716                                }
717                                dir::TransitProcessResult::Skip => {
718                                    file_options.skip_exist = true;
719                                }
720                                dir::TransitProcessResult::SkipAll => {
721                                    file_options.skip_exist = true;
722                                    options.skip_exist = true;
723                                }
724                                dir::TransitProcessResult::Retry => {}
725                                dir::TransitProcessResult::ContinueOrAbort => {
726                                    let err_msg = err.to_string();
727                                    err!(err_msg.as_str(), err.kind)
728                                }
729                                dir::TransitProcessResult::Abort => {
730                                    let err_msg = err.to_string();
731                                    err!(err_msg.as_str(), err.kind)
732                                }
733                            }
734                        }
735                        ErrorKind::PermissionDenied => {
736                            let mut info_process = info_process.clone();
737                            info_process.state = dir::TransitState::Exists;
738                            let user_decide = progress_handler(info_process);
739                            match user_decide {
740                                dir::TransitProcessResult::Overwrite => {
741                                    err!("Overwrite denied for this situation!", ErrorKind::Other);
742                                }
743                                dir::TransitProcessResult::OverwriteAll => {
744                                    err!("Overwrite denied for this situation!", ErrorKind::Other);
745                                }
746                                dir::TransitProcessResult::Skip => {
747                                    file_options.skip_exist = true;
748                                }
749                                dir::TransitProcessResult::SkipAll => {
750                                    file_options.skip_exist = true;
751                                    options.skip_exist = true;
752                                }
753                                dir::TransitProcessResult::Retry => {}
754                                dir::TransitProcessResult::ContinueOrAbort => {
755                                    let err_msg = err.to_string();
756                                    err!(err_msg.as_str(), err.kind)
757                                }
758                                dir::TransitProcessResult::Abort => {
759                                    let err_msg = err.to_string();
760                                    err!(err_msg.as_str(), err.kind)
761                                }
762                            }
763                        }
764                        _ => {
765                            let err_msg = err.to_string();
766                            err!(err_msg.as_str(), err.kind)
767                        }
768                    },
769                }
770            }
771        }
772    }
773    Ok(result)
774}
775
776/// Removes a list of files or directories.
777///
778/// # Example
779///
780/// ```rust,ignore
781///  let mut from_paths = Vec::new();
782///  from_paths.push("source/dir1");
783///  from_paths.push("source/file.txt");
784///
785///  remove_items(&from_paths).unwrap();
786/// ```
787///
788pub fn remove_items<P>(from_items: &[P]) -> Result<()>
789where
790    P: AsRef<Path>,
791{
792    for item in from_items {
793        let item = item.as_ref();
794        if item.is_dir() {
795            dir::remove(item)?;
796        } else {
797            file::remove(item)?
798        }
799    }
800
801    Ok(())
802}