Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/cargo/ops/tree/format/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ enum Chunk {
Repository,
Features,
LibName,
VersionRequirement,
}

pub struct Pattern(Vec<Chunk>);
Expand All @@ -30,6 +31,7 @@ impl Pattern {
RawChunk::Argument("r") => Chunk::Repository,
RawChunk::Argument("f") => Chunk::Features,
RawChunk::Argument("lib") => Chunk::LibName,
RawChunk::Argument("ver-req") => Chunk::VersionRequirement,
RawChunk::Argument(a) => {
bail!("unsupported pattern `{}`", a);
}
Expand Down Expand Up @@ -111,6 +113,13 @@ impl<'a> fmt::Display for Display<'a> {
write!(fmt, "{}", target.crate_name())?;
}
}
Chunk::VersionRequirement => {
if let Some(version_req) =
self.graph.version_req_for_id(package.package_id())
{
write!(fmt, "{}", version_req)?;
}
}
}
}
}
Expand Down
17 changes: 16 additions & 1 deletion src/cargo/ops/tree/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use crate::core::dependency::DepKind;
use crate::core::resolver::Resolve;
use crate::core::resolver::features::{CliFeatures, FeaturesFor, ResolvedFeatures};
use crate::core::{FeatureMap, FeatureValue, Package, PackageId, PackageIdSpec, Workspace};
use crate::util::CargoResult;
use crate::util::interning::{INTERNED_DEFAULT, InternedString};
use crate::util::{CargoResult, OptVersionReq};
use std::collections::{HashMap, HashSet};

#[derive(Debug, Copy, Clone)]
Expand Down Expand Up @@ -161,6 +161,8 @@ pub struct Graph<'a> {
/// Key is the index of a package node, value is a map of `dep_name` to a
/// set of `(pkg_node_index, is_optional)`.
dep_name_map: HashMap<NodeId, HashMap<InternedString, HashSet<(NodeId, bool)>>>,
/// Map for looking up version requirements for dependency packages.
version_req_map: HashMap<PackageId, OptVersionReq>,
}

impl<'a> Graph<'a> {
Expand All @@ -172,6 +174,7 @@ impl<'a> Graph<'a> {
package_map,
cli_features: HashSet::new(),
dep_name_map: HashMap::new(),
version_req_map: HashMap::new(),
}
}

Expand Down Expand Up @@ -240,6 +243,12 @@ impl<'a> Graph<'a> {
}
}

/// Returns the version requirement for the given package ID. Returns `None`
/// if no version requirement is recorded (e.g., root packages).
pub fn version_req_for_id(&self, package_id: PackageId) -> Option<&OptVersionReq> {
self.version_req_map.get(&package_id)
}

/// Returns `true` if the given feature node index is a feature enabled
/// via the command-line.
pub fn is_cli_feature(&self, index: NodeId) -> bool {
Expand Down Expand Up @@ -523,6 +532,12 @@ fn add_pkg(
requested_kind,
opts,
);
// Store the version requirement for this dependency for
// later use in formatting.
graph.version_req_map.insert(
graph.package_id_for_index(dep_index),
dep.version_req().clone(),
);
let new_edge = Edge {
kind: EdgeKind::Dep(dep.kind()),
node: dep_index,
Expand Down
22 changes: 11 additions & 11 deletions tests/testsuite/cargo_tree/deps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1248,25 +1248,25 @@ foo v0.1.0 ([ROOT]/foo) [bar,default,dep_that_is_awesome,foo,other-dep]
.run();

p.cargo("tree --all-features")
.arg("--format={p}")
.arg("--format={p} satisfies {ver-req}")
.with_stdout_data(str![[r#"
foo v0.1.0 ([ROOT]/foo)
├── dep v1.0.0
├── dep_that_is_awesome v1.0.0
└── other-dep v1.0.0
└── dep v1.0.0
foo v0.1.0 ([ROOT]/foo) satisfies
├── dep v1.0.0 satisfies ^1.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I misreading things? This says foo has ^1.0 when it instead has:

dep = {version="=1.0"}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, this version requirement seems to belong to other-dep. I think I see what's happening. We don't consider the parent when registering the version requirement in the lookup map, so it's probably replacing previous entries for the same package?

I think using NodeID as index should make this unique.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a forewarning, a problem I suspect we'll see when we have two different, compatible version requirements on the same package and --invert it is that we'll only see one of them.

version requirements are more of a property of edges and not nodes. We don't annotate edges. Is that good enough, a blocker for this feature, or a cause to re-think some base assumptions?

As an example of the latter, maybe for --invert, we put the version requirement on the dependent package and not the dependency.

├── dep_that_is_awesome v1.0.0 satisfies >=1.0, <2
└── other-dep v1.0.0 satisfies ^1.0
└── dep v1.0.0 satisfies ^1.0
"#]])
.run();

p.cargo("tree --all-features")
.arg("--format={p}")
.arg("--format={p} satisfies {ver-req}")
.arg("--invert=dep")
.with_stdout_data(str![[r#"
dep v1.0.0
├── foo v0.1.0 ([ROOT]/foo)
└── other-dep v1.0.0
└── foo v0.1.0 ([ROOT]/foo)
dep v1.0.0 satisfies ^1.0
├── foo v0.1.0 ([ROOT]/foo) satisfies
└── other-dep v1.0.0 satisfies ^1.0
└── foo v0.1.0 ([ROOT]/foo) satisfies
"#]])
.run();
Expand Down