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}