@@ -4,11 +4,12 @@ mod crate_spec;
4
4
mod dependency;
5
5
mod manifest;
6
6
7
- use anyhow :: Context ;
7
+ use std :: collections :: BTreeMap ;
8
8
use std:: collections:: BTreeSet ;
9
9
use std:: collections:: VecDeque ;
10
10
use std:: path:: Path ;
11
11
12
+ use anyhow:: Context as _;
12
13
use cargo_util:: paths;
13
14
use indexmap:: IndexSet ;
14
15
use termcolor:: Color :: Green ;
@@ -18,10 +19,12 @@ use toml_edit::Item as TomlItem;
18
19
19
20
use crate :: core:: dependency:: DepKind ;
20
21
use crate :: core:: registry:: PackageRegistry ;
22
+ use crate :: core:: FeatureValue ;
21
23
use crate :: core:: Package ;
22
24
use crate :: core:: QueryKind ;
23
25
use crate :: core:: Registry ;
24
26
use crate :: core:: Shell ;
27
+ use crate :: core:: Summary ;
25
28
use crate :: core:: Workspace ;
26
29
use crate :: CargoResult ;
27
30
use crate :: Config ;
@@ -200,7 +203,7 @@ fn resolve_dependency(
200
203
section : & DepTable ,
201
204
config : & Config ,
202
205
registry : & mut PackageRegistry < ' _ > ,
203
- ) -> CargoResult < Dependency > {
206
+ ) -> CargoResult < DependencyUI > {
204
207
let crate_spec = arg
205
208
. crate_spec
206
209
. as_deref ( )
@@ -284,9 +287,7 @@ fn resolve_dependency(
284
287
// Overwrite with `crate_spec`
285
288
old_dep. source = selected_dep. source ;
286
289
}
287
- old_dep = populate_dependency ( old_dep, arg) ;
288
- old_dep. available_features = selected_dep. available_features ;
289
- old_dep
290
+ populate_dependency ( old_dep, arg)
290
291
}
291
292
} else {
292
293
selected_dep
@@ -318,9 +319,7 @@ fn resolve_dependency(
318
319
) ) ?;
319
320
dependency. name = latest. name ; // Normalize the name
320
321
}
321
- dependency = dependency
322
- . set_source ( latest. source . expect ( "latest always has a source" ) )
323
- . set_available_features ( latest. available_features ) ;
322
+ dependency = dependency. set_source ( latest. source . expect ( "latest always has a source" ) ) ;
324
323
}
325
324
}
326
325
@@ -339,7 +338,25 @@ fn resolve_dependency(
339
338
dependency = dependency. clear_version ( ) ;
340
339
}
341
340
342
- dependency = populate_available_features ( dependency, config, registry, ws) ?;
341
+ let query = dependency. query ( config) ?;
342
+ let query = match query {
343
+ MaybeWorkspace :: Workspace ( _workspace) => {
344
+ let dep = find_workspace_dep ( dependency. toml_key ( ) , ws. root_manifest ( ) ) ?;
345
+ if let Some ( features) = dep. features . clone ( ) {
346
+ dependency = dependency. set_inherited_features ( features) ;
347
+ }
348
+ let query = dep. query ( config) ?;
349
+ match query {
350
+ MaybeWorkspace :: Workspace ( _) => {
351
+ unreachable ! ( "This should have been caught when parsing a workspace root" )
352
+ }
353
+ MaybeWorkspace :: Other ( query) => query,
354
+ }
355
+ }
356
+ MaybeWorkspace :: Other ( query) => query,
357
+ } ;
358
+
359
+ let dependency = populate_available_features ( dependency, & query, registry) ?;
343
360
344
361
Ok ( dependency)
345
362
}
@@ -582,34 +599,81 @@ fn populate_dependency(mut dependency: Dependency, arg: &DepOp) -> Dependency {
582
599
dependency
583
600
}
584
601
602
+ /// Track presentation-layer information with the editable representation of a `[dependencies]`
603
+ /// entry (Dependency)
604
+ pub struct DependencyUI {
605
+ /// Editable representation of a `[depednencies]` entry
606
+ dep : Dependency ,
607
+ /// The version of the crate that we pulled `available_features` from
608
+ available_version : Option < semver:: Version > ,
609
+ /// The widest set of features compatible with `Dependency`s version requirement
610
+ available_features : BTreeMap < String , Vec < String > > ,
611
+ }
612
+
613
+ impl DependencyUI {
614
+ fn new ( dep : Dependency ) -> Self {
615
+ Self {
616
+ dep,
617
+ available_version : None ,
618
+ available_features : Default :: default ( ) ,
619
+ }
620
+ }
621
+
622
+ fn apply_summary ( & mut self , summary : & Summary ) {
623
+ self . available_version = Some ( summary. version ( ) . clone ( ) ) ;
624
+ self . available_features = summary
625
+ . features ( )
626
+ . iter ( )
627
+ . map ( |( k, v) | {
628
+ (
629
+ k. as_str ( ) . to_owned ( ) ,
630
+ v. iter ( )
631
+ . filter_map ( |v| match v {
632
+ FeatureValue :: Feature ( f) => Some ( f. as_str ( ) . to_owned ( ) ) ,
633
+ FeatureValue :: Dep { .. } | FeatureValue :: DepFeature { .. } => None ,
634
+ } )
635
+ . collect :: < Vec < _ > > ( ) ,
636
+ )
637
+ } )
638
+ . collect ( ) ;
639
+ }
640
+ }
641
+
642
+ impl < ' s > From < & ' s Summary > for DependencyUI {
643
+ fn from ( other : & ' s Summary ) -> Self {
644
+ let dep = Dependency :: from ( other) ;
645
+ let mut dep = Self :: new ( dep) ;
646
+ dep. apply_summary ( other) ;
647
+ dep
648
+ }
649
+ }
650
+
651
+ impl std:: fmt:: Display for DependencyUI {
652
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
653
+ self . dep . fmt ( f)
654
+ }
655
+ }
656
+
657
+ impl std:: ops:: Deref for DependencyUI {
658
+ type Target = Dependency ;
659
+
660
+ fn deref ( & self ) -> & Self :: Target {
661
+ & self . dep
662
+ }
663
+ }
664
+
585
665
/// Lookup available features
586
666
fn populate_available_features (
587
- mut dependency : Dependency ,
588
- config : & Config ,
667
+ dependency : Dependency ,
668
+ query : & crate :: core :: dependency :: Dependency ,
589
669
registry : & mut PackageRegistry < ' _ > ,
590
- ws : & Workspace < ' _ > ,
591
- ) -> CargoResult < Dependency > {
670
+ ) -> CargoResult < DependencyUI > {
671
+ let mut dependency = DependencyUI :: new ( dependency) ;
672
+
592
673
if !dependency. available_features . is_empty ( ) {
593
674
return Ok ( dependency) ;
594
675
}
595
676
596
- let query = dependency. query ( config) ?;
597
- let query = match query {
598
- MaybeWorkspace :: Workspace ( _workspace) => {
599
- let dep = find_workspace_dep ( dependency. toml_key ( ) , ws. root_manifest ( ) ) ?;
600
- if let Some ( features) = dep. features . clone ( ) {
601
- dependency = dependency. set_inherited_features ( features) ;
602
- }
603
- let query = dep. query ( config) ?;
604
- match query {
605
- MaybeWorkspace :: Workspace ( _) => {
606
- unreachable ! ( "This should have been caught when parsing a workspace root" )
607
- }
608
- MaybeWorkspace :: Other ( query) => query,
609
- }
610
- }
611
- MaybeWorkspace :: Other ( query) => query,
612
- } ;
613
677
let possibilities = loop {
614
678
match registry. query_vec ( & query, QueryKind :: Fuzzy ) {
615
679
std:: task:: Poll :: Ready ( res) => {
@@ -631,12 +695,12 @@ fn populate_available_features(
631
695
. ok_or_else ( || {
632
696
anyhow:: format_err!( "the crate `{dependency}` could not be found in registry index." )
633
697
} ) ?;
634
- dependency = dependency . set_available_features_from_cargo ( lowest_common_denominator. features ( ) ) ;
698
+ dependency. apply_summary ( & lowest_common_denominator) ;
635
699
636
700
Ok ( dependency)
637
701
}
638
702
639
- fn print_msg ( shell : & mut Shell , dep : & Dependency , section : & [ String ] ) -> CargoResult < ( ) > {
703
+ fn print_msg ( shell : & mut Shell , dep : & DependencyUI , section : & [ String ] ) -> CargoResult < ( ) > {
640
704
use std:: fmt:: Write ;
641
705
642
706
if matches ! ( shell. verbosity( ) , crate :: core:: shell:: Verbosity :: Quiet ) {
@@ -709,7 +773,28 @@ fn print_msg(shell: &mut Shell, dep: &Dependency, section: &[String]) -> CargoRe
709
773
deactivated. sort ( ) ;
710
774
if !activated. is_empty ( ) || !deactivated. is_empty ( ) {
711
775
let prefix = format ! ( "{:>13}" , " " ) ;
712
- shell. write_stderr ( format_args ! ( "{}Features:\n " , prefix) , & ColorSpec :: new ( ) ) ?;
776
+ let suffix = if let Some ( version) = & dep. available_version {
777
+ let mut version = version. clone ( ) ;
778
+ version. build = Default :: default ( ) ;
779
+ let version = version. to_string ( ) ;
780
+ // Avoid displaying the version if it will visually look like the version req that we
781
+ // showed earlier
782
+ let version_req = dep
783
+ . version ( )
784
+ . and_then ( |v| semver:: VersionReq :: parse ( v) . ok ( ) )
785
+ . and_then ( |v| precise_version ( & v) ) ;
786
+ if version_req. as_deref ( ) != Some ( version. as_str ( ) ) {
787
+ format ! ( " as of v{version}" )
788
+ } else {
789
+ "" . to_owned ( )
790
+ }
791
+ } else {
792
+ "" . to_owned ( )
793
+ } ;
794
+ shell. write_stderr (
795
+ format_args ! ( "{}Features{}:\n " , prefix, suffix) ,
796
+ & ColorSpec :: new ( ) ,
797
+ ) ?;
713
798
for feat in activated {
714
799
shell. write_stderr ( & prefix, & ColorSpec :: new ( ) ) ?;
715
800
shell. write_stderr ( '+' , & ColorSpec :: new ( ) . set_bold ( true ) . set_fg ( Some ( Green ) ) ) ?;
@@ -765,3 +850,37 @@ fn find_workspace_dep(toml_key: &str, root_manifest: &Path) -> CargoResult<Depen
765
850
) ) ?;
766
851
Dependency :: from_toml ( root_manifest. parent ( ) . unwrap ( ) , toml_key, dep_item)
767
852
}
853
+
854
+ /// Convert a `semver::VersionReq` into a rendered `semver::Version` if all fields are fully
855
+ /// specified.
856
+ fn precise_version ( version_req : & semver:: VersionReq ) -> Option < String > {
857
+ version_req
858
+ . comparators
859
+ . iter ( )
860
+ . filter ( |c| {
861
+ matches ! (
862
+ c. op,
863
+ // Only ops we can determine a precise version from
864
+ semver:: Op :: Exact
865
+ | semver:: Op :: GreaterEq
866
+ | semver:: Op :: LessEq
867
+ | semver:: Op :: Tilde
868
+ | semver:: Op :: Caret
869
+ | semver:: Op :: Wildcard
870
+ )
871
+ } )
872
+ . filter_map ( |c| {
873
+ // Only do it when full precision is specified
874
+ c. minor . and_then ( |minor| {
875
+ c. patch . map ( |patch| semver:: Version {
876
+ major : c. major ,
877
+ minor,
878
+ patch,
879
+ pre : c. pre . clone ( ) ,
880
+ build : Default :: default ( ) ,
881
+ } )
882
+ } )
883
+ } )
884
+ . max ( )
885
+ . map ( |v| v. to_string ( ) )
886
+ }
0 commit comments