@@ -14,18 +14,31 @@ use crate::util::toml_mut::manifest::LocalManifest;
14
14
use crate :: util:: toml_mut:: upgrade:: upgrade_requirement;
15
15
use crate :: util:: { style, OptVersionReq } ;
16
16
use crate :: util:: { CargoResult , VersionExt } ;
17
+ use anyhow:: Context as _;
17
18
use itertools:: Itertools ;
18
19
use semver:: { Op , Version , VersionReq } ;
19
20
use std:: cmp:: Ordering ;
20
21
use std:: collections:: { BTreeMap , HashMap , HashSet } ;
21
22
use tracing:: { debug, trace} ;
22
23
23
- pub type UpgradeMap = HashMap < ( String , SourceId ) , Version > ;
24
+ /// A map of all breaking upgrades which is filled in by
25
+ /// upgrade_manifests/upgrade_dependency when going through workspace member
26
+ /// manifests, and later used by write_manifest_upgrades in order to know which
27
+ /// upgrades to write to manifest files on disk. Also used by update_lockfile to
28
+ /// know which dependencies to keep unchanged if any have been upgraded (we will
29
+ /// do either breaking or non-breaking updates, but not both).
30
+ pub type UpgradeMap = HashMap <
31
+ // The key is a tuple of the package name and the source id of the package.
32
+ ( String , SourceId ) ,
33
+ // The value is the upgraded version of the package.
34
+ Version ,
35
+ > ;
24
36
25
37
pub struct UpdateOptions < ' a > {
26
38
pub gctx : & ' a GlobalContext ,
27
39
pub to_update : Vec < String > ,
28
40
pub precise : Option < & ' a str > ,
41
+ pub breaking : bool ,
29
42
pub recursive : bool ,
30
43
pub dry_run : bool ,
31
44
pub workspace : bool ,
@@ -49,7 +62,11 @@ pub fn generate_lockfile(ws: &Workspace<'_>) -> CargoResult<()> {
49
62
Ok ( ( ) )
50
63
}
51
64
52
- pub fn update_lockfile ( ws : & Workspace < ' _ > , opts : & UpdateOptions < ' _ > ) -> CargoResult < ( ) > {
65
+ pub fn update_lockfile (
66
+ ws : & Workspace < ' _ > ,
67
+ opts : & UpdateOptions < ' _ > ,
68
+ upgrades : & UpgradeMap ,
69
+ ) -> CargoResult < ( ) > {
53
70
if opts. recursive && opts. precise . is_some ( ) {
54
71
anyhow:: bail!( "cannot specify both recursive and precise simultaneously" )
55
72
}
@@ -165,7 +182,15 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
165
182
. filter ( |s| !s. is_registry ( ) )
166
183
. collect ( ) ;
167
184
168
- let keep = |p : & PackageId | !to_avoid_sources. contains ( & p. source_id ( ) ) && !to_avoid. contains ( p) ;
185
+ let breaking_precise_upgrades = opts. precise . is_some ( ) && !upgrades. is_empty ( ) ;
186
+ let breaking_mode = opts. breaking || breaking_precise_upgrades;
187
+
188
+ let keep = |p : & PackageId | {
189
+ ( !to_avoid_sources. contains ( & p. source_id ( ) ) && !to_avoid. contains ( p) )
190
+ // In case of `--breaking` or precise upgrades, we want to keep all
191
+ // packages unchanged that didn't get upgraded.
192
+ || ( breaking_mode && !upgrades. contains_key ( & ( p. name ( ) . to_string ( ) , p. source_id ( ) ) ) )
193
+ } ;
169
194
170
195
let mut resolve = ops:: resolve_with_previous (
171
196
& mut registry,
@@ -185,11 +210,7 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
185
210
opts. precise . is_some ( ) ,
186
211
& mut registry,
187
212
) ?;
188
- if opts. dry_run {
189
- opts. gctx
190
- . shell ( )
191
- . warn ( "not updating lockfile due to dry run" ) ?;
192
- } else {
213
+ if !opts. dry_run {
193
214
ops:: write_pkg_lockfile ( ws, & mut resolve) ?;
194
215
}
195
216
Ok ( ( ) )
@@ -217,6 +238,7 @@ pub fn print_lockfile_changes(
217
238
pub fn upgrade_manifests (
218
239
ws : & mut Workspace < ' _ > ,
219
240
to_update : & Vec < String > ,
241
+ precise : & Option < & str > ,
220
242
) -> CargoResult < UpgradeMap > {
221
243
let gctx = ws. gctx ( ) ;
222
244
let mut upgrades = HashMap :: new ( ) ;
@@ -245,6 +267,7 @@ pub fn upgrade_manifests(
245
267
upgrade_dependency (
246
268
& gctx,
247
269
& to_update,
270
+ precise,
248
271
& mut registry,
249
272
& mut upgrades,
250
273
& mut upgrade_messages,
@@ -259,6 +282,7 @@ pub fn upgrade_manifests(
259
282
fn upgrade_dependency (
260
283
gctx : & GlobalContext ,
261
284
to_update : & Vec < PackageIdSpec > ,
285
+ precise : & Option < & str > ,
262
286
registry : & mut PackageRegistry < ' _ > ,
263
287
upgrades : & mut UpgradeMap ,
264
288
upgrade_messages : & mut HashSet < String > ,
@@ -316,7 +340,7 @@ fn upgrade_dependency(
316
340
let query =
317
341
crate :: core:: dependency:: Dependency :: parse ( name, None , dependency. source_id ( ) . clone ( ) ) ?;
318
342
319
- let possibilities = {
343
+ let possibilities = if precise . is_none ( ) {
320
344
loop {
321
345
match registry. query_vec ( & query, QueryKind :: Exact ) {
322
346
std:: task:: Poll :: Ready ( res) => {
@@ -325,6 +349,8 @@ fn upgrade_dependency(
325
349
std:: task:: Poll :: Pending => registry. block_until_ready ( ) ?,
326
350
}
327
351
}
352
+ } else {
353
+ Vec :: new ( )
328
354
} ;
329
355
330
356
let latest = if !possibilities. is_empty ( ) {
@@ -338,32 +364,60 @@ fn upgrade_dependency(
338
364
None
339
365
} ;
340
366
341
- let Some ( latest) = latest else {
367
+ let new_version = if let Some ( precise) = precise {
368
+ Version :: parse ( precise)
369
+ . with_context ( || format ! ( "invalid version format for precise version `{precise}`" ) ) ?
370
+ } else if let Some ( latest) = latest {
371
+ latest. clone ( )
372
+ } else {
373
+ // Breaking (not precise) upgrade did not find a latest version
342
374
trace ! ( "skipping dependency `{name}` without any published versions" ) ;
343
375
return Ok ( dependency) ;
344
376
} ;
345
377
346
- if current. matches ( & latest ) {
378
+ if current. matches ( & new_version ) {
347
379
trace ! ( "skipping dependency `{name}` without a breaking update available" ) ;
348
380
return Ok ( dependency) ;
349
381
}
350
382
351
- let Some ( ( new_req_string, _) ) = upgrade_requirement ( & current. to_string ( ) , latest ) ? else {
383
+ let Some ( ( new_req_string, _) ) = upgrade_requirement ( & current. to_string ( ) , & new_version ) ? else {
352
384
trace ! ( "skipping dependency `{name}` because the version requirement didn't change" ) ;
353
385
return Ok ( dependency) ;
354
386
} ;
355
387
356
388
let upgrade_message = format ! ( "{name} {current} -> {new_req_string}" ) ;
357
389
trace ! ( upgrade_message) ;
358
390
391
+ let old_version = semver:: Version :: new (
392
+ comparator. major ,
393
+ comparator. minor . unwrap_or_default ( ) ,
394
+ comparator. patch . unwrap_or_default ( ) ,
395
+ ) ;
396
+ let is_downgrade = new_version < old_version;
397
+ let status = if is_downgrade {
398
+ "Downgrading"
399
+ } else {
400
+ "Upgrading"
401
+ } ;
402
+
359
403
if upgrade_messages. insert ( upgrade_message. clone ( ) ) {
360
404
gctx. shell ( )
361
- . status_with_color ( "Upgrading" , & upgrade_message, & style:: GOOD ) ?;
405
+ . status_with_color ( status , & upgrade_message, & style:: WARN ) ?;
362
406
}
363
407
364
- upgrades. insert ( ( name. to_string ( ) , dependency. source_id ( ) ) , latest. clone ( ) ) ;
408
+ upgrades. insert (
409
+ ( name. to_string ( ) , dependency. source_id ( ) ) ,
410
+ new_version. clone ( ) ,
411
+ ) ;
412
+
413
+ let new_version_req = VersionReq :: parse ( & new_version. to_string ( ) ) ?;
414
+
415
+ let req = if precise. is_some ( ) {
416
+ OptVersionReq :: Precise ( new_version, new_version_req)
417
+ } else {
418
+ OptVersionReq :: Req ( new_version_req)
419
+ } ;
365
420
366
- let req = OptVersionReq :: Req ( VersionReq :: parse ( & latest. to_string ( ) ) ?) ;
367
421
let mut dep = dependency. clone ( ) ;
368
422
dep. set_version_req ( req) ;
369
423
Ok ( dep)
0 commit comments