db_dump/
versions.rs

1//! <b style="font-variant:small-caps">versions.csv</b>
2
3use 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/// Primary key of **versions.csv**.
18#[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/// One row of **versions.csv**.
24#[derive(Clone, Debug)]
25#[non_exhaustive]
26pub struct Row {
27    /// PRIMARY KEY
28    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}