@@ -4,11 +4,12 @@ mod crate_spec;
44mod dependency;
55mod manifest;
66
7- use anyhow :: Context ;
7+ use std :: collections :: BTreeMap ;
88use std:: collections:: BTreeSet ;
99use std:: collections:: VecDeque ;
1010use std:: path:: Path ;
1111
12+ use anyhow:: Context as _;
1213use cargo_util:: paths;
1314use indexmap:: IndexSet ;
1415use termcolor:: Color :: Green ;
@@ -18,10 +19,12 @@ use toml_edit::Item as TomlItem;
1819
1920use crate :: core:: dependency:: DepKind ;
2021use crate :: core:: registry:: PackageRegistry ;
22+ use crate :: core:: FeatureValue ;
2123use crate :: core:: Package ;
2224use crate :: core:: QueryKind ;
2325use crate :: core:: Registry ;
2426use crate :: core:: Shell ;
27+ use crate :: core:: Summary ;
2528use crate :: core:: Workspace ;
2629use crate :: CargoResult ;
2730use crate :: Config ;
@@ -200,7 +203,7 @@ fn resolve_dependency(
200203 section : & DepTable ,
201204 config : & Config ,
202205 registry : & mut PackageRegistry < ' _ > ,
203- ) -> CargoResult < Dependency > {
206+ ) -> CargoResult < DependencyUI > {
204207 let crate_spec = arg
205208 . crate_spec
206209 . as_deref ( )
@@ -284,9 +287,7 @@ fn resolve_dependency(
284287 // Overwrite with `crate_spec`
285288 old_dep. source = selected_dep. source ;
286289 }
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)
290291 }
291292 } else {
292293 selected_dep
@@ -318,9 +319,7 @@ fn resolve_dependency(
318319 ) ) ?;
319320 dependency. name = latest. name ; // Normalize the name
320321 }
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" ) ) ;
324323 }
325324 }
326325
@@ -339,7 +338,25 @@ fn resolve_dependency(
339338 dependency = dependency. clear_version ( ) ;
340339 }
341340
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) ?;
343360
344361 Ok ( dependency)
345362}
@@ -582,34 +599,81 @@ fn populate_dependency(mut dependency: Dependency, arg: &DepOp) -> Dependency {
582599 dependency
583600}
584601
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+
585665/// Lookup available features
586666fn populate_available_features (
587- mut dependency : Dependency ,
588- config : & Config ,
667+ dependency : Dependency ,
668+ query : & crate :: core :: dependency :: Dependency ,
589669 registry : & mut PackageRegistry < ' _ > ,
590- ws : & Workspace < ' _ > ,
591- ) -> CargoResult < Dependency > {
670+ ) -> CargoResult < DependencyUI > {
671+ let mut dependency = DependencyUI :: new ( dependency) ;
672+
592673 if !dependency. available_features . is_empty ( ) {
593674 return Ok ( dependency) ;
594675 }
595676
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- } ;
613677 let possibilities = loop {
614678 match registry. query_vec ( & query, QueryKind :: Fuzzy ) {
615679 std:: task:: Poll :: Ready ( res) => {
@@ -631,12 +695,12 @@ fn populate_available_features(
631695 . ok_or_else ( || {
632696 anyhow:: format_err!( "the crate `{dependency}` could not be found in registry index." )
633697 } ) ?;
634- dependency = dependency . set_available_features_from_cargo ( lowest_common_denominator. features ( ) ) ;
698+ dependency. apply_summary ( & lowest_common_denominator) ;
635699
636700 Ok ( dependency)
637701}
638702
639- fn print_msg ( shell : & mut Shell , dep : & Dependency , section : & [ String ] ) -> CargoResult < ( ) > {
703+ fn print_msg ( shell : & mut Shell , dep : & DependencyUI , section : & [ String ] ) -> CargoResult < ( ) > {
640704 use std:: fmt:: Write ;
641705
642706 if matches ! ( shell. verbosity( ) , crate :: core:: shell:: Verbosity :: Quiet ) {
@@ -709,7 +773,28 @@ fn print_msg(shell: &mut Shell, dep: &Dependency, section: &[String]) -> CargoRe
709773 deactivated. sort ( ) ;
710774 if !activated. is_empty ( ) || !deactivated. is_empty ( ) {
711775 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+ ) ?;
713798 for feat in activated {
714799 shell. write_stderr ( & prefix, & ColorSpec :: new ( ) ) ?;
715800 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
765850 ) ) ?;
766851 Dependency :: from_toml ( root_manifest. parent ( ) . unwrap ( ) , toml_key, dep_item)
767852}
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