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: &P in from_items {
793 let item: &Path = item.as_ref();
794 if item.is_dir() {
795 dir::remove(path:item)?;
796 } else {
797 file::remove(path:item)?
798 }
799 }
800
801 Ok(())
802}
803