#
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 publish time for the package.  Unstable.
    50    ///
    51    /// In ISO8601 with UTC timezone (e.g. 2025-11-12T19:30:12Z)
    52    pub pubtime: Option<String>,
    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` schema adds the `features2` field.
    59    ///
    60    /// Version `3` schema adds `artifact`, `bindep_targes`, and `lib` for
    61    /// artifact dependencies support.
    62    ///
    63    /// This provides a method to safely introduce changes to index entries
    64    /// and allow older versions of cargo to ignore newer entries it doesn't
    65    /// understand. This is honored as of 1.51, so unfortunately older
    66    /// versions will ignore it, and potentially misinterpret version 2 and
    67    /// newer entries.
    68    ///
    69    /// The intent is that versions older than 1.51 will work with a
    70    /// pre-existing `Cargo.lock`, but they may not correctly process `cargo
    71    /// update` or build a lock from scratch. In that case, cargo may
    72    /// incorrectly select a new package that uses a new index schema. A
    73    /// workaround is to downgrade any packages that are incompatible with the
    74    /// `--precise` flag of `cargo update`.
    75    pub v: Option<u32>,
    76}
    77
    78/// A dependency as encoded in the [`IndexPackage`] index JSON.
    79#[derive(Deserialize, Serialize, Clone)]
    80#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    81pub struct RegistryDependency<'a> {
    82    /// Name of the dependency. If the dependency is renamed, the original
    83    /// would be stored in [`RegistryDependency::package`].
    84    #[serde(borrow)]
    85    pub name: Cow<'a, str>,
    86    /// The SemVer requirement for this dependency.
    87    #[serde(borrow)]
    88    pub req: Cow<'a, str>,
    89    /// Set of features enabled for this dependency.
    90    #[serde(default)]
    91    pub features: Vec<Cow<'a, str>>,
    92    /// Whether or not this is an optional dependency.
    93    #[serde(default)]
    94    pub optional: bool,
    95    /// Whether or not default features are enabled.
    96    #[serde(default = "default_true")]
    97    pub default_features: bool,
    98    /// The target platform for this dependency.
    99    pub target: Option<Cow<'a, str>>,
    100    /// The dependency kind. "dev", "build", and "normal".
    101    pub kind: Option<Cow<'a, str>>,
    102    /// The URL of the index of the registry where this dependency is from.
    103    /// `None` if it is from the same index.
    104    pub registry: Option<Cow<'a, str>>,
    105    /// The original name if the dependency is renamed.
    106    pub package: Option<Cow<'a, str>>,
    107    /// Whether or not this is a public dependency. Unstable. See [RFC 1977].
    108    ///
    109    /// [RFC 1977]: https://rust-lang.github.io/rfcs/1977-public-private-dependencies.html
    110    pub public: Option<bool>,
    111    /// The artifacts to build from this dependency.
    112    pub artifact: Option<Vec<Cow<'a, str>>>,
    113    /// The target for bindep.
    114    pub bindep_target: Option<Cow<'a, str>>,
    115    /// Whether or not this is a library dependency.
    116    #[serde(default)]
    117    pub lib: bool,
    118}
    119
    120fn default_true() -> bool {
    121    true
    122}
    123
    124#[test]
    125fn escaped_char_in_index_json_blob() {
    126    let _: IndexPackage<'_> = serde_json::from_str(
    127        r#"{"name":"a","vers":"0.0.1","deps":[],"cksum":"bae3","features":{}}"#,
    128    )
    129    .unwrap();
    130    let _: IndexPackage<'_> = serde_json::from_str(
    131        r#"{"name":"a","vers":"0.0.1","deps":[],"cksum":"bae3","features":{"test":["k","q"]},"links":"a-sys"}"#
    132    ).unwrap();
    133
    134    // Now we add escaped cher all the places they can go
    135    // these are not valid, but it should error later than json parsing
    136    let _: IndexPackage<'_> = serde_json::from_str(
    137        r#"{
    138        "name":"This name has a escaped cher in it \n\t\" ",
    139        "vers":"0.0.1",
    140        "deps":[{
    141            "name": " \n\t\" ",
    142            "req": " \n\t\" ",
    143            "features": [" \n\t\" "],
    144            "optional": true,
    145            "default_features": true,
    146            "target": " \n\t\" ",
    147            "kind": " \n\t\" ",
    148            "registry": " \n\t\" "
    149        }],
    150        "cksum":"bae3",
    151        "features":{"test \n\t\" ":["k \n\t\" ","q \n\t\" "]},
    152        "links":" \n\t\" "}"#,
    153    )
    154    .unwrap();
    155}
    156
    157#[cfg(feature = "unstable-schema")]
    158#[test]
    159fn dump_index_schema() {
    160    let schema = schemars::schema_for!(crate::index::IndexPackage<'_>);
    161    let dump = serde_json::to_string_pretty(&schema).unwrap();
    162    snapbox::assert_data_eq!(dump, snapbox::file!("../index.schema.json").raw());
    163}