|
| 1 | +use crate::manifest::RustVersion; |
| 2 | +use semver::Version; |
| 3 | +use serde::{Deserialize, Serialize}; |
| 4 | +use 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))] |
| 9 | +pub 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))] |
| 77 | +pub 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 | + |
| 116 | +fn default_true() -> bool { |
| 117 | + true |
| 118 | +} |
| 119 | + |
| 120 | +#[test] |
| 121 | +fn 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] |
| 155 | +fn 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 | +} |
0 commit comments