Skip to content

Commit 40655bd

Browse files
committed
refactor logic to handle crate-deletes
1 parent d273245 commit 40655bd

File tree

4 files changed

+78
-94
lines changed

4 files changed

+78
-94
lines changed

src/index.rs

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
use super::CrateVersion;
2-
use serde_json;
1+
use super::{Change, CrateVersion};
32
use std::path::Path;
43

54
use git2::{
6-
build::RepoBuilder, Delta, DiffFormat, Error as GitError, ErrorClass, Object, ObjectType, Oid,
5+
build::RepoBuilder, Delta, Error as GitError, ErrorClass, Object, ObjectType, Oid,
76
Reference, Repository, Tree,
87
};
98
use std::str;
@@ -147,11 +146,11 @@ impl Index {
147146
}
148147

149148
/// As `peek_changes_with_options`, but without the options.
150-
pub fn peek_changes(&self) -> Result<(Vec<CrateVersion>, git2::Oid), GitError> {
149+
pub fn peek_changes(&self) -> Result<(Vec<Change>, git2::Oid), GitError> {
151150
self.peek_changes_with_options(None)
152151
}
153152

154-
/// Return all `CrateVersion`s that are observed between the last time `fetch_changes(…)` was called
153+
/// Return all `Change`s that are observed between the last time `fetch_changes(…)` was called
155154
/// and the latest state of the `crates.io` index repository, which is obtained by fetching
156155
/// the remote called `origin`.
157156
/// The `last_seen_reference()` will not be created or updated.
@@ -169,7 +168,7 @@ impl Index {
169168
pub fn peek_changes_with_options(
170169
&self,
171170
options: Option<&mut git2::FetchOptions<'_>>,
172-
) -> Result<(Vec<CrateVersion>, git2::Oid), GitError> {
171+
) -> Result<(Vec<Change>, git2::Oid), GitError> {
173172
let from = self
174173
.last_seen_reference()
175174
.and_then(|r| {
@@ -201,11 +200,11 @@ impl Index {
201200
}
202201

203202
/// As `fetch_changes_with_options`, but without the options.
204-
pub fn fetch_changes(&self) -> Result<Vec<CrateVersion>, GitError> {
203+
pub fn fetch_changes(&self) -> Result<Vec<Change>, GitError> {
205204
self.fetch_changes_with_options(None)
206205
}
207206

208-
/// Return all `CrateVersion`s that are observed between the last time this method was called
207+
/// Return all `Change`s that are observed between the last time this method was called
209208
/// and the latest state of the `crates.io` index repository, which is obtained by fetching
210209
/// the remote called `origin`.
211210
/// The `last_seen_reference()` will be created or adjusted to point to the latest fetched
@@ -221,7 +220,7 @@ impl Index {
221220
pub fn fetch_changes_with_options(
222221
&self,
223222
options: Option<&mut git2::FetchOptions<'_>>,
224-
) -> Result<Vec<CrateVersion>, GitError> {
223+
) -> Result<Vec<Change>, GitError> {
225224
let (changes, to) = self.peek_changes_with_options(options)?;
226225
self.set_last_seen_reference(to)?;
227226
Ok(changes)
@@ -253,7 +252,7 @@ impl Index {
253252
&self,
254253
from: impl AsRef<str>,
255254
to: impl AsRef<str>,
256-
) -> Result<Vec<CrateVersion>, GitError> {
255+
) -> Result<Vec<Change>, GitError> {
257256
self.changes_from_objects(
258257
&self.repo.revparse_single(from.as_ref())?,
259258
&self.repo.revparse_single(to.as_ref())?,
@@ -266,7 +265,7 @@ impl Index {
266265
&self,
267266
from: &Object,
268267
to: &Object,
269-
) -> Result<Vec<CrateVersion>, GitError> {
268+
) -> Result<Vec<Change>, GitError> {
270269
fn into_tree<'a>(repo: &'a Repository, obj: &Object) -> Result<Tree<'a>, GitError> {
271270
repo.find_tree(match obj.kind() {
272271
Some(ObjectType::Commit) => obj
@@ -285,24 +284,41 @@ impl Index {
285284
Some(&into_tree(&self.repo, to)?),
286285
None,
287286
)?;
288-
let mut res: Vec<CrateVersion> = Vec::new();
289-
diff.print(DiffFormat::Patch, |delta, _, diffline| {
290-
if diffline.origin() != LINE_ADDED_INDICATOR {
291-
return true;
292-
}
287+
let mut changes: Vec<Change> = Vec::new();
288+
let mut deletes: Vec<String> = Vec::new();
289+
diff.foreach(
290+
&mut |delta, _| {
291+
if delta.status() == Delta::Deleted {
292+
if let Some(path) = delta.new_file().path() {
293+
if let Some(file_name) = path.file_name() {
294+
deletes.push(file_name.to_string_lossy().to_string());
295+
}
296+
}
297+
}
298+
true
299+
},
300+
None,
301+
None,
302+
Some(&mut |delta, _hunk, diffline| {
303+
if diffline.origin() != LINE_ADDED_INDICATOR {
304+
return true;
305+
}
306+
if !matches!(delta.status(), Delta::Added | Delta::Modified) {
307+
return true;
308+
}
293309

294-
if !match delta.status() {
295-
Delta::Added | Delta::Modified => true,
296-
_ => false,
297-
} {
298-
return true;
299-
}
310+
if let Ok(crate_version) = serde_json::from_slice::<CrateVersion>(diffline.content()) {
311+
if crate_version.yanked {
312+
changes.push(Change::Yanked(crate_version));
313+
} else {
314+
changes.push(Change::Added(crate_version));
315+
}
316+
}
317+
true
318+
}),
319+
)?;
300320

301-
if let Ok(c) = serde_json::from_slice(diffline.content()) {
302-
res.push(c)
303-
}
304-
true
305-
})
306-
.map(|_| res)
321+
changes.extend(deletes.iter().map(|krate| Change::Deleted(krate.clone())));
322+
Ok(changes)
307323
}
308324
}

src/version.rs

Lines changed: 13 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,27 @@
1-
use serde::de::{Deserialize, Deserializer};
2-
use serde::ser::{Serialize, Serializer};
31
use std::collections::HashMap;
42

53
use std::fmt;
64

75
/// Identify a kind of change that occurred to a crate
8-
#[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Debug)]
9-
pub enum ChangeKind {
10-
/// A crate version was added
11-
Added,
6+
#[derive(Clone, Eq, PartialEq, Debug)]
7+
pub enum Change {
128
/// A crate version was added or it was unyanked.
13-
Yanked,
9+
Added(CrateVersion),
10+
/// A crate version was yanked.
11+
Yanked(CrateVersion),
12+
/// A crate was deleted
13+
Deleted(String),
1414
}
1515

16-
impl Default for ChangeKind {
17-
fn default() -> Self {
18-
ChangeKind::Added
19-
}
20-
}
21-
22-
impl<'de> Deserialize<'de> for ChangeKind {
23-
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
24-
where
25-
D: Deserializer<'de>,
26-
{
27-
struct Visitor;
28-
impl<'de> ::serde::de::Visitor<'de> for Visitor {
29-
type Value = ChangeKind;
30-
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
31-
formatter.write_str("boolean")
32-
}
33-
fn visit_bool<E>(self, value: bool) -> Result<ChangeKind, E>
34-
where
35-
E: ::serde::de::Error,
36-
{
37-
if value {
38-
Ok(ChangeKind::Yanked)
39-
} else {
40-
Ok(ChangeKind::Added)
41-
}
42-
}
43-
}
44-
deserializer.deserialize_bool(Visitor)
45-
}
46-
}
47-
48-
impl Serialize for ChangeKind {
49-
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
50-
where
51-
S: Serializer,
52-
{
53-
serializer.serialize_bool(self == &ChangeKind::Yanked)
54-
}
55-
}
56-
57-
impl fmt::Display for ChangeKind {
16+
impl fmt::Display for Change {
5817
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5918
write!(
6019
f,
6120
"{}",
6221
match *self {
63-
ChangeKind::Added => "added",
64-
ChangeKind::Yanked => "yanked",
22+
Change::Added(_) => "added",
23+
Change::Yanked(_) => "yanked",
24+
Change::Deleted(_) => "deleted",
6525
}
6626
)
6727
}
@@ -72,9 +32,8 @@ impl fmt::Display for ChangeKind {
7232
pub struct CrateVersion {
7333
/// The crate name, i.e. `clap`.
7434
pub name: String,
75-
/// The kind of change.
76-
#[serde(rename = "yanked")]
77-
pub kind: ChangeKind,
35+
/// is the release yanked?
36+
pub yanked: bool,
7837
/// The semantic version of the crate.
7938
#[serde(rename = "vers")]
8039
pub version: String,

tests/index.rs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const NUM_VERSIONS_AT_RECENT_COMMIT: usize = 39752;
88
const REV_ONE_ADDED: &'static str = "615c9c41942a3ba13e088fbcb1470c61b169a187";
99
const REV_ONE_YANKED: &'static str = "8cf8fbad7876586ced34c4b778f6a80fadd2a59b";
1010
const REV_ONE_UNYANKED: &'static str = "f8cb00181";
11+
const REV_CRATE_DELETE: &str = "de5be3e8bb6cd7a3179857bdbdf28ca4fa23f84c";
1112

1213
#[test]
1314
#[ignore] // This test takes too long for my taste, this library is stable by now
@@ -106,12 +107,20 @@ fn peek_changes_since_last_fetch() {
106107
);
107108
}
108109

109-
fn changes_of(index: &Index, commit: &str) -> Vec<CrateVersion> {
110+
fn changes_of(index: &Index, commit: &str) -> Vec<Change> {
110111
index
111-
.changes(format!("{}~1^{{tree}}", commit), format!("{}", commit))
112+
.changes(format!("{}~1^{{tree}}", commit), commit)
112113
.expect("id to be valid and diff OK")
113114
}
114115

116+
#[test]
117+
fn crate_delete() {
118+
let (index, _tmp) = make_index();
119+
120+
let changes = changes_of(&index, REV_CRATE_DELETE);
121+
assert_eq!(changes, vec![Change::Deleted("rustdecimal".to_string())],);
122+
}
123+
115124
#[test]
116125
#[ignore]
117126
fn quick_traverse_unyanked_crates() {
@@ -121,9 +130,9 @@ fn quick_traverse_unyanked_crates() {
121130
let crates = changes_of(&index, REV_ONE_UNYANKED);
122131
assert_eq!(
123132
crates,
124-
vec![CrateVersion {
133+
vec![Change::Added(CrateVersion {
125134
name: "gfx_text".to_owned(),
126-
kind: ChangeKind::Added,
135+
yanked: false,
127136
version: "0.13.2".to_owned(),
128137
dependencies: vec![
129138
Dependency {
@@ -174,7 +183,7 @@ fn quick_traverse_unyanked_crates() {
174183
h
175184
},
176185
checksum: "d0b1240e3627e646f69685ddd3e7d83dd3ff3d586afe83bf3679082028183f2d".into(),
177-
}]
186+
})]
178187
);
179188
}
180189

@@ -186,14 +195,14 @@ fn quick_traverse_yanked_crates() {
186195
let crates = changes_of(&index, REV_ONE_YANKED);
187196
assert_eq!(
188197
crates,
189-
vec![CrateVersion {
198+
vec![Change::Yanked(CrateVersion {
190199
name: "sha3".to_owned(),
191-
kind: ChangeKind::Yanked,
200+
yanked: true,
192201
version: "0.0.0".to_owned(),
193202
dependencies: Vec::new(),
194203
features: HashMap::new(),
195204
checksum: "dbba9d72d3d04e2167fb9c76ce22aed118eb003727bbe59774b9bf3603fa1f43".into(),
196-
}]
205+
})]
197206
);
198207
}
199208

@@ -207,9 +216,9 @@ fn quick_traverse_added_crates() {
207216
let crates = changes_of(&index, REV_ONE_ADDED);
208217
assert_eq!(
209218
crates,
210-
vec![CrateVersion {
219+
vec![Change::Added(CrateVersion {
211220
name: "rpwg".to_owned(),
212-
kind: ChangeKind::Added,
221+
yanked: false,
213222
version: "0.1.0".to_owned(),
214223
dependencies: vec![
215224
Dependency {
@@ -235,6 +244,6 @@ fn quick_traverse_added_crates() {
235244
],
236245
features: HashMap::new(),
237246
checksum: "14437a3702699dba0c49ddc401a0529898e83f8b769348549985a0f4d818d3ca".into(),
238-
}]
247+
})]
239248
);
240249
}

tests/version.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ fn test_parse_crate_version() {
1818
c,
1919
CrateVersion {
2020
name: "test".to_string(),
21-
kind: ChangeKind::Yanked,
21+
yanked: true,
2222
version: "1.0.0".to_string(),
2323
dependencies: Vec::new(),
2424
features: HashMap::new(),

0 commit comments

Comments
 (0)