Skip to content

Commit bef47cb

Browse files
committed
Auto merge of #12643 - Eh2406:IndexSummaryEnum, r=epage
Index summary enum ### What does this PR try to resolve? This is a tiny incremental part of allowing registries to return richer information about summaries that are not available for resolution. Eventually this should lead to better error messages, but for now it's just an internal re-factor. Discussion of the overall approach is at https://rust-lang.zulipchat.com/#narrow/stream/246057-t-cargo/topic/better.20error.20messages.20for.20filtered.20versions.2E ### How should we test and review this PR? Internal re-factor and tests still pass.
2 parents c2d26f3 + 5c8cf8d commit bef47cb

File tree

3 files changed

+205
-83
lines changed

3 files changed

+205
-83
lines changed

src/cargo/sources/registry/index.rs

Lines changed: 155 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,64 @@ enum MaybeIndexSummary {
189189
/// from a line from a raw index file, or a JSON blob from on-disk index cache.
190190
///
191191
/// In addition to a full [`Summary`], we have information on whether it is `yanked`.
192-
pub struct IndexSummary {
193-
pub summary: Summary,
194-
pub yanked: bool,
195-
/// Schema version, see [`IndexPackage::v`].
196-
v: u32,
192+
#[derive(Clone, Debug)]
193+
pub enum IndexSummary {
194+
/// Available for consideration
195+
Candidate(Summary),
196+
/// Yanked within its registry
197+
Yanked(Summary),
198+
/// Not available as we are offline and create is not downloaded yet
199+
Offline(Summary),
200+
/// From a newer schema version and is likely incomplete or inaccurate
201+
Unsupported(Summary, u32),
202+
}
203+
204+
impl IndexSummary {
205+
/// Extract the summary from any variant
206+
pub fn as_summary(&self) -> &Summary {
207+
match self {
208+
IndexSummary::Candidate(sum)
209+
| IndexSummary::Yanked(sum)
210+
| IndexSummary::Offline(sum)
211+
| IndexSummary::Unsupported(sum, _) => sum,
212+
}
213+
}
214+
215+
/// Extract the summary from any variant
216+
pub fn into_summary(self) -> Summary {
217+
match self {
218+
IndexSummary::Candidate(sum)
219+
| IndexSummary::Yanked(sum)
220+
| IndexSummary::Offline(sum)
221+
| IndexSummary::Unsupported(sum, _) => sum,
222+
}
223+
}
224+
225+
/// Extract the package id from any variant
226+
pub fn package_id(&self) -> PackageId {
227+
match self {
228+
IndexSummary::Candidate(sum)
229+
| IndexSummary::Yanked(sum)
230+
| IndexSummary::Offline(sum)
231+
| IndexSummary::Unsupported(sum, _) => sum.package_id(),
232+
}
233+
}
234+
235+
/// Returns `true` if the index summary is [`Yanked`].
236+
///
237+
/// [`Yanked`]: IndexSummary::Yanked
238+
#[must_use]
239+
pub fn is_yanked(&self) -> bool {
240+
matches!(self, Self::Yanked(..))
241+
}
242+
243+
/// Returns `true` if the index summary is [`Offline`].
244+
///
245+
/// [`Offline`]: IndexSummary::Offline
246+
#[must_use]
247+
pub fn is_offline(&self) -> bool {
248+
matches!(self, Self::Offline(..))
249+
}
197250
}
198251

199252
/// A representation of the cache on disk that Cargo maintains of summaries.
@@ -385,13 +438,13 @@ impl<'cfg> RegistryIndex<'cfg> {
385438
/// the index file, aka [`IndexSummary`].
386439
pub fn hash(&mut self, pkg: PackageId, load: &mut dyn RegistryData) -> Poll<CargoResult<&str>> {
387440
let req = OptVersionReq::exact(pkg.version());
388-
let summary = self.summaries(&pkg.name(), &req, load)?;
441+
let summary = self.summaries(pkg.name(), &req, load)?;
389442
let summary = ready!(summary)
390-
.filter(|s| s.summary.version() == pkg.version())
443+
.filter(|s| s.package_id().version() == pkg.version())
391444
.next();
392445
Poll::Ready(Ok(summary
393446
.ok_or_else(|| internal(format!("no hash listed for {}", pkg)))?
394-
.summary
447+
.as_summary()
395448
.checksum()
396449
.ok_or_else(|| internal(format!("no hash listed for {}", pkg)))?))
397450
}
@@ -407,9 +460,9 @@ impl<'cfg> RegistryIndex<'cfg> {
407460
///
408461
/// Internally there's quite a few layer of caching to amortize this cost
409462
/// though since this method is called quite a lot on null builds in Cargo.
410-
pub fn summaries<'a, 'b>(
463+
fn summaries<'a, 'b>(
411464
&'a mut self,
412-
name: &str,
465+
name: InternedString,
413466
req: &'b OptVersionReq,
414467
load: &mut dyn RegistryData,
415468
) -> Poll<CargoResult<impl Iterator<Item = &'a IndexSummary> + 'b>>
@@ -421,7 +474,6 @@ impl<'cfg> RegistryIndex<'cfg> {
421474
let source_id = self.source_id;
422475

423476
// First up parse what summaries we have available.
424-
let name = InternedString::new(name);
425477
let summaries = ready!(self.load_summaries(name, load)?);
426478

427479
// Iterate over our summaries, extract all relevant ones which match our
@@ -435,26 +487,27 @@ impl<'cfg> RegistryIndex<'cfg> {
435487
.versions
436488
.iter_mut()
437489
.filter_map(move |(k, v)| if req.matches(k) { Some(v) } else { None })
438-
.filter_map(move |maybe| match maybe.parse(raw_data, source_id) {
439-
Ok(summary) => Some(summary),
440-
Err(e) => {
441-
info!("failed to parse `{}` registry package: {}", name, e);
442-
None
443-
}
444-
})
445-
.filter(move |is| {
446-
if is.v == 3 && bindeps {
447-
true
448-
} else if is.v > INDEX_V_MAX {
449-
debug!(
450-
"unsupported schema version {} ({} {})",
451-
is.v,
452-
is.summary.name(),
453-
is.summary.version()
454-
);
455-
false
456-
} else {
457-
true
490+
.filter_map(move |maybe| {
491+
match maybe.parse(raw_data, source_id, bindeps) {
492+
Ok(sum @ IndexSummary::Candidate(_) | sum @ IndexSummary::Yanked(_)) => {
493+
Some(sum)
494+
}
495+
Ok(IndexSummary::Unsupported(summary, v)) => {
496+
debug!(
497+
"unsupported schema version {} ({} {})",
498+
v,
499+
summary.name(),
500+
summary.version()
501+
);
502+
None
503+
}
504+
Ok(IndexSummary::Offline(_)) => {
505+
unreachable!("We do not check for off-line until later")
506+
}
507+
Err(e) => {
508+
info!("failed to parse `{}` registry package: {}", name, e);
509+
None
510+
}
458511
}
459512
})))
460513
}
@@ -518,7 +571,7 @@ impl<'cfg> RegistryIndex<'cfg> {
518571
/// This is primarily used by [`Source::query`](super::Source).
519572
pub fn query_inner(
520573
&mut self,
521-
name: &str,
574+
name: InternedString,
522575
req: &OptVersionReq,
523576
load: &mut dyn RegistryData,
524577
yanked_whitelist: &HashSet<PackageId>,
@@ -535,14 +588,35 @@ impl<'cfg> RegistryIndex<'cfg> {
535588
// then cargo will fail to download and an error message
536589
// indicating that the required dependency is unavailable while
537590
// offline will be displayed.
538-
if ready!(self.query_inner_with_online(name, req, load, yanked_whitelist, f, false)?)
539-
> 0
540-
{
591+
let mut called = false;
592+
let callback = &mut |s: IndexSummary| {
593+
if !s.is_offline() {
594+
called = true;
595+
f(s.into_summary());
596+
}
597+
};
598+
ready!(self.query_inner_with_online(
599+
name,
600+
req,
601+
load,
602+
yanked_whitelist,
603+
callback,
604+
false
605+
)?);
606+
if called {
541607
return Poll::Ready(Ok(()));
542608
}
543609
}
544-
self.query_inner_with_online(name, req, load, yanked_whitelist, f, true)
545-
.map_ok(|_| ())
610+
self.query_inner_with_online(
611+
name,
612+
req,
613+
load,
614+
yanked_whitelist,
615+
&mut |s| {
616+
f(s.into_summary());
617+
},
618+
true,
619+
)
546620
}
547621

548622
/// Inner implementation of [`Self::query_inner`]. Returns the number of
@@ -551,13 +625,13 @@ impl<'cfg> RegistryIndex<'cfg> {
551625
/// The `online` controls whether Cargo can access the network when needed.
552626
fn query_inner_with_online(
553627
&mut self,
554-
name: &str,
628+
name: InternedString,
555629
req: &OptVersionReq,
556630
load: &mut dyn RegistryData,
557631
yanked_whitelist: &HashSet<PackageId>,
558-
f: &mut dyn FnMut(Summary),
632+
f: &mut dyn FnMut(IndexSummary),
559633
online: bool,
560-
) -> Poll<CargoResult<usize>> {
634+
) -> Poll<CargoResult<()>> {
561635
let source_id = self.source_id;
562636

563637
let summaries = ready!(self.summaries(name, req, load))?;
@@ -573,23 +647,28 @@ impl<'cfg> RegistryIndex<'cfg> {
573647
// does not satisfy the requirements, then resolution will
574648
// fail. Unfortunately, whether or not something is optional
575649
// is not known here.
576-
.filter(|s| (online || load.is_crate_downloaded(s.summary.package_id())))
650+
.map(|s| {
651+
if online || load.is_crate_downloaded(s.package_id()) {
652+
s.clone()
653+
} else {
654+
IndexSummary::Offline(s.as_summary().clone())
655+
}
656+
})
577657
// Next filter out all yanked packages. Some yanked packages may
578658
// leak through if they're in a whitelist (aka if they were
579659
// previously in `Cargo.lock`
580-
.filter(|s| !s.yanked || yanked_whitelist.contains(&s.summary.package_id()))
581-
.map(|s| s.summary.clone());
660+
.filter(|s| !s.is_yanked() || yanked_whitelist.contains(&s.package_id()));
582661

583662
// Handle `cargo update --precise` here.
584-
let precise = source_id.precise_registry_version(name);
663+
let precise = source_id.precise_registry_version(name.as_str());
585664
let summaries = summaries.filter(|s| match &precise {
586665
Some((current, requested)) => {
587666
if req.matches(current) {
588667
// Unfortunately crates.io allows versions to differ only
589668
// by build metadata. This shouldn't be allowed, but since
590669
// it is, this will honor it if requested. However, if not
591670
// specified, then ignore it.
592-
let s_vers = s.version();
671+
let s_vers = s.package_id().version();
593672
match (s_vers.build.is_empty(), requested.build.is_empty()) {
594673
(true, true) => s_vers == requested,
595674
(true, false) => false,
@@ -609,12 +688,8 @@ impl<'cfg> RegistryIndex<'cfg> {
609688
None => true,
610689
});
611690

612-
let mut count = 0;
613-
for summary in summaries {
614-
f(summary);
615-
count += 1;
616-
}
617-
Poll::Ready(Ok(count))
691+
summaries.for_each(f);
692+
Poll::Ready(Ok(()))
618693
}
619694

620695
/// Looks into the summaries to check if a package has been yanked.
@@ -624,9 +699,9 @@ impl<'cfg> RegistryIndex<'cfg> {
624699
load: &mut dyn RegistryData,
625700
) -> Poll<CargoResult<bool>> {
626701
let req = OptVersionReq::exact(pkg.version());
627-
let found = ready!(self.summaries(&pkg.name(), &req, load))?
628-
.filter(|s| s.summary.version() == pkg.version())
629-
.any(|summary| summary.yanked);
702+
let found = ready!(self.summaries(pkg.name(), &req, load))?
703+
.filter(|s| s.package_id().version() == pkg.version())
704+
.any(|s| s.is_yanked());
630705
Poll::Ready(Ok(found))
631706
}
632707
}
@@ -682,6 +757,8 @@ impl Summaries {
682757

683758
let response = ready!(load.load(root, relative, index_version.as_deref())?);
684759

760+
let bindeps = config.cli_unstable().bindeps;
761+
685762
match response {
686763
LoadResponse::CacheValid => {
687764
tracing::debug!("fast path for registry cache of {:?}", relative);
@@ -712,7 +789,7 @@ impl Summaries {
712789
// allow future cargo implementations to break the
713790
// interpretation of each line here and older cargo will simply
714791
// ignore the new lines.
715-
let summary = match IndexSummary::parse(line, source_id) {
792+
let summary = match IndexSummary::parse(line, source_id, bindeps) {
716793
Ok(summary) => summary,
717794
Err(e) => {
718795
// This should only happen when there is an index
@@ -731,7 +808,7 @@ impl Summaries {
731808
continue;
732809
}
733810
};
734-
let version = summary.summary.package_id().version().clone();
811+
let version = summary.package_id().version().clone();
735812
cache.versions.push((version.clone(), line));
736813
ret.versions.insert(version, summary.into());
737814
}
@@ -865,12 +942,17 @@ impl MaybeIndexSummary {
865942
/// Does nothing if this is already `Parsed`, and otherwise the `raw_data`
866943
/// passed in is sliced with the bounds in `Unparsed` and then actually
867944
/// parsed.
868-
fn parse(&mut self, raw_data: &[u8], source_id: SourceId) -> CargoResult<&IndexSummary> {
945+
fn parse(
946+
&mut self,
947+
raw_data: &[u8],
948+
source_id: SourceId,
949+
bindeps: bool,
950+
) -> CargoResult<&IndexSummary> {
869951
let (start, end) = match self {
870952
MaybeIndexSummary::Unparsed { start, end } => (*start, *end),
871953
MaybeIndexSummary::Parsed(summary) => return Ok(summary),
872954
};
873-
let summary = IndexSummary::parse(&raw_data[start..end], source_id)?;
955+
let summary = IndexSummary::parse(&raw_data[start..end], source_id, bindeps)?;
874956
*self = MaybeIndexSummary::Parsed(summary);
875957
match self {
876958
MaybeIndexSummary::Unparsed { .. } => unreachable!(),
@@ -891,7 +973,7 @@ impl IndexSummary {
891973
///
892974
/// The `line` provided is expected to be valid JSON. It is supposed to be
893975
/// a [`IndexPackage`].
894-
fn parse(line: &[u8], source_id: SourceId) -> CargoResult<IndexSummary> {
976+
fn parse(line: &[u8], source_id: SourceId, bindeps: bool) -> CargoResult<IndexSummary> {
895977
// ****CAUTION**** Please be extremely careful with returning errors
896978
// from this function. Entries that error are not included in the
897979
// index cache, and can cause cargo to get confused when switching
@@ -924,11 +1006,20 @@ impl IndexSummary {
9241006
}
9251007
let mut summary = Summary::new(pkgid, deps, &features, links, rust_version)?;
9261008
summary.set_checksum(cksum);
927-
Ok(IndexSummary {
928-
summary,
929-
yanked: yanked.unwrap_or(false),
930-
v,
931-
})
1009+
1010+
let v_max = if bindeps {
1011+
INDEX_V_MAX + 1
1012+
} else {
1013+
INDEX_V_MAX
1014+
};
1015+
1016+
if v_max < v {
1017+
Ok(IndexSummary::Unsupported(summary, v))
1018+
} else if yanked.unwrap_or(false) {
1019+
Ok(IndexSummary::Yanked(summary))
1020+
} else {
1021+
Ok(IndexSummary::Candidate(summary))
1022+
}
9321023
}
9331024
}
9341025

0 commit comments

Comments
 (0)