77//!
88//! ```no_run
99//! extern crate dir_diff;
10- //!
10+ //!
1111//! assert!(dir_diff::is_different("dir/a", "dir/b").unwrap());
1212//! ```
1313
@@ -16,8 +16,9 @@ extern crate walkdir;
1616use std:: fs:: File ;
1717use std:: io:: prelude:: * ;
1818use std:: path:: Path ;
19+ use std:: cmp:: Ordering ;
1920
20- use walkdir:: WalkDir ;
21+ use walkdir:: { DirEntry , WalkDir } ;
2122
2223/// The various errors that can happen when diffing two directories
2324#[ derive( Debug ) ]
@@ -33,68 +34,37 @@ pub enum Error {
3334///
3435/// ```no_run
3536/// extern crate dir_diff;
36- ///
37+ ///
3738/// assert!(dir_diff::is_different("dir/a", "dir/b").unwrap());
3839/// ```
3940pub fn is_different < A : AsRef < Path > , B : AsRef < Path > > ( a_base : A , b_base : B ) -> Result < bool , Error > {
40- let a_base = a_base . as_ref ( ) ;
41- let b_base = b_base . as_ref ( ) ;
41+ let mut a_walker = walk_dir ( a_base ) ;
42+ let mut b_walker = walk_dir ( b_base ) ;
4243
43- // We start by checking the count in the top-level directories. There's two
44- // reasons for this: first, if a is empty, but b is not, our for loop will not
45- // catch it; it will execute zero times and return true, and that's wrong!
46- // Second, if they're differnet, there's no reason to bother comparing the
47- // files; we already know they're different.
44+ for ( a, b) in ( & mut a_walker) . zip ( & mut b_walker) {
45+ let a = a?;
46+ let b = b?;
4847
49- let a_count = std:: fs:: read_dir ( & a_base) ?. count ( ) ;
50- let b_count = std:: fs:: read_dir ( & b_base) ?. count ( ) ;
51-
52- if a_count != b_count {
53- return Ok ( true ) ;
48+ if a. depth ( ) != b. depth ( ) || a. file_type ( ) != b. file_type ( )
49+ || a. file_name ( ) != b. file_name ( )
50+ || ( a. file_type ( ) . is_file ( ) && read_to_vec ( a. path ( ) ) ? != read_to_vec ( b. path ( ) ) ?)
51+ {
52+ return Ok ( true ) ;
53+ }
5454 }
5555
56- Ok ( compare ( a_base , b_base ) ? || compare ( b_base , a_base ) ? )
56+ Ok ( !a_walker . next ( ) . is_none ( ) || !b_walker . next ( ) . is_none ( ) )
5757}
5858
59- fn compare ( a_base : & Path , b_base : & Path ) -> Result < bool , Error > {
60- // next, we walk all of the entries in a and compare them to b
61- for entry in WalkDir :: new ( a_base) {
62- let entry = entry?;
63- let a = entry. path ( ) ;
64-
65- // calculate just the part of the path relative to a
66- let no_prefix = a. strip_prefix ( a_base) ?;
67-
68- // and then join that with b to get the path in b
69- let b = b_base. join ( no_prefix) ;
70-
71- if a. is_dir ( ) {
72- if b. is_dir ( ) {
73- // can't compare the contents of directories, so just continue
74- continue ;
75- } else {
76- // if one is a file and one is a directory, we have a difference!
77- return Ok ( true )
78- }
79- }
80-
81- // file a is guaranteed to exist...
82- let a_text = read_to_vec ( a) ?;
83-
84- // but file b is not. If we have any kind of error when loading
85- // it up, that's a positive result, not an actual error.
86- let b_text = match read_to_vec ( b) {
87- Ok ( contents) => contents,
88- Err ( _) => return Ok ( true ) ,
89- } ;
90-
91- if a_text != b_text {
92- return Ok ( true ) ;
93- }
94- }
59+ fn walk_dir < P : AsRef < Path > > ( path : P ) -> std:: iter:: Skip < walkdir:: IntoIter > {
60+ WalkDir :: new ( path)
61+ . sort_by ( compare_by_file_name)
62+ . into_iter ( )
63+ . skip ( 1 )
64+ }
9565
96- // if we made it here, we're good!
97- Ok ( false )
66+ fn compare_by_file_name ( a : & DirEntry , b : & DirEntry ) -> Ordering {
67+ a . file_name ( ) . cmp ( b . file_name ( ) )
9868}
9969
10070fn read_to_vec < P : AsRef < Path > > ( file : P ) -> Result < Vec < u8 > , std:: io:: Error > {
@@ -122,4 +92,4 @@ impl From<walkdir::Error> for Error {
12292 fn from ( e : walkdir:: Error ) -> Error {
12393 Error :: WalkDir ( e)
12494 }
125- }
95+ }
0 commit comments