cargo/ops/registry/info/
mod.rs
1use anyhow::bail;
4use cargo_util_schemas::core::{PackageIdSpec, PartialVersion};
5
6use crate::core::registry::PackageRegistry;
7use crate::core::{Dependency, Package, PackageId, PackageIdSpecQuery, Registry, Workspace};
8use crate::ops::registry::info::view::pretty_view;
9use crate::ops::registry::{get_source_id_with_package_id, RegistryOrIndex, RegistrySourceIds};
10use crate::ops::resolve_ws;
11use crate::sources::source::QueryKind;
12use crate::sources::{IndexSummary, SourceConfigMap};
13use crate::util::cache_lock::CacheLockMode;
14use crate::util::command_prelude::root_manifest;
15use crate::{CargoResult, GlobalContext};
16
17mod view;
18
19pub fn info(
20 spec: &PackageIdSpec,
21 gctx: &GlobalContext,
22 reg_or_index: Option<RegistryOrIndex>,
23) -> CargoResult<()> {
24 let source_config = SourceConfigMap::new(gctx)?;
25 let mut registry = PackageRegistry::new_with_source_config(gctx, source_config)?;
26 let _lock = gctx.acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
28 registry.lock_patches();
29
30 let nearest_manifest_path = root_manifest(None, gctx).ok();
32 let ws = nearest_manifest_path
33 .as_ref()
34 .and_then(|root| Workspace::new(root, gctx).ok());
35 validate_locked_and_frozen_options(ws.is_some(), gctx)?;
36 let nearest_package = ws.as_ref().and_then(|ws| {
37 nearest_manifest_path
38 .as_ref()
39 .and_then(|path| ws.members().find(|p| p.manifest_path() == path))
40 });
41 let (mut package_id, is_member) = find_pkgid_in_ws(nearest_package, ws.as_ref(), spec);
42 let (use_package_source_id, source_ids) =
43 get_source_id_with_package_id(gctx, package_id, reg_or_index.as_ref())?;
44 if !use_package_source_id {
46 package_id = None;
47 }
48
49 let msrv_from_nearest_manifest_path_or_ws =
50 try_get_msrv_from_nearest_manifest_or_ws(nearest_package, ws.as_ref());
51 let rustc_version = match msrv_from_nearest_manifest_path_or_ws {
54 Some(msrv) => msrv,
55 None => {
56 let current_rustc = gctx.load_global_rustc(ws.as_ref())?.version;
57 semver::Version::new(
60 current_rustc.major,
61 current_rustc.minor,
62 current_rustc.patch,
63 )
64 .into()
65 }
66 };
67 let suggest_cargo_tree_command = package_id.is_some() && !is_member;
70
71 let summaries = query_summaries(spec, &mut registry, &source_ids)?;
72 let package_id = match package_id {
73 Some(id) => id,
74 None => find_pkgid_in_summaries(&summaries, spec, &rustc_version, &source_ids)?,
75 };
76
77 let package = registry.get(&[package_id])?;
78 let package = package.get_one(package_id)?;
79 pretty_view(package, &summaries, suggest_cargo_tree_command, gctx)?;
80
81 Ok(())
82}
83
84fn find_pkgid_in_ws(
85 nearest_package: Option<&Package>,
86 ws: Option<&Workspace<'_>>,
87 spec: &PackageIdSpec,
88) -> (Option<PackageId>, bool) {
89 let Some(ws) = ws else {
90 return (None, false);
91 };
92
93 if let Some(member) = ws.members().find(|p| spec.matches(p.package_id())) {
94 return (Some(member.package_id()), true);
95 }
96
97 let Ok((_, resolve)) = resolve_ws(ws, false) else {
98 return (None, false);
99 };
100
101 if let Some(package_id) = nearest_package
102 .map(|p| p.package_id())
103 .into_iter()
104 .flat_map(|p| resolve.deps(p))
105 .map(|(p, _)| p)
106 .filter(|&p| spec.matches(p))
107 .max_by_key(|&p| p.version())
108 {
109 return (Some(package_id), false);
110 }
111
112 if let Some(package_id) = ws
113 .members()
114 .map(|p| p.package_id())
115 .flat_map(|p| resolve.deps(p))
116 .map(|(p, _)| p)
117 .filter(|&p| spec.matches(p))
118 .max_by_key(|&p| p.version())
119 {
120 return (Some(package_id), false);
121 }
122
123 if let Some(package_id) = resolve
124 .iter()
125 .filter(|&p| spec.matches(p))
126 .max_by_key(|&p| p.version())
127 {
128 return (Some(package_id), false);
129 }
130
131 (None, false)
132}
133
134fn find_pkgid_in_summaries(
135 summaries: &[IndexSummary],
136 spec: &PackageIdSpec,
137 rustc_version: &PartialVersion,
138 source_ids: &RegistrySourceIds,
139) -> CargoResult<PackageId> {
140 let summary = summaries
141 .iter()
142 .filter(|s| spec.matches(s.package_id()))
143 .max_by(|s1, s2| {
144 let s1_matches = s1
146 .as_summary()
147 .rust_version()
148 .map(|v| v.is_compatible_with(rustc_version))
149 .unwrap_or_else(|| false);
150 let s2_matches = s2
151 .as_summary()
152 .rust_version()
153 .map(|v| v.is_compatible_with(rustc_version))
154 .unwrap_or_else(|| false);
155 match (s1_matches, s2_matches) {
157 (true, false) => std::cmp::Ordering::Greater,
158 (false, true) => std::cmp::Ordering::Less,
159 _ => s1.package_id().version().cmp(s2.package_id().version()),
162 }
163 });
164
165 match summary {
166 Some(summary) => Ok(summary.package_id()),
167 None => {
168 anyhow::bail!(
169 "could not find `{}` in registry `{}`",
170 spec,
171 source_ids.original.url()
172 )
173 }
174 }
175}
176
177fn query_summaries(
178 spec: &PackageIdSpec,
179 registry: &mut PackageRegistry<'_>,
180 source_ids: &RegistrySourceIds,
181) -> CargoResult<Vec<IndexSummary>> {
182 let dep = Dependency::parse(spec.name(), None, source_ids.original)?;
184 loop {
185 match registry.query_vec(&dep, QueryKind::Exact) {
187 std::task::Poll::Ready(res) => {
188 break res;
189 }
190 std::task::Poll::Pending => registry.block_until_ready()?,
191 }
192 }
193}
194
195fn validate_locked_and_frozen_options(
196 in_workspace: bool,
197 gctx: &GlobalContext,
198) -> Result<(), anyhow::Error> {
199 if !in_workspace {
201 if let Some(locked_flag) = gctx.locked_flag() {
202 bail!("the option `{locked_flag}` can only be used within a workspace");
203 }
204 }
205 Ok(())
206}
207
208fn try_get_msrv_from_nearest_manifest_or_ws(
209 nearest_package: Option<&Package>,
210 ws: Option<&Workspace<'_>>,
211) -> Option<PartialVersion> {
212 let rust_version = nearest_package.and_then(|p| p.rust_version().map(|v| v.as_partial()));
214 rust_version
216 .or_else(|| ws.and_then(|ws| ws.lowest_rust_version().map(|v| v.as_partial())))
217 .cloned()
218}