1use crate::error::*;
2use std::collections::{HashMap, HashSet};
3use std::fs::{create_dir, create_dir_all, read_dir, remove_dir_all, Metadata};
4use std::path::{Path, PathBuf};
5use std::time::SystemTime;
6
7#[derive(Clone)]
9pub struct CopyOptions {
10 pub overwrite: bool,
12 pub skip_exist: bool,
14 pub buffer_size: usize,
16 pub copy_inside: bool,
18 pub content_only: bool,
20 pub depth: u64,
24}
25
26impl CopyOptions {
27 pub fn new() -> CopyOptions {
39 CopyOptions {
40 overwrite: false,
41 skip_exist: false,
42 buffer_size: 64000, copy_inside: false,
44 content_only: false,
45 depth: 0,
46 }
47 }
48
49 pub fn overwrite(mut self, overwrite: bool) -> Self {
51 self.overwrite = overwrite;
52 self
53 }
54
55 pub fn skip_exist(mut self, skip_exist: bool) -> Self {
57 self.skip_exist = skip_exist;
58 self
59 }
60
61 pub fn buffer_size(mut self, buffer_size: usize) -> Self {
63 self.buffer_size = buffer_size;
64 self
65 }
66
67 pub fn copy_inside(mut self, copy_inside: bool) -> Self {
69 self.copy_inside = copy_inside;
70 self
71 }
72
73 pub fn content_only(mut self, content_only: bool) -> Self {
75 self.content_only = content_only;
76 self
77 }
78
79 pub fn depth(mut self, depth: u64) -> Self {
81 self.depth = depth;
82 self
83 }
84}
85
86impl Default for CopyOptions {
87 fn default() -> Self {
88 CopyOptions::new()
89 }
90}
91
92#[derive(Clone, Default)]
94pub struct DirOptions {
95 pub depth: u64,
97}
98
99impl DirOptions {
100 pub fn new() -> DirOptions {
102 Default::default()
103 }
104}
105
106pub struct DirContent {
108 pub dir_size: u64,
110 pub files: Vec<String>,
112 pub directories: Vec<String>,
114}
115
116pub struct TransitProcess {
118 pub copied_bytes: u64,
120 pub total_bytes: u64,
122 pub file_bytes_copied: u64,
124 pub file_total_bytes: u64,
126 pub file_name: String,
128 pub state: TransitState,
130}
131
132#[derive(Hash, Eq, PartialEq, Clone)]
134pub enum TransitState {
135 Normal,
137 Exists,
139 NoAccess,
142}
143
144pub enum TransitProcessResult {
146 Overwrite,
148 OverwriteAll,
150 Skip,
152 SkipAll,
154 Retry,
156 Abort,
158 ContinueOrAbort,
160}
161
162impl Clone for TransitProcess {
163 fn clone(&self) -> TransitProcess {
164 TransitProcess {
165 copied_bytes: self.copied_bytes,
166 total_bytes: self.total_bytes,
167 file_bytes_copied: self.file_bytes_copied,
168 file_total_bytes: self.file_total_bytes,
169 file_name: self.file_name.clone(),
170 state: self.state.clone(),
171 }
172 }
173}
174
175#[derive(Hash, Eq, PartialEq, Clone)]
177pub enum DirEntryAttr {
178 Name,
180 Ext,
182 FullName,
184 Path,
186 DosPath,
188 FileSize,
190 Size,
194 IsDir,
196 IsFile,
198 Modified,
200 Accessed,
202 Created,
206 BaseInfo,
208}
209
210pub enum DirEntryValue {
212 String(String),
214 Boolean(bool),
216 SystemTime(SystemTime),
218 U64(u64),
220}
221
222pub struct LsResult {
224 pub base: HashMap<DirEntryAttr, DirEntryValue>,
226 pub items: Vec<HashMap<DirEntryAttr, DirEntryValue>>,
228}
229
230pub fn get_details_entry<P>(
262 path: P,
263 config: &HashSet<DirEntryAttr>,
264) -> Result<HashMap<DirEntryAttr, DirEntryValue>>
265where
266 P: AsRef<Path>,
267{
268 let path = path.as_ref();
269 let metadata = path.metadata()?;
270 get_details_entry_with_meta(path, config, metadata)
271}
272
273fn get_details_entry_with_meta<P>(
274 path: P,
275 config: &HashSet<DirEntryAttr>,
276 metadata: Metadata,
277) -> Result<HashMap<DirEntryAttr, DirEntryValue>>
278where
279 P: AsRef<Path>,
280{
281 let path = path.as_ref();
282 let mut item = HashMap::new();
283 if config.contains(&DirEntryAttr::Name) {
284 if metadata.is_dir() {
285 if let Some(file_name) = path.file_name() {
286 item.insert(
287 DirEntryAttr::Name,
288 DirEntryValue::String(file_name.to_os_string().into_string()?),
289 );
290 } else {
291 item.insert(DirEntryAttr::Name, DirEntryValue::String(String::new()));
292 }
293 } else if let Some(file_stem) = path.file_stem() {
294 item.insert(
295 DirEntryAttr::Name,
296 DirEntryValue::String(file_stem.to_os_string().into_string()?),
297 );
298 } else {
299 item.insert(DirEntryAttr::Name, DirEntryValue::String(String::new()));
300 }
301 }
302 if config.contains(&DirEntryAttr::Ext) {
303 if let Some(value) = path.extension() {
304 item.insert(
305 DirEntryAttr::Ext,
306 DirEntryValue::String(value.to_os_string().into_string()?),
307 );
308 } else {
309 item.insert(DirEntryAttr::Ext, DirEntryValue::String(String::from("")));
310 }
311 }
312 if config.contains(&DirEntryAttr::FullName) {
313 if let Some(file_name) = path.file_name() {
314 item.insert(
315 DirEntryAttr::FullName,
316 DirEntryValue::String(file_name.to_os_string().into_string()?),
317 );
318 } else {
319 item.insert(DirEntryAttr::FullName, DirEntryValue::String(String::new()));
320 }
321 }
322 if config.contains(&DirEntryAttr::Path) {
323 let mut result_path: PathBuf;
324 match path.canonicalize() {
325 Ok(new_path) => {
326 result_path = new_path;
327 }
328 Err(_) => {
329 if let Some(parent_path) = path.parent() {
330 if let Some(name) = path.file_name() {
331 result_path = parent_path.canonicalize()?;
332 result_path.push(name);
333 } else {
334 err!("Error get part name path", ErrorKind::Other);
335 }
336 } else {
337 err!("Error get parent path", ErrorKind::Other);
338 }
339 }
340 }
341 let mut path = result_path.as_os_str().to_os_string().into_string()?;
342 if path.find("\\\\?\\") == Some(0) {
343 path = path[4..].to_string();
344 }
345 item.insert(DirEntryAttr::Path, DirEntryValue::String(path));
346 }
347 if config.contains(&DirEntryAttr::DosPath) {
348 let mut result_path: PathBuf;
349 match path.canonicalize() {
350 Ok(new_path) => {
351 result_path = new_path;
352 }
353 Err(_) => {
354 if let Some(parent_path) = path.parent() {
355 if let Some(name) = path.file_name() {
356 result_path = parent_path.canonicalize()?;
357 result_path.push(name);
358 } else {
359 err!("Error get part name path", ErrorKind::Other);
360 }
361 } else {
362 err!("Error get parent path", ErrorKind::Other);
363 }
364 }
365 }
366 let path = result_path.as_os_str().to_os_string().into_string()?;
367 item.insert(DirEntryAttr::DosPath, DirEntryValue::String(path));
368 }
369 if config.contains(&DirEntryAttr::Size) {
370 item.insert(DirEntryAttr::Size, DirEntryValue::U64(get_size(&path)?));
371 }
372 if config.contains(&DirEntryAttr::FileSize) {
373 item.insert(DirEntryAttr::FileSize, DirEntryValue::U64(metadata.len()));
374 }
375 if config.contains(&DirEntryAttr::IsDir) {
376 item.insert(
377 DirEntryAttr::IsDir,
378 DirEntryValue::Boolean(metadata.is_dir()),
379 );
380 }
381 if config.contains(&DirEntryAttr::IsFile) {
382 item.insert(
383 DirEntryAttr::IsFile,
384 DirEntryValue::Boolean(metadata.is_file()),
385 );
386 }
387 if config.contains(&DirEntryAttr::Modified) {
388 item.insert(
389 DirEntryAttr::Modified,
390 DirEntryValue::SystemTime(metadata.modified()?),
391 );
392 }
393 if config.contains(&DirEntryAttr::Accessed) {
394 item.insert(
395 DirEntryAttr::Accessed,
396 DirEntryValue::SystemTime(metadata.accessed()?),
397 );
398 }
399 if config.contains(&DirEntryAttr::Created) {
400 item.insert(
401 DirEntryAttr::Created,
402 DirEntryValue::SystemTime(metadata.created()?),
403 );
404 }
405 Ok(item)
406}
407
408pub fn ls<P>(path: P, config: &HashSet<DirEntryAttr>) -> Result<LsResult>
442where
443 P: AsRef<Path>,
444{
445 let mut items = Vec::new();
446 let path = path.as_ref();
447 if !path.is_dir() {
448 err!("Path does not directory", ErrorKind::InvalidFolder);
449 }
450 for entry in read_dir(&path)? {
451 let entry = entry?;
452 let path = entry.path();
453 let metadata = entry.metadata()?;
454 let item = get_details_entry_with_meta(path, &config, metadata)?;
455 items.push(item);
456 }
457 let mut base = HashMap::new();
458 if config.contains(&DirEntryAttr::BaseInfo) {
459 base = get_details_entry(&path, &config)?;
460 }
461 Ok(LsResult { items, base })
462}
463
464pub fn create<P>(path: P, erase: bool) -> Result<()>
490where
491 P: AsRef<Path>,
492{
493 if erase && path.as_ref().exists() {
494 remove(&path)?;
495 }
496 Ok(create_dir(&path)?)
497}
498
499pub fn create_all<P>(path: P, erase: bool) -> Result<()>
524where
525 P: AsRef<Path>,
526{
527 if erase && path.as_ref().exists() {
528 remove(&path)?;
529 }
530 Ok(create_dir_all(&path)?)
531}
532
533pub fn copy<P, Q>(from: P, to: Q, options: &CopyOptions) -> Result<u64>
561where
562 P: AsRef<Path>,
563 Q: AsRef<Path>,
564{
565 let from = from.as_ref();
566
567 if !from.exists() {
568 if let Some(msg) = from.to_str() {
569 let msg = format!("Path \"{}\" does not exist or you don't have access!", msg);
570 err!(&msg, ErrorKind::NotFound);
571 }
572 err!(
573 "Path does not exist Or you don't have access!",
574 ErrorKind::NotFound
575 );
576 }
577 if !from.is_dir() {
578 if let Some(msg) = from.to_str() {
579 let msg = format!("Path \"{}\" is not a directory!", msg);
580 err!(&msg, ErrorKind::InvalidFolder);
581 }
582 err!("Path is not a directory!", ErrorKind::InvalidFolder);
583 }
584 let dir_name;
585 if let Some(val) = from.components().last() {
586 dir_name = val.as_os_str();
587 } else {
588 err!("Invalid folder from", ErrorKind::InvalidFolder);
589 }
590 let mut to: PathBuf = to.as_ref().to_path_buf();
591 if (to.exists() || !options.copy_inside) && !options.content_only {
592 to.push(dir_name);
593 }
594
595 let mut read_options = DirOptions::new();
596 if options.depth > 0 {
597 read_options.depth = options.depth;
598 }
599
600 let dir_content = get_dir_content2(from, &read_options)?;
601 for directory in dir_content.directories {
602 let tmp_to = Path::new(&directory).strip_prefix(from)?;
603 let dir = to.join(&tmp_to);
604 if !dir.exists() {
605 if options.copy_inside {
606 create_all(dir, false)?;
607 } else {
608 create(dir, false)?;
609 }
610 }
611 }
612 let mut result: u64 = 0;
613 for file in dir_content.files {
614 let to = to.to_path_buf();
615 let tp = Path::new(&file).strip_prefix(from)?;
616 let path = to.join(&tp);
617
618 let file_options = super::file::CopyOptions {
619 overwrite: options.overwrite,
620 skip_exist: options.skip_exist,
621 buffer_size: options.buffer_size,
622 };
623 let mut result_copy: Result<u64>;
624 let mut work = true;
625
626 while work {
627 result_copy = super::file::copy(&file, &path, &file_options);
628 match result_copy {
629 Ok(val) => {
630 result += val;
631 work = false;
632 }
633 Err(err) => {
634 let err_msg = err.to_string();
635 err!(err_msg.as_str(), err.kind)
636 }
637 }
638 }
639 }
640 Ok(result)
641}
642
643pub fn get_dir_content<P>(path: P) -> Result<DirContent>
670where
671 P: AsRef<Path>,
672{
673 let options = DirOptions::new();
674 get_dir_content2(path, &options)
675}
676
677pub fn get_dir_content2<P>(path: P, options: &DirOptions) -> Result<DirContent>
706where
707 P: AsRef<Path>,
708{
709 let mut depth = 0;
710 if options.depth != 0 {
711 depth = options.depth + 1;
712 }
713 _get_dir_content(path, depth)
714}
715
716fn _get_dir_content<P>(path: P, mut depth: u64) -> Result<DirContent>
717where
718 P: AsRef<Path>,
719{
720 let mut directories = Vec::new();
721 let mut files = Vec::new();
722 let mut dir_size;
723 let item = path.as_ref().to_str();
724 if item.is_none() {
725 err!("Invalid path", ErrorKind::InvalidPath);
726 }
727 let item = item.unwrap().to_string();
728
729 if path.as_ref().is_dir() {
730 dir_size = path.as_ref().metadata()?.len();
731 directories.push(item);
732 if depth == 0 || depth > 1 {
733 if depth > 1 {
734 depth -= 1;
735 }
736 for entry in read_dir(&path)? {
737 let _path = entry?.path();
738
739 match _get_dir_content(_path, depth) {
740 Ok(items) => {
741 let mut _files = items.files;
742 let mut _directories = items.directories;
743 dir_size += items.dir_size;
744 files.append(&mut _files);
745 directories.append(&mut _directories);
746 }
747 Err(err) => return Err(err),
748 }
749 }
750 }
751 } else {
752 dir_size = path.as_ref().metadata()?.len();
753 files.push(item);
754 }
755 Ok(DirContent {
756 dir_size,
757 files,
758 directories,
759 })
760}
761
762pub fn get_size<P>(path: P) -> Result<u64>
787where
788 P: AsRef<Path>,
789{
790 let path_metadata = path.as_ref().symlink_metadata()?;
793
794 let mut size_in_bytes = 0;
795
796 if path_metadata.is_dir() {
797 for entry in read_dir(&path)? {
798 let entry = entry?;
799 let entry_metadata = entry.metadata()?;
802
803 if entry_metadata.is_dir() {
804 size_in_bytes += get_size(entry.path())?;
807 } else {
808 size_in_bytes += entry_metadata.len();
809 }
810 }
811 } else {
812 size_in_bytes = path_metadata.len();
813 }
814
815 Ok(size_in_bytes)
816}
817
818pub fn copy_with_progress<P, Q, F>(
847 from: P,
848 to: Q,
849 options: &CopyOptions,
850 mut progress_handler: F,
851) -> Result<u64>
852where
853 P: AsRef<Path>,
854 Q: AsRef<Path>,
855 F: FnMut(TransitProcess) -> TransitProcessResult,
856{
857 let from = from.as_ref();
858
859 if !from.exists() {
860 if let Some(msg) = from.to_str() {
861 let msg = format!("Path \"{}\" does not exist or you don't have access!", msg);
862 err!(&msg, ErrorKind::NotFound);
863 }
864 err!(
865 "Path does not exist or you don't have access!",
866 ErrorKind::NotFound
867 );
868 }
869
870 let mut to: PathBuf = to.as_ref().to_path_buf();
871 if !from.is_dir() {
872 if let Some(msg) = from.to_str() {
873 let msg = format!("Path \"{}\" is not a directory!", msg);
874 err!(&msg, ErrorKind::InvalidFolder);
875 }
876 err!("Path is not a directory!", ErrorKind::InvalidFolder);
877 }
878
879 let dir_name;
880 if let Some(val) = from.components().last() {
881 dir_name = val.as_os_str();
882 } else {
883 err!("Invalid folder from", ErrorKind::InvalidFolder);
884 }
885 if (to.exists() || !options.copy_inside) && !options.content_only {
886 to.push(dir_name);
887 }
888
889 let mut read_options = DirOptions::new();
890 if options.depth > 0 {
891 read_options.depth = options.depth;
892 }
893
894 let dir_content = get_dir_content2(from, &read_options)?;
895 for directory in dir_content.directories {
896 let tmp_to = Path::new(&directory).strip_prefix(from)?;
897 let dir = to.join(&tmp_to);
898 if !dir.exists() {
899 if options.copy_inside {
900 create_all(dir, false)?;
901 } else {
902 create(dir, false)?;
903 }
904 }
905 }
906
907 let mut result: u64 = 0;
908 let mut info_process = TransitProcess {
909 copied_bytes: 0,
910 total_bytes: dir_content.dir_size,
911 file_bytes_copied: 0,
912 file_total_bytes: 0,
913 file_name: String::new(),
914 state: TransitState::Normal,
915 };
916
917 let mut options = options.clone();
918 for file in dir_content.files {
919 let mut to = to.to_path_buf();
920 let tp = Path::new(&file).strip_prefix(from)?;
921 let path = to.join(&tp);
922
923 let file_name = path.file_name();
924 if file_name.is_none() {
925 err!("No file name");
926 }
927 let file_name = file_name.unwrap();
928 to.push(file_name);
929
930 let mut file_options = super::file::CopyOptions {
931 overwrite: options.overwrite,
932 skip_exist: options.skip_exist,
933 buffer_size: options.buffer_size,
934 };
935
936 if let Some(file_name) = file_name.to_str() {
937 info_process.file_name = file_name.to_string();
938 } else {
939 err!("Invalid file name", ErrorKind::InvalidFileName);
940 }
941
942 info_process.file_bytes_copied = 0;
943 info_process.file_total_bytes = Path::new(&file).metadata()?.len();
944
945 let mut result_copy: Result<u64>;
946 let mut work = true;
947 let copied_bytes = result;
948 while work {
949 {
950 let _progress_handler = |info: super::file::TransitProcess| {
951 info_process.copied_bytes = copied_bytes + info.copied_bytes;
952 info_process.file_bytes_copied = info.copied_bytes;
953 progress_handler(info_process.clone());
954 };
955
956 result_copy =
957 super::file::copy_with_progress(&file, &path, &file_options, _progress_handler);
958 }
959 match result_copy {
960 Ok(val) => {
961 result += val;
962 work = false;
963 }
964 Err(err) => match err.kind {
965 ErrorKind::AlreadyExists => {
966 let mut info_process = info_process.clone();
967 info_process.state = TransitState::Exists;
968 let user_decide = progress_handler(info_process);
969 match user_decide {
970 TransitProcessResult::Overwrite => {
971 file_options.overwrite = true;
972 }
973 TransitProcessResult::OverwriteAll => {
974 file_options.overwrite = true;
975 options.overwrite = true;
976 }
977 TransitProcessResult::Skip => {
978 file_options.skip_exist = true;
979 }
980 TransitProcessResult::SkipAll => {
981 file_options.skip_exist = true;
982 options.skip_exist = true;
983 }
984 TransitProcessResult::Retry => {}
985 TransitProcessResult::ContinueOrAbort => {
986 let err_msg = err.to_string();
987 err!(err_msg.as_str(), err.kind)
988 }
989 TransitProcessResult::Abort => {
990 let err_msg = err.to_string();
991 err!(err_msg.as_str(), err.kind)
992 }
993 }
994 }
995 ErrorKind::PermissionDenied => {
996 let mut info_process = info_process.clone();
997 info_process.state = TransitState::Exists;
998 let user_decide = progress_handler(info_process);
999 match user_decide {
1000 TransitProcessResult::Overwrite => {
1001 err!("Overwrite denied for this situation!", ErrorKind::Other);
1002 }
1003 TransitProcessResult::OverwriteAll => {
1004 err!("Overwrite denied for this situation!", ErrorKind::Other);
1005 }
1006 TransitProcessResult::Skip => {
1007 file_options.skip_exist = true;
1008 }
1009 TransitProcessResult::SkipAll => {
1010 file_options.skip_exist = true;
1011 options.skip_exist = true;
1012 }
1013 TransitProcessResult::Retry => {}
1014 TransitProcessResult::ContinueOrAbort => {
1015 let err_msg = err.to_string();
1016 err!(err_msg.as_str(), err.kind)
1017 }
1018 TransitProcessResult::Abort => {
1019 let err_msg = err.to_string();
1020 err!(err_msg.as_str(), err.kind)
1021 }
1022 }
1023 }
1024 _ => {
1025 let err_msg = err.to_string();
1026 err!(err_msg.as_str(), err.kind)
1027 }
1028 },
1029 }
1030 }
1031 }
1032
1033 Ok(result)
1034}
1035
1036pub fn move_dir<P, Q>(from: P, to: Q, options: &CopyOptions) -> Result<u64>
1062where
1063 P: AsRef<Path>,
1064 Q: AsRef<Path>,
1065{
1066 let mut is_remove = true;
1067 if options.skip_exist && to.as_ref().exists() && !options.overwrite {
1068 is_remove = false;
1069 }
1070 let from = from.as_ref();
1071
1072 if !from.exists() {
1073 if let Some(msg) = from.to_str() {
1074 let msg = format!("Path \"{}\" does not exist", msg);
1075 err!(&msg, ErrorKind::NotFound);
1076 }
1077 err!(
1078 "Path does not exist or you don't have access!",
1079 ErrorKind::NotFound
1080 );
1081 }
1082
1083 let mut to: PathBuf = to.as_ref().to_path_buf();
1084 if !from.is_dir() {
1085 if let Some(msg) = from.to_str() {
1086 let msg = format!(
1087 "Path \"{}\" is not a directory or you don't have access!",
1088 msg
1089 );
1090 err!(&msg, ErrorKind::InvalidFolder);
1091 }
1092 err!(
1093 "Path is not a directory or you don't have access!",
1094 ErrorKind::InvalidFolder
1095 );
1096 }
1097 let dir_name;
1098 if let Some(val) = from.components().last() {
1099 dir_name = val.as_os_str();
1100 } else {
1101 err!("Invalid folder from", ErrorKind::InvalidFolder);
1102 }
1103
1104 if (to.exists() || !options.copy_inside) && !options.content_only {
1105 to.push(dir_name);
1106 }
1107 let dir_content = get_dir_content(from)?;
1108 for directory in dir_content.directories {
1109 let tmp_to = Path::new(&directory).strip_prefix(from)?;
1110 let dir = to.join(&tmp_to);
1111 if !dir.exists() {
1112 if options.copy_inside {
1113 create_all(dir, false)?;
1114 } else {
1115 create(dir, false)?;
1116 }
1117 }
1118 }
1119 let mut result: u64 = 0;
1120 for file in dir_content.files {
1121 let to = to.to_path_buf();
1122 let tp = Path::new(&file).strip_prefix(from)?;
1123 let path = to.join(&tp);
1124
1125 let file_options = super::file::CopyOptions {
1126 overwrite: options.overwrite,
1127 skip_exist: options.skip_exist,
1128 buffer_size: options.buffer_size,
1129 };
1130
1131 let mut result_copy: Result<u64>;
1132 let mut work = true;
1133 while work {
1134 {
1135 result_copy = super::file::move_file(&file, &path, &file_options);
1136 match result_copy {
1137 Ok(val) => {
1138 result += val;
1139 work = false;
1140 }
1141 Err(err) => {
1142 let err_msg = err.to_string();
1143 err!(err_msg.as_str(), err.kind)
1144 }
1145 }
1146 }
1147 }
1148 }
1149 if is_remove {
1150 remove(from)?;
1151 }
1152
1153 Ok(result)
1154}
1155
1156pub fn move_dir_with_progress<P, Q, F>(
1186 from: P,
1187 to: Q,
1188 options: &CopyOptions,
1189 mut progress_handler: F,
1190) -> Result<u64>
1191where
1192 P: AsRef<Path>,
1193 Q: AsRef<Path>,
1194 F: FnMut(TransitProcess) -> TransitProcessResult,
1195{
1196 let mut is_remove = true;
1197 if options.skip_exist && to.as_ref().exists() && !options.overwrite {
1198 is_remove = false;
1199 }
1200 let from = from.as_ref();
1201
1202 if !from.exists() {
1203 if let Some(msg) = from.to_str() {
1204 let msg = format!("Path \"{}\" does not exist or you don't have access!", msg);
1205 err!(&msg, ErrorKind::NotFound);
1206 }
1207 err!(
1208 "Path does not exist or you don't have access!",
1209 ErrorKind::NotFound
1210 );
1211 }
1212
1213 let mut to: PathBuf = to.as_ref().to_path_buf();
1214 if !from.is_dir() {
1215 if let Some(msg) = from.to_str() {
1216 let msg = format!("Path \"{}\" is not a directory!", msg);
1217 err!(&msg, ErrorKind::InvalidFolder);
1218 }
1219 err!("Path is not a directory!", ErrorKind::InvalidFolder);
1220 }
1221 let dir_name;
1222 if let Some(val) = from.components().last() {
1223 dir_name = val.as_os_str();
1224 } else {
1225 err!("Invalid folder from", ErrorKind::InvalidFolder);
1226 }
1227 if !(options.content_only || options.copy_inside && !to.exists()) {
1228 to.push(dir_name);
1229 }
1230
1231 let dir_content = get_dir_content(from)?;
1232 for directory in dir_content.directories {
1233 let tmp_to = Path::new(&directory).strip_prefix(from)?;
1234 let dir = to.join(&tmp_to);
1235 if !dir.exists() {
1236 if options.copy_inside {
1237 create_all(dir, false)?;
1238 } else {
1239 create(dir, false)?;
1240 }
1241 }
1242 }
1243
1244 let mut result: u64 = 0;
1245 let mut info_process = TransitProcess {
1246 copied_bytes: 0,
1247 total_bytes: dir_content.dir_size,
1248 file_bytes_copied: 0,
1249 file_total_bytes: 0,
1250 file_name: String::new(),
1251 state: TransitState::Normal,
1252 };
1253
1254 let mut options = options.clone();
1255 for file in dir_content.files {
1256 let mut to = to.to_path_buf();
1257 let tp = Path::new(&file).strip_prefix(from)?;
1258 let path = to.join(&tp);
1259
1260 let file_name = path.file_name();
1261 if file_name.is_none() {
1262 err!("No file name");
1263 }
1264 let file_name = file_name.unwrap();
1265 to.push(file_name);
1266
1267 let mut file_options = super::file::CopyOptions {
1268 overwrite: options.overwrite,
1269 skip_exist: options.skip_exist,
1270 buffer_size: options.buffer_size,
1271 };
1272
1273 if let Some(file_name) = file_name.to_str() {
1274 info_process.file_name = file_name.to_string();
1275 } else {
1276 err!("Invalid file name", ErrorKind::InvalidFileName);
1277 }
1278
1279 info_process.file_bytes_copied = 0;
1280 info_process.file_total_bytes = Path::new(&file).metadata()?.len();
1281
1282 let mut result_copy: Result<u64>;
1283 let mut work = true;
1284 let copied_bytes = result;
1285 while work {
1286 {
1287 let _progress_handler = |info: super::file::TransitProcess| {
1288 info_process.copied_bytes = copied_bytes + info.copied_bytes;
1289 info_process.file_bytes_copied = info.copied_bytes;
1290 progress_handler(info_process.clone());
1291 };
1292
1293 result_copy = super::file::move_file_with_progress(
1294 &file,
1295 &path,
1296 &file_options,
1297 _progress_handler,
1298 );
1299 }
1300 match result_copy {
1301 Ok(val) => {
1302 result += val;
1303 work = false;
1304 }
1305 Err(err) => match err.kind {
1306 ErrorKind::AlreadyExists => {
1307 let mut info_process = info_process.clone();
1308 info_process.state = TransitState::Exists;
1309 let user_decide = progress_handler(info_process);
1310 match user_decide {
1311 TransitProcessResult::Overwrite => {
1312 file_options.overwrite = true;
1313 }
1314 TransitProcessResult::OverwriteAll => {
1315 file_options.overwrite = true;
1316 options.overwrite = true;
1317 }
1318 TransitProcessResult::Skip => {
1319 is_remove = false;
1320 file_options.skip_exist = true;
1321 }
1322 TransitProcessResult::SkipAll => {
1323 is_remove = false;
1324 file_options.skip_exist = true;
1325 options.skip_exist = true;
1326 }
1327 TransitProcessResult::Retry => {}
1328 TransitProcessResult::ContinueOrAbort => {
1329 let err_msg = err.to_string();
1330 err!(err_msg.as_str(), err.kind)
1331 }
1332 TransitProcessResult::Abort => {
1333 let err_msg = err.to_string();
1334 err!(err_msg.as_str(), err.kind)
1335 }
1336 }
1337 }
1338 ErrorKind::PermissionDenied => {
1339 let mut info_process = info_process.clone();
1340 info_process.state = TransitState::Exists;
1341 let user_decide = progress_handler(info_process);
1342 match user_decide {
1343 TransitProcessResult::Overwrite => {
1344 err!("Overwrite denied for this situation!", ErrorKind::Other);
1345 }
1346 TransitProcessResult::OverwriteAll => {
1347 err!("Overwrite denied for this situation!", ErrorKind::Other);
1348 }
1349 TransitProcessResult::Skip => {
1350 is_remove = false;
1351 file_options.skip_exist = true;
1352 }
1353 TransitProcessResult::SkipAll => {
1354 file_options.skip_exist = true;
1355 options.skip_exist = true;
1356 }
1357 TransitProcessResult::Retry => {}
1358 TransitProcessResult::ContinueOrAbort => {
1359 let err_msg = err.to_string();
1360 err!(err_msg.as_str(), err.kind)
1361 }
1362 TransitProcessResult::Abort => {
1363 let err_msg = err.to_string();
1364 err!(err_msg.as_str(), err.kind)
1365 }
1366 }
1367 }
1368 _ => {
1369 let err_msg = err.to_string();
1370 err!(err_msg.as_str(), err.kind)
1371 }
1372 },
1373 }
1374 }
1375 }
1376 if is_remove {
1377 remove(from)?;
1378 }
1379
1380 Ok(result)
1381}
1382
1383pub fn remove<P: AsRef<Path>>(path: P) -> Result<()> {
1393 if path.as_ref().exists() {
1394 Ok(remove_dir_all(path)?)
1395 } else {
1396 Ok(())
1397 }
1398}