1 | macro_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. |
12 | pub 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 | /// ``` |
81 | pub 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 | /// |
154 | pub mod dir; |
155 | |
156 | use crate::error::*; |
157 | use 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 | /// |
190 | pub fn copy_items<P, Q>(from: &[P], to: Q, options: &dir::CopyOptions) -> Result<u64> |
191 | where |
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. |
224 | pub 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 | |
241 | impl 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 | /// |
289 | pub 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> |
295 | where |
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 | /// |
496 | pub fn move_items<P, Q>(from_items: &[P], to: Q, options: &dir::CopyOptions) -> Result<u64> |
497 | where |
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 | /// |
601 | pub 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> |
607 | where |
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 | /// |
788 | pub fn remove_items<P>(from_items: &[P]) -> Result<()> |
789 | where |
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 | |