crates_index_diff/
types.rs

1use std::collections::HashMap;
2
3use bstr::BString;
4use smartstring::alias::String as SmolString;
5use std::hash::Hash;
6use std::{fmt, slice};
7
8/// A wrapper for a repository of the crates.io index.
9pub struct Index {
10    /// The name and path of the reference used to keep track of the last seen state of the
11    /// crates.io repository. The default value is `refs/heads/crates-index-diff_last-seen`.
12    pub seen_ref_name: &'static str,
13    /// The name of the branch to fetch. This value also affects the tracking branch.
14    pub branch_name: &'static str,
15    /// The name of the symbolic name of the remote to fetch from.
16    /// If `None`, obtain the remote name from the configuration of the currently checked-out branch.
17    pub remote_name: Option<BString>,
18    /// The git repository to use for diffing
19    pub(crate) repo: gix::Repository,
20}
21
22/// Identify a kind of change that occurred to a crate
23#[derive(Clone, Eq, PartialEq, Debug)]
24pub enum Change {
25    /// A crate version was added.
26    Added(CrateVersion),
27    /// A crate version was unyanked.
28    Unyanked(CrateVersion),
29    /// A crate version was added in a yanked state.
30    ///
31    /// This can happen if we don't see the commit that added them, so it appears to pop into existence yanked.
32    /// Knowing this should help to trigger the correct action, as simply `Yanked` crates would be treated quite differently.
33    AddedAndYanked(CrateVersion),
34    /// A crate version was yanked.
35    Yanked(CrateVersion),
36    /// The name of the crate whose file was deleted, which implies all versions were deleted as well.
37    CrateDeleted {
38        /// The name of the deleted crate.
39        name: String,
40        /// All of its versions that were deleted along with the file.
41        versions: Vec<CrateVersion>,
42    },
43    /// A crate version was deleted.
44    ///
45    /// Note that this is equivalent to deleting a line from a crates version file.
46    /// Should more than one lines be removed per commit, the order of these changes is nondeterministic.
47    VersionDeleted(CrateVersion),
48}
49
50impl Change {
51    /// Return the added crate, if this is this kind of change.
52    pub fn added(&self) -> Option<&CrateVersion> {
53        match self {
54            Change::Added(v) | Change::AddedAndYanked(v) => Some(v),
55            _ => None,
56        }
57    }
58
59    /// Return the yanked crate, if this is this kind of change.
60    pub fn yanked(&self) -> Option<&CrateVersion> {
61        match self {
62            Change::Yanked(v) | Change::AddedAndYanked(v) => Some(v),
63            _ => None,
64        }
65    }
66
67    /// Return the unyanked crate, if this is this kind of change.
68    pub fn unyanked(&self) -> Option<&CrateVersion> {
69        match self {
70            Change::Unyanked(v) => Some(v),
71            _ => None,
72        }
73    }
74
75    /// Return the deleted crate, if this is this kind of change.
76    pub fn crate_deleted(&self) -> Option<(&str, &[CrateVersion])> {
77        match self {
78            Change::CrateDeleted { name, versions } => Some((name.as_str(), versions)),
79            _ => None,
80        }
81    }
82
83    /// Return the deleted version crate, if this is this kind of change.
84    pub fn version_deleted(&self) -> Option<&CrateVersion> {
85        match self {
86            Change::VersionDeleted(v) => Some(v),
87            _ => None,
88        }
89    }
90
91    /// Returns all versions affected by this change.
92    ///
93    /// The returned slice usually has length 1.
94    /// However, if a crate was purged from the index by an admin,
95    /// all versions of the purged crate are returned.
96    pub fn versions(&self) -> &[CrateVersion] {
97        match self {
98            Change::Added(v)
99            | Change::Unyanked(v)
100            | Change::AddedAndYanked(v)
101            | Change::Yanked(v)
102            | Change::VersionDeleted(v) => slice::from_ref(v),
103            Change::CrateDeleted { versions, .. } => versions,
104        }
105    }
106}
107
108impl fmt::Display for Change {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        write!(
111            f,
112            "{}",
113            match *self {
114                Change::Added(_) => "added",
115                Change::Yanked(_) => "yanked",
116                Change::CrateDeleted { .. } => "crate deleted",
117                Change::VersionDeleted(_) => "version deleted",
118                Change::Unyanked(_) => "unyanked",
119                Change::AddedAndYanked(_) => "added and yanked",
120            }
121        )
122    }
123}
124
125/// Section in which a dependency was defined in.
126#[derive(
127    Debug, Copy, Clone, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash, Ord, PartialOrd,
128)]
129#[serde(rename_all = "lowercase")]
130pub enum DependencyKind {
131    /// Used for production builds.
132    Normal,
133    /// Used only for tests and examples.
134    Dev,
135    /// Used in build scripts.
136    Build,
137}
138
139/// Pack all information we know about a change made to a version of a crate.
140#[derive(Default, Clone, serde::Serialize, serde::Deserialize, Eq, PartialEq, Debug)]
141pub struct CrateVersion {
142    /// The crate name, i.e. `clap`.
143    pub name: SmolString,
144    /// is the release yanked?
145    pub yanked: bool,
146    /// The semantic version of the crate.
147    #[serde(rename = "vers")]
148    pub version: SmolString,
149    /// The checksum over the crate archive
150    #[serde(rename = "cksum", with = "hex")]
151    pub checksum: [u8; 32],
152    /// All cargo features
153    pub features: HashMap<String, Vec<String>>,
154    /// All crate dependencies
155    #[serde(rename = "deps")]
156    pub dependencies: Vec<Dependency>,
157}
158
159/// A single dependency of a specific crate version
160#[derive(
161    Clone, serde::Serialize, serde::Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug, Hash,
162)]
163pub struct Dependency {
164    /// The crate name
165    pub name: SmolString,
166    /// The version the parent crate requires of this dependency
167    #[serde(rename = "req")]
168    pub required_version: SmolString,
169    /// All cargo features configured by the parent crate
170    pub features: Vec<String>,
171    /// True if this is an optional dependency
172    pub optional: bool,
173    /// True if default features are enabled
174    pub default_features: bool,
175    /// The name of the build target
176    #[serde(skip_serializing_if = "Option::is_none")]
177    pub target: Option<SmolString>,
178    /// The kind of dependency, usually 'normal' or 'dev'
179    #[serde(skip_serializing_if = "Option::is_none")]
180    pub kind: Option<DependencyKind>,
181    /// The package this crate is contained in
182    #[serde(skip_serializing_if = "Option::is_none")]
183    pub package: Option<SmolString>,
184}