@@ -3,10 +3,14 @@ use crate::{Change, CrateVersion};
3
3
use ahash:: { AHashSet , RandomState } ;
4
4
use bstr:: BStr ;
5
5
use hashbrown:: raw:: RawTable ;
6
+ use std:: hash:: Hasher ;
7
+ use std:: ops:: Deref ;
6
8
7
9
#[ derive( Default ) ]
8
10
pub ( crate ) struct Delegate {
9
11
changes : Vec < Change > ,
12
+ /// All changes that happen within a file, along the line-number it happens in .
13
+ per_file_changes : Vec < ( usize , Change ) > ,
10
14
err : Option < Error > ,
11
15
}
12
16
@@ -65,8 +69,8 @@ impl Delegate {
65
69
if let Some ( diff) = change. event . diff ( ) . transpose ( ) ? {
66
70
let mut old_lines = AHashSet :: with_capacity ( 1024 ) ;
67
71
let location = change. location ;
68
- for line in diff. old . data . lines ( ) {
69
- old_lines. insert ( line) ;
72
+ for ( number , line) in diff. old . data . lines ( ) . enumerate ( ) {
73
+ old_lines. insert ( Line ( number , line) ) ;
70
74
}
71
75
72
76
// A RawTable is used to represent a Checksum -> CrateVersion map
@@ -75,47 +79,52 @@ impl Delegate {
75
79
let mut new_versions = RawTable :: with_capacity ( old_lines. len ( ) . min ( 1024 ) ) ;
76
80
let hasher = RandomState :: new ( ) ;
77
81
78
- for line in diff. new . data . lines ( ) {
82
+ for ( number , line) in diff. new . data . lines ( ) . enumerate ( ) {
79
83
// first quickly check if the exact same line is already present in this file in that case we don't need to do anything else
80
- if old_lines. remove ( line) {
84
+ if old_lines. remove ( & Line ( number , line) ) {
81
85
continue ;
82
86
}
83
87
// no need to check if the checksum already exists in the hashmap
84
- // as each checksum appear only once
88
+ // as each checksum appears only once
85
89
let new_version = version_from_json_line ( line, location) ?;
86
90
new_versions. insert (
87
91
hasher. hash_one ( new_version. checksum ) ,
88
- new_version,
89
- |rehashed| hasher. hash_one ( rehashed. checksum ) ,
92
+ ( number , new_version) ,
93
+ |rehashed| hasher. hash_one ( rehashed. 1 . checksum ) ,
90
94
) ;
91
95
}
92
96
93
97
for line in old_lines. drain ( ) {
94
- let old_version = version_from_json_line ( line, location) ?;
98
+ let old_version = version_from_json_line ( & line, location) ?;
95
99
let new_version = new_versions
96
100
. remove_entry ( hasher. hash_one ( old_version. checksum ) , |version| {
97
- version. checksum == old_version. checksum
101
+ version. 1 . checksum == old_version. checksum
98
102
} ) ;
99
103
match new_version {
100
- Some ( new_version) => {
104
+ Some ( ( _ , new_version) ) => {
101
105
let change = match ( old_version. yanked , new_version. yanked ) {
102
106
( true , false ) => Change :: Unyanked ( new_version) ,
103
107
( false , true ) => Change :: Yanked ( new_version) ,
104
108
_ => continue ,
105
109
} ;
106
- self . changes . push ( change)
110
+ self . per_file_changes . push ( ( line . 0 , change) )
107
111
}
108
- None => self . changes . push ( Change :: VersionDeleted ( old_version) ) ,
112
+ None => self
113
+ . per_file_changes
114
+ . push ( ( line. 0 , Change :: VersionDeleted ( old_version) ) ) ,
109
115
}
110
116
}
111
- for version in new_versions. drain ( ) {
117
+ for ( number , version) in new_versions. drain ( ) {
112
118
let change = if version. yanked {
113
119
Change :: AddedAndYanked ( version)
114
120
} else {
115
121
Change :: Added ( version)
116
122
} ;
117
- self . changes . push ( change) ;
123
+ self . per_file_changes . push ( ( number , change) ) ;
118
124
}
125
+ self . per_file_changes . sort_by_key ( |t| t. 0 ) ;
126
+ self . changes
127
+ . extend ( self . per_file_changes . drain ( ..) . map ( |t| t. 1 ) ) ;
119
128
}
120
129
}
121
130
}
@@ -130,6 +139,32 @@ impl Delegate {
130
139
}
131
140
}
132
141
142
+ /// A line that assumes there never are equal lines within a file which
143
+ /// is the case due to the checksum.
144
+ struct Line < ' a > ( usize , & ' a [ u8 ] ) ;
145
+
146
+ impl std:: hash:: Hash for Line < ' _ > {
147
+ fn hash < H : Hasher > ( & self , state : & mut H ) {
148
+ self . 1 . hash ( state)
149
+ }
150
+ }
151
+
152
+ impl PartialEq < Self > for Line < ' _ > {
153
+ fn eq ( & self , other : & Self ) -> bool {
154
+ self . 1 . eq ( other. 1 )
155
+ }
156
+ }
157
+
158
+ impl Eq for Line < ' _ > { }
159
+
160
+ impl < ' a > Deref for Line < ' a > {
161
+ type Target = [ u8 ] ;
162
+
163
+ fn deref ( & self ) -> & Self :: Target {
164
+ self . 1
165
+ }
166
+ }
167
+
133
168
fn version_from_json_line ( line : & [ u8 ] , file_name : & BStr ) -> Result < CrateVersion , Error > {
134
169
serde_json:: from_slice ( line) . map_err ( |err| Error :: VersionDecode {
135
170
source : err,
0 commit comments