1use crate::crates::CrateId;
4use crate::ignore::IgnoredStr;
5use crate::users::UserId;
6use chrono::{DateTime, Utc};
7use semver::{BuildMetadata, Op, Version, VersionReq};
8use serde::de::{Deserialize, Deserializer, Unexpected, Visitor};
9use serde_derive::{Deserialize, Serialize};
10use std::borrow::Borrow;
11use std::cmp::Ordering;
12use std::collections::BTreeMap as Map;
13use std::fmt;
14use std::hash::{Hash, Hasher};
15use std::str::FromStr;
16
17#[derive(Serialize, Deserialize, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
19#[serde(transparent)]
20#[cfg_attr(not(doc), repr(transparent))]
21pub struct VersionId(pub u32);
22
23#[derive(Clone, Debug)]
25#[non_exhaustive]
26pub struct Row {
27 pub id: VersionId,
29 pub crate_id: CrateId,
30 pub num: Version,
31 pub updated_at: DateTime<Utc>,
32 pub created_at: DateTime<Utc>,
33 pub downloads: u64,
34 pub features: Map<String, Vec<String>>,
35 pub yanked: bool,
36 pub license: String,
37 pub crate_size: Option<u64>,
38 pub published_by: Option<UserId>,
39 pub checksum: Option<[u8; 32]>,
40 pub links: Option<String>,
41 pub rust_version: Option<Version>,
42 pub has_lib: bool,
43 pub bin_names: Vec<String>,
44 pub edition: Option<u16>,
45 pub description: Option<String>,
46 pub homepage: Option<String>,
47 pub documentation: Option<String>,
48 pub repository: Option<String>,
49 pub categories: Vec<String>,
50 pub keywords: Vec<String>,
51}
52
53impl<'de> Deserialize<'de> for Row {
54 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
55 where
56 D: Deserializer<'de>,
57 {
58 #[derive(Deserialize)]
59 #[serde(deny_unknown_fields)]
60 struct Row {
61 id: VersionId,
62 crate_id: CrateId,
63 #[serde(deserialize_with = "version")]
64 num: Version,
65 #[serde(default)]
66 #[allow(dead_code)]
67 num_no_build: IgnoredStr,
68 #[serde(deserialize_with = "crate::datetime::de")]
69 updated_at: DateTime<Utc>,
70 #[serde(deserialize_with = "crate::datetime::de")]
71 created_at: DateTime<Utc>,
72 downloads: u64,
73 #[serde(deserialize_with = "features_map")]
74 features: Map<String, Vec<String>>,
75 #[serde(deserialize_with = "crate::bool::de")]
76 yanked: bool,
77 license: String,
78 crate_size: Option<u64>,
79 published_by: Option<UserId>,
80 #[serde(deserialize_with = "checksum", default)]
81 checksum: Option<[u8; 32]>,
82 #[serde(default)]
83 links: Option<String>,
84 #[serde(default, deserialize_with = "rust_version")]
85 rust_version: Option<Version>,
86 #[serde(default, deserialize_with = "has_lib")]
87 has_lib: bool,
88 #[serde(default, deserialize_with = "bin_names")]
89 bin_names: Vec<String>,
90 edition: Option<u16>,
91 description: Option<String>,
92 homepage: Option<String>,
93 documentation: Option<String>,
94 repository: Option<String>,
95 #[serde(default, deserialize_with = "categories")]
96 categories: Vec<String>,
97 #[serde(default, deserialize_with = "keywords")]
98 keywords: Vec<String>,
99 }
100
101 let Row {
102 id,
103 crate_id,
104 num,
105 num_no_build: _,
106 updated_at,
107 created_at,
108 downloads,
109 features,
110 yanked,
111 license,
112 crate_size,
113 published_by,
114 checksum,
115 links,
116 rust_version,
117 has_lib,
118 bin_names,
119 edition,
120 description,
121 homepage,
122 documentation,
123 repository,
124 categories,
125 keywords,
126 } = Row::deserialize(deserializer)?;
127 Ok(Self {
128 id,
129 crate_id,
130 num,
131 updated_at,
132 created_at,
133 downloads,
134 features,
135 yanked,
136 license,
137 crate_size,
138 published_by,
139 checksum,
140 links,
141 rust_version,
142 has_lib,
143 bin_names,
144 edition,
145 description,
146 homepage,
147 documentation,
148 repository,
149 categories,
150 keywords,
151 })
152 }
153}
154
155impl Ord for Row {
156 fn cmp(&self, other: &Self) -> Ordering {
157 VersionId::cmp(&self.id, &other.id)
158 }
159}
160
161impl PartialOrd for Row {
162 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
163 Some(self.cmp(other))
164 }
165}
166
167impl Eq for Row {}
168
169impl PartialEq for Row {
170 fn eq(&self, other: &Self) -> bool {
171 VersionId::eq(&self.id, &other.id)
172 }
173}
174
175impl Hash for Row {
176 fn hash<H: Hasher>(&self, state: &mut H) {
177 VersionId::hash(&self.id, state);
178 }
179}
180
181impl Borrow<VersionId> for Row {
182 fn borrow(&self) -> &VersionId {
183 &self.id
184 }
185}
186
187fn compat(string: &str) -> Option<Version> {
188 let deprecated = match string {
189 "0.0.1-001" => "0.0.1-1",
190 "0.3.0-alpha.01" => "0.3.0-alpha.1",
191 "0.4.0-alpha.00" => "0.4.0-alpha.0",
192 "0.4.0-alpha.01" => "0.4.0-alpha.1",
193 _ => return None,
194 };
195 Some(deprecated.parse().unwrap())
196}
197
198struct VersionVisitor;
199
200impl<'de> Visitor<'de> for VersionVisitor {
201 type Value = Version;
202
203 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
204 formatter.write_str("semver version")
205 }
206
207 fn visit_str<E>(self, string: &str) -> Result<Self::Value, E>
208 where
209 E: serde::de::Error,
210 {
211 match string.parse() {
212 Ok(version) => Ok(version),
213 Err(err) => {
214 if let Some(version) = compat(string) {
215 Ok(version)
216 } else {
217 Err(serde::de::Error::custom(format_args!(
218 "{}: {}",
219 err, string,
220 )))
221 }
222 }
223 }
224 }
225}
226
227fn version<'de, D>(deserializer: D) -> Result<Version, D::Error>
228where
229 D: Deserializer<'de>,
230{
231 deserializer.deserialize_str(VersionVisitor)
232}
233
234struct FeaturesMapVisitor;
235
236impl<'de> Visitor<'de> for FeaturesMapVisitor {
237 type Value = Map<String, Vec<String>>;
238
239 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
240 formatter.write_str("features map")
241 }
242
243 fn visit_str<E>(self, string: &str) -> Result<Self::Value, E>
244 where
245 E: serde::de::Error,
246 {
247 serde_json::from_str(string).map_err(serde::de::Error::custom)
248 }
249}
250
251fn features_map<'de, D>(deserializer: D) -> Result<Map<String, Vec<String>>, D::Error>
252where
253 D: Deserializer<'de>,
254{
255 deserializer.deserialize_str(FeaturesMapVisitor)
256}
257
258struct ChecksumVisitor;
259
260impl<'de> Visitor<'de> for ChecksumVisitor {
261 type Value = Option<[u8; 32]>;
262
263 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
264 formatter.write_str("checksum as 64-character hex string")
265 }
266
267 fn visit_str<E>(self, string: &str) -> Result<Self::Value, E>
268 where
269 E: serde::de::Error,
270 {
271 match string.len() {
272 0 => Ok(None),
273 64 => {
274 let mut checksum = [0u8; 32];
275 for i in 0..32 {
276 match u8::from_str_radix(&string[i * 2..][..2], 16) {
277 Ok(byte) => checksum[i] = byte,
278 Err(_) => return Err(E::invalid_value(Unexpected::Str(string), &self)),
279 }
280 }
281 Ok(Some(checksum))
282 }
283 _ => Err(E::invalid_value(Unexpected::Str(string), &self)),
284 }
285 }
286}
287
288fn checksum<'de, D>(deserializer: D) -> Result<Option<[u8; 32]>, D::Error>
289where
290 D: Deserializer<'de>,
291{
292 deserializer.deserialize_str(ChecksumVisitor)
293}
294
295struct RustVersionVisitor;
296
297impl<'de> Visitor<'de> for RustVersionVisitor {
298 type Value = Option<Version>;
299
300 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
301 formatter.write_str("a compiler version number")
302 }
303
304 fn visit_str<E>(self, string: &str) -> Result<Self::Value, E>
305 where
306 E: serde::de::Error,
307 {
308 match VersionReq::from_str(string) {
309 Ok(mut req) if req.comparators.len() == 1 => {
310 let req = req.comparators.pop().unwrap();
311 if req.op == Op::Caret {
312 Ok(Some(Version {
313 major: req.major,
314 minor: req.minor.unwrap_or(0),
315 patch: req.patch.unwrap_or(0),
316 pre: req.pre,
317 build: BuildMetadata::EMPTY,
318 }))
319 } else {
320 Ok(None)
321 }
322 }
323 Ok(_) => Ok(None),
324 Err(parse_error) => Err(E::custom(parse_error)),
325 }
326 }
327
328 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
329 where
330 D: Deserializer<'de>,
331 {
332 deserializer.deserialize_str(self)
333 }
334
335 fn visit_none<E>(self) -> Result<Self::Value, E>
336 where
337 E: serde::de::Error,
338 {
339 Ok(None)
340 }
341}
342
343fn rust_version<'de, D>(deserializer: D) -> Result<Option<Version>, D::Error>
344where
345 D: Deserializer<'de>,
346{
347 deserializer.deserialize_option(RustVersionVisitor)
348}
349
350#[derive(Deserialize)]
351#[serde(transparent)]
352struct HasLib(#[serde(deserialize_with = "crate::bool::de")] bool);
353
354fn has_lib<'de, D>(deserializer: D) -> Result<bool, D::Error>
355where
356 D: Deserializer<'de>,
357{
358 match Deserialize::deserialize(deserializer)? {
359 Some(HasLib(has_lib)) => Ok(has_lib),
360 None => Ok(false),
361 }
362}
363
364fn bin_names<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
365where
366 D: Deserializer<'de>,
367{
368 crate::set::optional(deserializer, "binary names set")
369}
370
371fn categories<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
372where
373 D: Deserializer<'de>,
374{
375 crate::set::de(deserializer, "categories set")
376}
377
378fn keywords<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
379where
380 D: Deserializer<'de>,
381{
382 crate::set::de(deserializer, "keywords set")
383}