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}