@@ -91,6 +91,17 @@ fn handle_progress_state(
91
91
}
92
92
}
93
93
94
+ fn get_directory_name ( file_name : & str ) -> & str {
95
+ const SUPPORTED_EXTENSIONS : [ & str ; 4 ] = [ ".tar.gz" , ".tgz" , ".tar" , ".zip" ] ;
96
+
97
+ for ext in & SUPPORTED_EXTENSIONS {
98
+ if file_name. ends_with ( ext) {
99
+ return & file_name[ ..file_name. len ( ) - ext. len ( ) ] ;
100
+ }
101
+ }
102
+ file_name
103
+ }
104
+
94
105
#[ derive( Clone , Copy , Debug , Eq , Hash , PartialEq ) ]
95
106
pub enum ReplaceResult {
96
107
Replace ( bool ) ,
@@ -319,37 +330,51 @@ async fn copy_or_move(
319
330
320
331
fn copy_unique_path ( from : & Path , to : & Path ) -> PathBuf {
321
332
let mut to = to. to_owned ( ) ;
322
- // Separate the full file name into its file name plus extension.
323
- // `[Path::file_stem]` returns the full name for dotfiles (e.g.
324
- // .someconf is the file name)
325
- if let ( Some ( stem) , ext) = (
326
- // FIXME: Replace `[Path::file_stem]` with `[Path::file_prefix]` when stablized to handle .tar.gz et al. better
327
- from. file_stem ( ) . and_then ( |name| name. to_str ( ) ) ,
328
- from. extension ( )
329
- . and_then ( |ext| ext. to_str ( ) )
330
- . unwrap_or_default ( ) ,
331
- ) {
332
- // '.' needs to be re-added for paths with extensions.
333
- let dot = if ext. is_empty ( ) { "" } else { "." } ;
333
+ if let Some ( file_name) = from. file_name ( ) . and_then ( |name| name. to_str ( ) ) {
334
+ let is_dir = from. is_dir ( ) ;
335
+ let ( stem, ext) = if !is_dir {
336
+ match from. extension ( ) . and_then ( |e| e. to_str ( ) ) {
337
+ Some ( ext) => {
338
+ let stem = from
339
+ . file_stem ( )
340
+ . and_then ( |s| s. to_str ( ) )
341
+ . unwrap_or ( file_name) ;
342
+ ( stem. to_string ( ) , Some ( ext. to_string ( ) ) )
343
+ }
344
+ None => ( file_name. to_string ( ) , None ) ,
345
+ }
346
+ } else {
347
+ ( file_name. to_string ( ) , None )
348
+ } ;
349
+
334
350
let mut n = 0u32 ;
335
- // Loop until a valid `copy n` variant is found
336
351
loop {
337
- n = if let Some ( n ) = n . checked_add ( 1 ) {
338
- n
352
+ let new_name = if n == 0 {
353
+ file_name . to_string ( )
339
354
} else {
340
- // TODO: Return error? fs_extra will handle it anyway
341
- break to;
355
+ if is_dir {
356
+ format ! ( "{} ({} {})" , file_name, fl!( "copy_noun" ) , n)
357
+ } else {
358
+ match & ext {
359
+ Some ( ext) => format ! ( "{} ({} {}).{}" , stem, fl!( "copy_noun" ) , n, ext) ,
360
+ None => format ! ( "{} ({} {})" , stem, fl!( "copy_noun" ) , n) ,
361
+ }
362
+ }
342
363
} ;
343
364
344
- // Rebuild file name
345
- let new_name = format ! ( "{stem} ({} {n}){dot}{ext}" , fl!( "copy_noun" ) ) ;
346
365
to = to. join ( new_name) ;
347
366
348
367
if !matches ! ( to. try_exists( ) , Ok ( true ) ) {
349
368
break to;
350
369
}
351
370
// Continue if a copy with index exists
352
371
to. pop ( ) ;
372
+
373
+ n = if let Some ( n) = n. checked_add ( 1 ) {
374
+ n
375
+ } else {
376
+ break to;
377
+ } ;
353
378
}
354
379
} else {
355
380
to
@@ -683,12 +708,10 @@ impl Operation {
683
708
684
709
let to = to. to_owned ( ) ;
685
710
686
- if let Some ( file_stem) = path. file_stem ( ) {
687
- let mut new_dir = to. join ( file_stem) ;
688
- // Make sure all extension parts are removed (file_stem may still contain them)
689
- while new_dir. extension ( ) . is_some ( ) {
690
- new_dir. set_extension ( "" ) ;
691
- }
711
+ if let Some ( file_name) = path. file_name ( ) . and_then ( |f| f. to_str ( ) ) {
712
+ let dir_name = get_directory_name ( file_name) ;
713
+ let mut new_dir = to. join ( dir_name) ;
714
+
692
715
if new_dir. exists ( ) {
693
716
if let Some ( new_dir_parent) = new_dir. parent ( ) {
694
717
new_dir = copy_unique_path ( & new_dir, new_dir_parent) ;
@@ -702,19 +725,19 @@ impl Operation {
702
725
. map ( io:: BufReader :: new)
703
726
. map ( flate2:: read:: GzDecoder :: new)
704
727
. map ( tar:: Archive :: new)
705
- . and_then ( |mut archive| archive. unpack ( new_dir) )
728
+ . and_then ( |mut archive| archive. unpack ( & new_dir) )
706
729
. map_err ( err_str) ?
707
730
}
708
731
"application/x-tar" => fs:: File :: open ( path)
709
732
. map ( io:: BufReader :: new)
710
733
. map ( tar:: Archive :: new)
711
- . and_then ( |mut archive| archive. unpack ( new_dir) )
734
+ . and_then ( |mut archive| archive. unpack ( & new_dir) )
712
735
. map_err ( err_str) ?,
713
736
"application/zip" => fs:: File :: open ( path)
714
737
. map ( io:: BufReader :: new)
715
738
. map ( zip:: ZipArchive :: new)
716
739
. map_err ( err_str) ?
717
- . and_then ( |mut archive| archive. extract ( new_dir) )
740
+ . and_then ( |mut archive| archive. extract ( & new_dir) )
718
741
. map_err ( err_str) ?,
719
742
#[ cfg( feature = "bzip2" ) ]
720
743
"application/x-bzip" | "application/x-bzip-compressed-tar" => {
0 commit comments