cargo_util_schemas/index.rs
1use crate::manifest::RustVersion;
2use semver::Version;
3use serde::{Deserialize, Serialize};
4use std::{borrow::Cow, collections::BTreeMap};
5
6/// A single line in the index representing a single version of a package.
7#[derive(Deserialize, Serialize)]
8#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
9pub struct IndexPackage<'a> {
10 /// Name of the package.
11 #[serde(borrow)]
12 pub name: Cow<'a, str>,
13 /// The version of this dependency.
14 pub vers: Version,
15 /// All kinds of direct dependencies of the package, including dev and
16 /// build dependencies.
17 #[serde(borrow)]
18 pub deps: Vec<RegistryDependency<'a>>,
19 /// Set of features defined for the package, i.e., `[features]` table.
20 #[serde(default)]
21 pub features: BTreeMap<Cow<'a, str>, Vec<Cow<'a, str>>>,
22 /// This field contains features with new, extended syntax. Specifically,
23 /// namespaced features (`dep:`) and weak dependencies (`pkg?/feat`).
24 ///
25 /// This is separated from `features` because versions older than 1.19
26 /// will fail to load due to not being able to parse the new syntax, even
27 /// with a `Cargo.lock` file.
28 pub features2: Option<BTreeMap<Cow<'a, str>, Vec<Cow<'a, str>>>>,
29 /// Checksum for verifying the integrity of the corresponding downloaded package.
30 pub cksum: String,
31 /// If `true`, Cargo will skip this version when resolving.
32 ///
33 /// This was added in 2014. Everything in the crates.io index has this set
34 /// now, so this probably doesn't need to be an option anymore.
35 pub yanked: Option<bool>,
36 /// Native library name this package links to.
37 ///
38 /// Added early 2018 (see <https://github.com/rust-lang/cargo/pull/4978>),
39 /// can be `None` if published before then.
40 pub links: Option<Cow<'a, str>>,
41 /// Required version of rust
42 ///
43 /// Corresponds to `package.rust-version`.
44 ///
45 /// Added in 2023 (see <https://github.com/rust-lang/crates.io/pull/6267>),
46 /// can be `None` if published before then or if not set in the manifest.
47 #[cfg_attr(feature = "unstable-schema", schemars(with = "Option<String>"))]
48 pub rust_version: Option<RustVersion>,
49 /// The schema version for this entry.
50 ///
51 /// If this is None, it defaults to version `1`. Entries with unknown
52 /// versions are ignored.
53 ///
54 /// Version `2` schema adds the `features2` field.
55 ///
56 /// Version `3` schema adds `artifact`, `bindep_targes`, and `lib` for
57 /// artifact dependencies support.
58 ///
59 /// This provides a method to safely introduce changes to index entries
60 /// and allow older versions of cargo to ignore newer entries it doesn't
61 /// understand. This is honored as of 1.51, so unfortunately older
62 /// versions will ignore it, and potentially misinterpret version 2 and
63 /// newer entries.
64 ///
65 /// The intent is that versions older than 1.51 will work with a
66 /// pre-existing `Cargo.lock`, but they may not correctly process `cargo
67 /// update` or build a lock from scratch. In that case, cargo may
68 /// incorrectly select a new package that uses a new index schema. A
69 /// workaround is to downgrade any packages that are incompatible with the
70 /// `--precise` flag of `cargo update`.
71 pub v: Option<u32>,
72}
73
74/// A dependency as encoded in the [`IndexPackage`] index JSON.
75#[derive(Deserialize, Serialize, Clone)]
76#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
77pub struct RegistryDependency<'a> {
78 /// Name of the dependency. If the dependency is renamed, the original
79 /// would be stored in [`RegistryDependency::package`].
80 #[serde(borrow)]
81 pub name: Cow<'a, str>,
82 /// The SemVer requirement for this dependency.
83 #[serde(borrow)]
84 pub req: Cow<'a, str>,
85 /// Set of features enabled for this dependency.
86 #[serde(default)]
87 pub features: Vec<Cow<'a, str>>,
88 /// Whether or not this is an optional dependency.
89 #[serde(default)]
90 pub optional: bool,
91 /// Whether or not default features are enabled.
92 #[serde(default = "default_true")]
93 pub default_features: bool,
94 /// The target platform for this dependency.
95 pub target: Option<Cow<'a, str>>,
96 /// The dependency kind. "dev", "build", and "normal".
97 pub kind: Option<Cow<'a, str>>,
98 /// The URL of the index of the registry where this dependency is from.
99 /// `None` if it is from the same index.
100 pub registry: Option<Cow<'a, str>>,
101 /// The original name if the dependency is renamed.
102 pub package: Option<Cow<'a, str>>,
103 /// Whether or not this is a public dependency. Unstable. See [RFC 1977].
104 ///
105 /// [RFC 1977]: https://rust-lang.github.io/rfcs/1977-public-private-dependencies.html
106 pub public: Option<bool>,
107 /// The artifacts to build from this dependency.
108 pub artifact: Option<Vec<Cow<'a, str>>>,
109 /// The target for bindep.
110 pub bindep_target: Option<Cow<'a, str>>,
111 /// Whether or not this is a library dependency.
112 #[serde(default)]
113 pub lib: bool,
114}
115
116fn default_true() -> bool {
117 true
118}
119
120#[test]
121fn escaped_char_in_index_json_blob() {
122 let _: IndexPackage<'_> = serde_json::from_str(
123 r#"{"name":"a","vers":"0.0.1","deps":[],"cksum":"bae3","features":{}}"#,
124 )
125 .unwrap();
126 let _: IndexPackage<'_> = serde_json::from_str(
127 r#"{"name":"a","vers":"0.0.1","deps":[],"cksum":"bae3","features":{"test":["k","q"]},"links":"a-sys"}"#
128 ).unwrap();
129
130 // Now we add escaped cher all the places they can go
131 // these are not valid, but it should error later than json parsing
132 let _: IndexPackage<'_> = serde_json::from_str(
133 r#"{
134 "name":"This name has a escaped cher in it \n\t\" ",
135 "vers":"0.0.1",
136 "deps":[{
137 "name": " \n\t\" ",
138 "req": " \n\t\" ",
139 "features": [" \n\t\" "],
140 "optional": true,
141 "default_features": true,
142 "target": " \n\t\" ",
143 "kind": " \n\t\" ",
144 "registry": " \n\t\" "
145 }],
146 "cksum":"bae3",
147 "features":{"test \n\t\" ":["k \n\t\" ","q \n\t\" "]},
148 "links":" \n\t\" "}"#,
149 )
150 .unwrap();
151}
152
153#[cfg(feature = "unstable-schema")]
154#[test]
155fn dump_index_schema() {
156 let schema = schemars::schema_for!(crate::index::IndexPackage<'_>);
157 let dump = serde_json::to_string_pretty(&schema).unwrap();
158 snapbox::assert_data_eq!(dump, snapbox::file!("../index.schema.json").raw());
159}