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