@@ -51,6 +51,8 @@ pub struct AddOptions<'a> {
51
51
pub section : DepTable ,
52
52
/// Act as if dependencies will be added
53
53
pub dry_run : bool ,
54
+ /// Whether the minimum supported Rust version should be considered during resolution
55
+ pub honor_rust_version : bool ,
54
56
}
55
57
56
58
/// Add dependencies to a manifest
@@ -86,7 +88,9 @@ pub fn add(workspace: &Workspace<'_>, options: &AddOptions<'_>) -> CargoResult<(
86
88
& manifest,
87
89
raw,
88
90
workspace,
91
+ & options. spec ,
89
92
& options. section ,
93
+ options. honor_rust_version ,
90
94
options. config ,
91
95
& mut registry,
92
96
)
@@ -256,7 +260,9 @@ fn resolve_dependency(
256
260
manifest : & LocalManifest ,
257
261
arg : & DepOp ,
258
262
ws : & Workspace < ' _ > ,
263
+ spec : & Package ,
259
264
section : & DepTable ,
265
+ honor_rust_version : bool ,
260
266
config : & Config ,
261
267
registry : & mut PackageRegistry < ' _ > ,
262
268
) -> CargoResult < DependencyUI > {
@@ -368,7 +374,14 @@ fn resolve_dependency(
368
374
}
369
375
dependency = dependency. set_source ( src) ;
370
376
} else {
371
- let latest = get_latest_dependency ( & dependency, false , config, registry) ?;
377
+ let latest = get_latest_dependency (
378
+ spec,
379
+ & dependency,
380
+ false ,
381
+ honor_rust_version,
382
+ config,
383
+ registry,
384
+ ) ?;
372
385
373
386
if dependency. name != latest. name {
374
387
config. shell ( ) . warn ( format ! (
@@ -518,8 +531,10 @@ fn get_existing_dependency(
518
531
}
519
532
520
533
fn get_latest_dependency (
534
+ spec : & Package ,
521
535
dependency : & Dependency ,
522
536
_flag_allow_prerelease : bool ,
537
+ honor_rust_version : bool ,
523
538
config : & Config ,
524
539
registry : & mut PackageRegistry < ' _ > ,
525
540
) -> CargoResult < Dependency > {
@@ -529,27 +544,87 @@ fn get_latest_dependency(
529
544
unreachable ! ( "registry dependencies required, found a workspace dependency" ) ;
530
545
}
531
546
MaybeWorkspace :: Other ( query) => {
532
- let possibilities = loop {
547
+ let mut possibilities = loop {
533
548
match registry. query_vec ( & query, QueryKind :: Fuzzy ) {
534
549
std:: task:: Poll :: Ready ( res) => {
535
550
break res?;
536
551
}
537
552
std:: task:: Poll :: Pending => registry. block_until_ready ( ) ?,
538
553
}
539
554
} ;
540
- let latest = possibilities
541
- . iter ( )
542
- . max_by_key ( |s| {
543
- // Fallback to a pre-release if no official release is available by sorting them as
544
- // less.
545
- let stable = s. version ( ) . pre . is_empty ( ) ;
546
- ( stable, s. version ( ) )
547
- } )
548
- . ok_or_else ( || {
549
- anyhow:: format_err!(
550
- "the crate `{dependency}` could not be found in registry index."
551
- )
552
- } ) ?;
555
+
556
+ possibilities. sort_by_key ( |s| {
557
+ // Fallback to a pre-release if no official release is available by sorting them as
558
+ // less.
559
+ let stable = s. version ( ) . pre . is_empty ( ) ;
560
+ ( stable, s. version ( ) . clone ( ) )
561
+ } ) ;
562
+
563
+ let mut latest = possibilities. last ( ) . ok_or_else ( || {
564
+ anyhow:: format_err!(
565
+ "the crate `{dependency}` could not be found in registry index."
566
+ )
567
+ } ) ?;
568
+
569
+ if config. cli_unstable ( ) . msrv_policy && honor_rust_version {
570
+ fn parse_msrv ( rust_version : impl AsRef < str > ) -> ( u64 , u64 , u64 ) {
571
+ // HACK: `rust-version` is a subset of the `VersionReq` syntax that only ever
572
+ // has one comparator with a required minor and optional patch, and uses no
573
+ // other features. If in the future this syntax is expanded, this code will need
574
+ // to be updated.
575
+ let version_req = semver:: VersionReq :: parse ( rust_version. as_ref ( ) ) . unwrap ( ) ;
576
+ assert ! ( version_req. comparators. len( ) == 1 ) ;
577
+ let comp = & version_req. comparators [ 0 ] ;
578
+ assert_eq ! ( comp. op, semver:: Op :: Caret ) ;
579
+ assert_eq ! ( comp. pre, semver:: Prerelease :: EMPTY ) ;
580
+ ( comp. major , comp. minor . unwrap_or ( 0 ) , comp. patch . unwrap_or ( 0 ) )
581
+ }
582
+
583
+ if let Some ( req_msrv) = spec. rust_version ( ) . map ( parse_msrv) {
584
+ let msrvs = possibilities
585
+ . iter ( )
586
+ . map ( |s| ( s, s. rust_version ( ) . map ( parse_msrv) ) )
587
+ . collect :: < Vec < _ > > ( ) ;
588
+
589
+ // Find the latest version of the dep which has a compatible rust-version. To
590
+ // determine whether or not one rust-version is compatible with another, we
591
+ // compare the lowest possible versions they could represent, and treat
592
+ // candidates without a rust-version as compatible by default.
593
+ let ( latest_msrv, _) = msrvs
594
+ . iter ( )
595
+ . filter ( |( _, v) | v. map ( |msrv| req_msrv >= msrv) . unwrap_or ( true ) )
596
+ . last ( )
597
+ . ok_or_else ( || {
598
+ // Failing that, try to find the highest version with the lowest
599
+ // rust-version to report to the user.
600
+ let lowest_candidate = msrvs
601
+ . iter ( )
602
+ . min_set_by_key ( |( _, v) | v)
603
+ . iter ( )
604
+ . map ( |( s, _) | s)
605
+ . max_by_key ( |s| s. version ( ) ) ;
606
+ rust_version_incompat_error (
607
+ & dependency. name ,
608
+ spec. rust_version ( ) . unwrap ( ) ,
609
+ lowest_candidate. copied ( ) ,
610
+ )
611
+ } ) ?;
612
+
613
+ if latest_msrv. version ( ) < latest. version ( ) {
614
+ config. shell ( ) . warn ( format_args ! (
615
+ "ignoring `{dependency}@{latest_version}` (which has a rust-version of \
616
+ {latest_rust_version}) to satisfy this package's rust-version of \
617
+ {rust_version} (use `--ignore-rust-version` to override)",
618
+ latest_version = latest. version( ) ,
619
+ latest_rust_version = latest. rust_version( ) . unwrap( ) ,
620
+ rust_version = spec. rust_version( ) . unwrap( ) ,
621
+ ) ) ?;
622
+
623
+ latest = latest_msrv;
624
+ }
625
+ }
626
+ }
627
+
553
628
let mut dep = Dependency :: from ( latest) ;
554
629
if let Some ( reg_name) = dependency. registry . as_deref ( ) {
555
630
dep = dep. set_registry ( reg_name) ;
@@ -559,6 +634,31 @@ fn get_latest_dependency(
559
634
}
560
635
}
561
636
637
+ fn rust_version_incompat_error (
638
+ dep : & str ,
639
+ rust_version : & str ,
640
+ lowest_rust_version : Option < & Summary > ,
641
+ ) -> anyhow:: Error {
642
+ let mut error_msg = format ! (
643
+ "could not find version of crate `{dep}` that satisfies this package's rust-version of \
644
+ {rust_version}\n \
645
+ help: use `--ignore-rust-version` to override this behavior"
646
+ ) ;
647
+
648
+ if let Some ( lowest) = lowest_rust_version {
649
+ // rust-version must be present for this candidate since it would have been selected as
650
+ // compatible previously if it weren't.
651
+ let version = lowest. version ( ) ;
652
+ let rust_version = lowest. rust_version ( ) . unwrap ( ) ;
653
+ error_msg. push_str ( & format ! (
654
+ "\n note: the lowest rust-version available for `{dep}` is {rust_version}, used in \
655
+ version {version}"
656
+ ) ) ;
657
+ }
658
+
659
+ anyhow:: format_err!( error_msg)
660
+ }
661
+
562
662
fn select_package (
563
663
dependency : & Dependency ,
564
664
config : & Config ,
0 commit comments