cargo_index_transit/index.rs
1use serde::{Deserialize, Serialize};
2use std::borrow::Cow;
3use std::collections::BTreeMap;
4use std::fmt::Debug;
5use std::sync::Arc;
6
7/// A single line in the index representing a single version of a package.
8#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
9pub struct Entry<Name, Version, Req, Feature, Target, Links>
10where
11 Feature: Ord,
12{
13 pub name: Name,
14 #[serde(rename = "vers")]
15 pub version: Version,
16
17 // These are Arc so that they can be deduplicated easily by calling code if they happen to be
18 // reading it all of the versions of a single crate at once (as nearby versions often share
19 // dependency and feature lists).
20 #[serde(rename = "deps")]
21 pub dependencies: Arc<[RegistryDependency<Name, Req, Feature, Target>]>,
22
23 pub features: Arc<BTreeMap<Feature, Vec<Feature>>>,
24
25 /// This field contains features with new, extended syntax. Specifically,
26 /// namespaced features (`dep:`) and weak dependencies (`pkg?/feat`).
27 ///
28 /// This is separated from `features` because versions older than 1.19
29 /// will fail to load due to not being able to parse the new syntax, even
30 /// with a `Cargo.lock` file.
31 ///
32 /// It's wrapped in a `Box` to reduce size of the struct when the field is unused (i.e. almost
33 /// always).
34 /// <https://rust-lang.github.io/rfcs/3143-cargo-weak-namespaced-features.html#index-changes>
35 #[serde(default, skip_serializing_if = "Option::is_none")]
36 pub features2: Option<Box<BTreeMap<Feature, Vec<Feature>>>>,
37
38 #[serde(with = "hex")]
39 #[serde(rename = "cksum")]
40 pub checksum: [u8; 32],
41
42 /// If `true`, Cargo will skip this version when resolving.
43 #[serde(default)]
44 pub yanked: bool,
45
46 /// Native library name this package links to.
47 ///
48 /// Added early 2018 (see <https://github.com/rust-lang/cargo/pull/4978>),
49 /// can be `None` if published before then.
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub links: Option<Links>,
52
53 /// The schema version for this entry.
54 ///
55 /// If this is None, it defaults to version 1. Entries with unknown
56 /// versions are ignored.
57 ///
58 /// Version `2` format adds the `features2` field.
59 ///
60 /// This provides a method to safely introduce changes to index entries
61 /// and allow older versions of cargo to ignore newer entries it doesn't
62 /// understand. This is honored as of 1.51, so unfortunately older
63 /// versions will ignore it, and potentially misinterpret version 2 and
64 /// newer entries.
65 ///
66 /// The intent is that versions older than 1.51 will work with a
67 /// pre-existing `Cargo.lock`, but they may not correctly process `cargo
68 /// update` or build a lock from scratch. In that case, cargo may
69 /// incorrectly select a new package that uses a new index format. A
70 /// workaround is to downgrade any packages that are incompatible with the
71 /// `--precise` flag of `cargo update`.
72 #[serde(skip_serializing_if = "Option::is_none")]
73 #[serde(rename = "v")]
74 pub schema_version: Option<u8>,
75}
76
77impl<'a>
78 Entry<
79 Cow<'a, str>,
80 semver::Version,
81 semver::VersionReq,
82 Cow<'a, str>,
83 Cow<'a, str>,
84 Cow<'a, str>,
85 >
86{
87 pub fn from_manifest<Name, Feature: Ord>(
88 v: super::dotcrate::NormalizedManifest<Name, Feature>,
89 via_registry: &'_ str,
90 checksum: [u8; 32],
91 ) -> Self
92 where
93 Name: Into<Cow<'a, str>>,
94 Feature: Into<Cow<'a, str>>,
95 {
96 let in_registry = super::publish::CrateVersion::new(v, (None, None), via_registry);
97 Self::from_publish(in_registry, checksum)
98 }
99
100 pub fn from_publish(v: super::publish::CrateVersion<'a>, checksum: [u8; 32]) -> Self {
101 let (features, features2): (BTreeMap<_, _>, BTreeMap<_, _>) =
102 v.features.into_iter().partition(|(_k, vals)| {
103 !vals
104 .iter()
105 .any(|v| v.starts_with("dep:") || v.contains("?/"))
106 });
107 let (features2, schema_version) = if features2.is_empty() {
108 (None, None)
109 } else {
110 (Some(features2), Some(2))
111 };
112
113 Self {
114 name: v.name,
115 version: v.version,
116 dependencies: Arc::from(
117 v.dependencies
118 .into_iter()
119 .map(|d| {
120 let (name, package) = match (d.name, d.explicit_name_in_toml) {
121 (p, Some(n)) => (n, Some(Box::new(p))),
122 (n, None) => (n, None),
123 };
124 RegistryDependency {
125 name,
126 kind: Some(d.kind),
127 requirements: d.requirements,
128 features: Box::new(d.features.into_boxed_slice()),
129 optional: d.optional,
130 default_features: d.default_features,
131 target: d.target.map(Box::new),
132 registry: d
133 .registry
134 .map(|r| r.into_owned().into_boxed_str())
135 .map(Box::new),
136 package,
137 public: None,
138 }
139 })
140 .collect::<Vec<_>>()
141 .into_boxed_slice(),
142 ),
143 features: Arc::new(features),
144 features2: features2.map(Box::new),
145 checksum,
146 yanked: false,
147 links: v.links.map(Into::into),
148 schema_version,
149 }
150 }
151}
152
153/// A dependency as encoded in the index JSON.
154#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord)]
155pub struct RegistryDependency<Name, Req, Feature, Target> {
156 // In old `cargo` versions the dependency order appears to matter if the same dependency exists
157 // twice but with different `kind` fields. In those cases the `optional` field can sometimes be
158 // ignored or misinterpreted. By placing the fields in this order, we ensure that `normal`
159 // dependencies are always first when multiple with the same `name` exist.
160 pub name: Name,
161
162 #[serde(skip_serializing_if = "Option::is_none")]
163 pub kind: Option<super::publish::DependencyKind>,
164
165 #[serde(rename = "req")]
166 pub requirements: Req,
167
168 pub features: Box<Box<[Feature]>>,
169 pub optional: bool,
170 pub default_features: bool,
171 #[serde(skip_serializing_if = "Option::is_none")]
172 pub target: Option<Box<Target>>,
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub registry: Option<Box<Box<str>>>,
175 #[serde(skip_serializing_if = "Option::is_none")]
176 pub package: Option<Box<Name>>,
177 #[serde(skip_serializing_if = "Option::is_none")]
178 pub public: Option<bool>,
179}