1use crate::Platform;
6use crate::resolve::{
7 DependencyKind, IncludedDependencyReason, IncludedDependencyVersion, Reasons, Resolved,
8 SpecificCrateIdent,
9};
10use semver::Version;
11use serde::Serialize;
12use std::collections::{BTreeMap, BTreeSet};
13
14#[derive(Serialize, Debug)]
18pub struct Added<'a> {
19 pub ident: SpecificCrateIdent,
21 pub kind: DependencyKind,
22 pub has_build_rs: bool,
23 pub is_proc_macro: bool,
24 pub platforms: &'a BTreeSet<Platform>,
26 pub reasons: &'a Reasons,
28}
29
30#[derive(Serialize, Debug)]
33pub struct Comparison<'a> {
34 pub ident: SpecificCrateIdent,
36 pub kind: DependencyKind,
37 pub has_build_rs: bool,
38 pub is_proc_macro: bool,
39 pub platforms: &'a BTreeSet<Platform>,
41 pub reasons: &'a Reasons,
42
43 pub closest_different_old_version: Option<Version>,
46 pub all_other_old_versions: Vec<Version>,
49
50 pub added_in_platforms: BTreeMap<&'a Platform, Vec<&'a IncludedDependencyReason>>,
53 pub added_in_build: BTreeMap<&'a IncludedDependencyReason, &'a BTreeSet<Platform>>,
55 pub added_in_non_debug: BTreeMap<&'a IncludedDependencyReason, &'a BTreeSet<Platform>>,
58}
59
60impl Comparison<'_> {
61 fn requires_review(&self) -> bool {
62 self.closest_different_old_version.is_some()
63 || !self.added_in_platforms.is_empty()
64 || !self.added_in_build.is_empty()
65 || !self.added_in_non_debug.is_empty()
66 }
67}
68
69#[derive(Serialize, Debug)]
73pub struct Removed {
74 pub ident: SpecificCrateIdent,
76 pub remaining_versions: Vec<Version>,
78}
79
80#[derive(Serialize, Debug)]
82pub struct Diff<'a> {
83 pub added: Vec<Added<'a>>,
84 pub changed: Vec<Comparison<'a>>,
85 pub removed: Vec<Removed>,
86 pub filtered_added: Vec<SpecificCrateIdent>,
89 pub filtered_removed: Vec<SpecificCrateIdent>,
92}
93
94impl<'a> Diff<'a> {
95 fn compare(
96 name: &'a str,
97 old: &'a BTreeMap<Version, IncludedDependencyVersion>,
98 new_version: Version,
99 new: &'a IncludedDependencyVersion,
100 ) -> Comparison<'a> {
101 let (closest_old_version, closest_old_info) =
104 old.range(&new_version..).next().unwrap_or_else(|| {
105 old.last_key_value()
106 .expect("Higher ones were already checked, version set is never empty")
107 });
108
109 let closest_different_old_version =
110 (*closest_old_version != new_version).then(|| closest_old_version.clone());
111
112 let all_other_old_versions =
113 if let Some(ref already_mentioned) = closest_different_old_version {
114 old.keys()
115 .filter(|i| *i != already_mentioned)
116 .cloned()
117 .collect::<Vec<_>>()
118 } else {
119 Vec::new()
120 };
121
122 let added_in_platforms = new
123 .platforms
124 .iter()
125 .filter(|i| !closest_old_info.platforms.contains(i))
126 .map(|platform| {
127 let reasons = new
128 .reasons
129 .iter()
130 .filter(|(_, platforms)| platforms.contains(platform))
131 .map(|(reason, _)| reason)
132 .collect::<Vec<_>>();
133 (platform, reasons)
134 })
135 .collect();
136
137 let added_in_build = if new.kind.run_at_build && !closest_old_info.kind.run_at_build {
138 new.reasons
139 .iter()
140 .filter(|(reason, _)| reason.kind.run_at_build)
141 .collect()
142 } else {
143 BTreeMap::new()
144 };
145
146 let added_in_non_debug =
147 if !new.kind.only_debug_builds && closest_old_info.kind.only_debug_builds {
148 new.reasons
149 .iter()
150 .filter(|(reason, _)| !reason.kind.only_debug_builds)
151 .collect()
152 } else {
153 BTreeMap::new()
154 };
155
156 Comparison {
157 ident: SpecificCrateIdent {
158 name: name.to_owned(),
159 version: new_version,
160 },
161 kind: new.kind,
162 has_build_rs: new.has_build_rs,
163 is_proc_macro: new.is_proc_macro,
164 platforms: &new.platforms,
165 reasons: &new.reasons,
166
167 closest_different_old_version,
168 all_other_old_versions,
169
170 added_in_platforms,
171 added_in_build,
172 added_in_non_debug,
173 }
174 }
175
176 pub fn between(old: &'a Resolved, new: &'a Resolved) -> Self {
178 let added = new
179 .included
180 .iter()
181 .filter(|(name, _)| !old.included.contains_key(*name))
182 .flat_map(|(name, versions)| {
183 versions
184 .iter()
185 .map(move |(version, item)| (name, version, item))
186 })
187 .map(|(name, version, info)| Added {
188 ident: SpecificCrateIdent {
189 name: name.clone(),
190 version: version.clone(),
191 },
192 kind: info.kind,
193 has_build_rs: info.has_build_rs,
194 is_proc_macro: info.is_proc_macro,
195 platforms: &info.platforms,
196 reasons: &info.reasons,
197 })
198 .collect();
199
200 let changed = new
201 .included
202 .iter()
203 .filter_map(|(name, new_versions)| {
204 old.included
205 .get(name)
206 .map(|old_versions| (name, old_versions, new_versions))
207 })
208 .flat_map(|(name, old_versions, new_versions)| {
209 new_versions.iter().map(move |(new_version, new_info)| {
210 Self::compare(name, old_versions, new_version.clone(), new_info)
211 })
212 })
213 .filter(|comparison| comparison.requires_review())
214 .collect();
215
216 let removed = old
217 .included
218 .iter()
219 .filter_map(|(name, versions)| {
220 let new_versions = new.included.get(name);
221 let has_change = new_versions
222 .is_some_and(|new| new.keys().any(|key| !versions.contains_key(key)));
223 if has_change {
224 None
228 } else {
229 Some((name, versions, new_versions))
230 }
231 })
232 .flat_map(|(name, versions, new_versions)| {
233 let is_in_new = move |version: &Version| {
234 new_versions.is_some_and(|new| new.contains_key(version))
235 };
236 let remaining_versions = versions
237 .keys()
238 .filter(|version| is_in_new(version))
239 .cloned()
240 .collect::<Vec<_>>();
241 versions
242 .keys()
243 .filter(move |version| !is_in_new(version))
244 .map(move |version| Removed {
245 ident: SpecificCrateIdent {
246 name: name.clone(),
247 version: version.clone(),
248 },
249 remaining_versions: remaining_versions.clone(),
250 })
251 })
252 .collect();
253
254 let in_right_set = |left: &BTreeSet<SpecificCrateIdent>, right: &BTreeSet<_>| {
257 right
258 .iter()
259 .filter(|item| !left.contains(item))
260 .cloned()
261 .collect()
262 };
263
264 let filtered_added = in_right_set(&old.filtered, &new.filtered);
265 let filtered_removed = in_right_set(&old.filtered, &new.filtered);
266
267 Diff {
268 added,
269 changed,
270 removed,
271 filtered_added,
272 filtered_removed,
273 }
274 }
275}