11use std:: io:: { Error , Result , ErrorKind } ;
2- use std:: path:: { PathBuf } ;
2+ use std:: path:: PathBuf ;
33use std:: fs;
44
55/// Recursively copies the contents of the directory `src` to the directory `dst`.
6- /// Analogous to `cp -rf src/* dst`.
6+ /// Analogous to `cp -rf src/* dst` and Python's `shutil.copytree`
77pub ( crate ) fn copy_dir_all < S : Into < PathBuf > , D : Into < PathBuf > > ( src : S , dst : D ) -> Result < ( ) > {
88 copy_dir_mono ( src. into ( ) , dst. into ( ) )
99}
1010
11- /// Monomorphized version of `copy_dir`
11+ /// Helper for `copy_dir`
1212fn copy_dir_mono ( src : PathBuf , dst : PathBuf ) -> Result < ( ) > {
1313 let mut dirs: Vec < ( PathBuf , PathBuf ) > = Vec :: default ( ) ;
1414
@@ -31,7 +31,7 @@ fn copy_dir_mono(src: PathBuf, dst: PathBuf) -> Result<()> {
3131 for entry in src. read_dir ( ) ? {
3232 let entry = entry?;
3333 let file_type = entry. file_type ( ) ?;
34- let src_filename = dbg ! ( entry. file_name( ) ) ;
34+ let src_filename = entry. file_name ( ) ;
3535 let src = src. join ( & src_filename) ;
3636 let dst = dst. join ( & src_filename) ;
3737 if file_type. is_dir ( ) {
@@ -49,20 +49,92 @@ fn copy_dir_mono(src: PathBuf, dst: PathBuf) -> Result<()> {
4949mod tests {
5050 use super :: copy_dir_all;
5151 use tempfile:: TempDir ;
52- // use std::fs::{write, read_to_string};
53- use std:: io:: { Result } ;
52+ use std:: io:: Result ;
53+ use std:: path:: Path ;
54+ use std:: fs;
5455
55- #[ test]
56- fn empty_dir ( ) -> Result < ( ) > {
57- let src = TempDir :: new ( ) ?;
58- let dst = TempDir :: new ( ) ?;
56+ fn create_paths ( root : & Path , paths : & [ & str ] ) -> Result < ( ) > {
57+ for path in paths {
58+ let is_directory = path. ends_with ( "/" ) ;
59+ let path = root. join ( path) ;
60+ if is_directory {
61+ fs:: create_dir_all ( & path) ?;
62+ } else {
63+ fs:: create_dir_all ( path. parent ( ) . unwrap ( ) ) ?;
64+ fs:: write ( & path, "" ) ?;
65+ }
66+ }
67+
68+ Ok ( ( ) )
69+ }
70+
71+ fn verify_paths ( root : & Path , paths : & [ & str ] ) {
72+ for path in paths {
73+ let should_create_directory = path. ends_with ( "/" ) ;
74+ let path = root. join ( path) ;
75+ if should_create_directory {
76+ assert ! ( path. is_dir( ) , "expected {path:?} to be directory" ) ;
77+ } else {
78+ assert ! ( path. is_file( ) , "expected {path:?} to be a file" ) ;
79+ }
80+ }
81+ }
82+
83+ fn run_test ( paths : & [ & str ] ) -> Result < ( ) > {
84+ let src = TempDir :: with_prefix ( "src" ) ?;
5985 let src = src. path ( ) ;
86+ let dst = TempDir :: with_prefix ( "dst" ) ?;
6087 let dst = dst. path ( ) ;
88+ create_paths ( src, paths) ?;
89+ verify_paths ( src, paths) ;
6190 copy_dir_all ( src, dst) ?;
62- let mut dst = dst . read_dir ( ) ? ;
63- assert ! ( dst. next ( ) . is_none ( ) , "we copied nothing into the destination" ) ;
91+ verify_paths ( src , paths ) ;
92+ verify_paths ( dst, paths ) ;
6493 Ok ( ( ) )
6594 }
6695
96+ #[ test]
97+ fn empty_dir ( ) -> Result < ( ) > {
98+ run_test ( & [ ] )
99+ }
100+
101+ #[ test]
102+ fn one_file ( ) -> Result < ( ) > {
103+ run_test ( & [ "a" ] )
104+ }
105+
106+ #[ test]
107+ fn directory_no_files ( ) -> Result < ( ) > {
108+ run_test ( & [ "a/" ] )
109+ }
110+
111+ #[ test]
112+ fn one_file_directory ( ) -> Result < ( ) > {
113+ run_test ( & [ "a" , "b/c" ] )
114+ }
115+
116+ #[ test]
117+ fn nested_directory ( ) -> Result < ( ) > {
118+ run_test ( & [ "b/c/d/e/f" ] )
119+ }
120+
121+ #[ test]
122+ fn two_directory ( ) -> Result < ( ) > {
123+ run_test ( & [ "a/a" , "b/b" ] )
124+ }
125+
126+ #[ test]
127+ fn two_directory2 ( ) -> Result < ( ) > {
128+ run_test ( & [ "a/b" , "b/a" ] )
129+ }
67130
131+ #[ test]
132+ fn directory_with_multiple_files ( ) -> Result < ( ) > {
133+ run_test ( & [ "a/a" , "a/b" , "a/c" ] )
134+ }
135+
136+ #[ test]
137+ fn multiple_directories_with_multiple_files ( ) -> Result < ( ) > {
138+ run_test ( & [ "d/" , "a/a" , "a/b/" , "a/c" , "a/b/c/d" , "b" ] )
139+ }
68140}
0 commit comments