1mod dedupe;
4
5use crate::Error;
6use dedupe::DedupeContext;
7use semver::Version;
8use serde::{Deserialize, Serialize};
9use smol_str::SmolStr;
10use std::{collections::BTreeMap, sync::Arc};
11
12pub type FeatureMap = BTreeMap<String, Vec<String>>;
14
15#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
17pub struct IndexVersion {
18 pub name: SmolStr,
20 #[serde(rename = "vers")]
22 pub version: SmolStr,
23 pub deps: Arc<[IndexDependency]>,
25 #[serde(rename = "cksum")]
27 pub checksum: Chksum,
28 features: Arc<FeatureMap>,
30 #[serde(default, skip_serializing_if = "Option::is_none")]
33 features2: Option<Arc<FeatureMap>>,
34 #[serde(default)]
36 pub yanked: bool,
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub links: Option<Box<SmolStr>>,
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub rust_version: Option<SmolStr>,
43 #[serde(skip_serializing_if = "Option::is_none")]
45 v: Option<u32>,
46}
47
48impl IndexVersion {
49 #[doc(hidden)]
51 pub fn fake(name: &str, version: impl Into<SmolStr>) -> Self {
52 Self {
53 name: name.into(),
54 version: version.into(),
55 deps: Arc::new([]),
56 features: Arc::default(),
57 features2: None,
58 links: None,
59 rust_version: None,
60 checksum: Chksum(Default::default()),
61 yanked: false,
62 v: None,
63 }
64 }
65
66 #[inline]
68 pub fn dependencies(&self) -> &[IndexDependency] {
69 &self.deps
70 }
71
72 #[inline]
76 pub fn checksum(&self) -> &[u8; 32] {
77 &self.checksum.0
78 }
79
80 #[inline]
87 pub fn features(&self) -> impl Iterator<Item = (&String, &Vec<String>)> {
88 self.features.iter().chain(
89 self.features2
90 .as_ref()
91 .map(|f| f.iter())
92 .into_iter()
93 .flatten(),
94 )
95 }
96
97 #[inline]
102 pub fn links(&self) -> Option<&str> {
103 self.links.as_ref().map(|s| s.as_str())
104 }
105
106 #[inline]
109 pub fn is_yanked(&self) -> bool {
110 self.yanked
111 }
112
113 #[inline]
120 pub fn rust_version(&self) -> Option<&str> {
121 self.rust_version.as_deref()
122 }
123
124 #[inline]
126 pub fn download_url(&self, index: &crate::index::IndexConfig) -> Option<String> {
127 Some(index.download_url(self.name.as_str().try_into().ok()?, self.version.as_ref()))
128 }
129}
130
131#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Hash)]
133pub struct IndexDependency {
134 pub name: SmolStr,
136 pub req: SmolStr,
138 pub features: Box<Box<[String]>>,
140 pub optional: bool,
142 pub default_features: bool,
144 pub target: Option<Box<SmolStr>>,
146 #[serde(skip_serializing_if = "Option::is_none")]
148 pub kind: Option<DependencyKind>,
149 #[serde(skip_serializing_if = "Option::is_none")]
151 pub package: Option<Box<SmolStr>>,
152}
153
154impl IndexDependency {
155 #[inline]
157 pub fn version_requirement(&self) -> semver::VersionReq {
158 self.req.parse().unwrap()
159 }
160
161 #[inline]
165 pub fn features(&self) -> &[String] {
166 &self.features
167 }
168
169 #[inline]
172 pub fn is_optional(&self) -> bool {
173 self.optional
174 }
175
176 #[inline]
178 pub fn has_default_features(&self) -> bool {
179 self.default_features
180 }
181
182 #[inline]
184 pub fn target(&self) -> Option<&str> {
185 self.target.as_ref().map(|s| s.as_str())
186 }
187
188 #[inline]
190 pub fn kind(&self) -> DependencyKind {
191 self.kind.unwrap_or_default()
192 }
193
194 #[inline]
196 pub fn package(&self) -> Option<&str> {
197 self.package.as_ref().map(|s| s.as_str())
198 }
199
200 #[inline]
214 pub fn crate_name(&self) -> &str {
215 match &self.package {
216 Some(s) => s,
217 None => &self.name,
218 }
219 }
220}
221
222#[derive(Debug, Copy, Clone, Serialize, Deserialize, Eq, PartialEq, Hash, Default)]
224#[serde(rename_all = "lowercase")]
225pub enum DependencyKind {
226 #[default]
228 Normal,
229 Dev,
231 Build,
233}
234
235#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
237pub struct IndexKrate {
238 pub versions: Vec<IndexVersion>,
240}
241
242impl IndexKrate {
243 #[inline]
248 pub fn highest_version(&self) -> &IndexVersion {
249 self.versions
250 .iter()
251 .max_by_key(|v| Version::parse(&v.version).ok())
252 .unwrap()
256 }
257
258 #[inline]
265 pub fn highest_normal_version(&self) -> Option<&IndexVersion> {
266 self.versions
267 .iter()
268 .filter_map(|v| {
269 if v.is_yanked() {
270 return None;
271 }
272
273 v.version
274 .parse::<Version>()
275 .ok()
276 .filter(|v| v.pre.is_empty())
277 .map(|vs| (v, vs))
278 })
279 .max_by(|a, b| a.1.cmp(&b.1))
280 .map(|(v, _vs)| v)
281 }
282
283 #[inline]
285 pub fn name(&self) -> &str {
286 &self.versions[0].name
287 }
288
289 #[inline]
293 pub fn most_recent_version(&self) -> &IndexVersion {
294 &self.versions[self.versions.len() - 1]
295 }
296
297 #[inline]
301 pub fn earliest_version(&self) -> &IndexVersion {
302 &self.versions[0]
303 }
304}
305
306impl IndexKrate {
307 #[inline]
311 pub fn new(index_path: impl AsRef<crate::Path>) -> Result<Self, Error> {
312 let lines = std::fs::read(index_path.as_ref())?;
313 Self::from_slice(&lines)
314 }
315
316 #[inline]
318 pub fn from_slice(bytes: &[u8]) -> Result<Self, Error> {
319 let mut dedupe = DedupeContext::default();
320 Self::from_slice_with_context(bytes, &mut dedupe)
321 }
322
323 pub(crate) fn from_slice_with_context(
325 mut bytes: &[u8],
326 dedupe: &mut DedupeContext,
327 ) -> Result<Self, Error> {
328 use crate::index::cache::split;
329 while bytes.last() == Some(&b'\n') {
331 bytes = &bytes[..bytes.len() - 1];
332 }
333
334 let num_versions = split(bytes, b'\n').count();
335 let mut versions = Vec::with_capacity(num_versions);
336 for line in split(bytes, b'\n') {
337 let mut version: IndexVersion = serde_json::from_slice(line)?;
338
339 dedupe.deps(&mut version.deps);
341 dedupe.features(&mut version.features);
342
343 if let Some(features2) = &mut version.features2 {
344 dedupe.features(features2);
345 }
346
347 versions.push(version);
348 }
349
350 if versions.is_empty() {
351 return Err(Error::NoCrateVersions);
352 }
353
354 Ok(Self { versions })
355 }
356
357 pub fn write_json_lines<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
362 use std::io::{BufWriter, Write};
363
364 let mut w = BufWriter::new(writer);
365 for iv in &self.versions {
366 serde_json::to_writer(&mut w, &iv)?;
367 w.write_all(b"\n")?;
368 }
369
370 Ok(w.flush()?)
371 }
372}
373
374#[derive(Clone, Eq, PartialEq)]
377pub struct Chksum(pub [u8; 32]);
378
379use std::fmt;
380
381impl fmt::Debug for Chksum {
382 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383 let mut hex = [0; 64];
384 let hs = crate::utils::encode_hex(&self.0, &mut hex);
385
386 f.debug_struct("Chksum").field("sha-256", &hs).finish()
387 }
388}
389
390impl fmt::Display for Chksum {
391 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
392 let mut hex = [0; 64];
393 let hs = crate::utils::encode_hex(&self.0, &mut hex);
394
395 f.write_str(hs)
396 }
397}
398
399#[derive(Debug)]
401pub enum ChksumParseError {
402 InvalidLength(usize),
404 InvalidValue(char),
406}
407
408impl std::error::Error for ChksumParseError {}
409
410impl fmt::Display for ChksumParseError {
411 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
412 match self {
413 Self::InvalidLength(len) => {
414 write!(f, "expected string with length 64 but got length {len}")
415 }
416 Self::InvalidValue(c) => write!(f, "encountered non-hex character '{c}'"),
417 }
418 }
419}
420
421impl std::str::FromStr for Chksum {
422 type Err = ChksumParseError;
423
424 fn from_str(data: &str) -> Result<Self, Self::Err> {
425 if data.len() != 64 {
426 return Err(ChksumParseError::InvalidLength(data.len()));
427 }
428
429 let mut array = [0u8; 32];
430
431 for (ind, chunk) in data.as_bytes().chunks(2).enumerate() {
432 #[inline]
433 fn parse_hex(b: u8) -> Result<u8, ChksumParseError> {
434 Ok(match b {
435 b'A'..=b'F' => b - b'A' + 10,
436 b'a'..=b'f' => b - b'a' + 10,
437 b'0'..=b'9' => b - b'0',
438 c => {
439 return Err(ChksumParseError::InvalidValue(c as char));
440 }
441 })
442 }
443
444 let mut cur = parse_hex(chunk[0])?;
445 cur <<= 4;
446 cur |= parse_hex(chunk[1])?;
447
448 array[ind] = cur;
449 }
450
451 Ok(Self(array))
452 }
453}
454
455impl<'de> Deserialize<'de> for Chksum {
456 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
457 where
458 D: serde::Deserializer<'de>,
459 {
460 use serde::de::Error;
461 struct HexStrVisitor;
462
463 impl<'de> serde::de::Visitor<'de> for HexStrVisitor {
464 type Value = Chksum;
465
466 fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
467 write!(f, "a hex encoded string")
468 }
469
470 fn visit_str<E: Error>(self, data: &str) -> Result<Self::Value, E> {
471 data.parse().map_err(|err| match err {
472 ChksumParseError::InvalidLength(len) => {
473 serde::de::Error::invalid_length(len, &"a string with 64 characters")
474 }
475 ChksumParseError::InvalidValue(c) => serde::de::Error::invalid_value(
476 serde::de::Unexpected::Char(c),
477 &"a hexadecimal character",
478 ),
479 })
480 }
481
482 fn visit_borrowed_str<E: Error>(self, data: &'de str) -> Result<Self::Value, E> {
483 self.visit_str(data)
484 }
485 }
486
487 deserializer.deserialize_str(HexStrVisitor)
488 }
489}
490
491impl Serialize for Chksum {
492 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
493 where
494 S: serde::Serializer,
495 {
496 let mut raw = [0u8; 64];
497 let s = crate::utils::encode_hex(&self.0, &mut raw);
498 serializer.serialize_str(s)
499 }
500}
501
502#[cfg(test)]
503mod test {
504 #[test]
505 fn krate_versions() {
506 use super::IndexVersion as iv;
507 let ik = super::IndexKrate {
508 versions: vec![
509 iv::fake("vers", "0.1.0"),
510 iv::fake("vers", "0.1.1"),
511 iv::fake("vers", "0.1.0"),
512 iv::fake("vers", "0.2.0"),
513 iv::fake("vers", "0.3.0"),
514 iv::fake("vers", "0.4.0"),
516 iv::fake("vers", "0.4.0-alpha.00"),
517 {
518 let mut iv = iv::fake("vers", "0.5.0");
519 iv.yanked = true;
520 iv
521 },
522 ],
523 };
524
525 assert_eq!(ik.earliest_version().version, "0.1.0");
526 assert_eq!(ik.most_recent_version().version, "0.5.0");
527 assert_eq!(ik.highest_version().version, "0.5.0");
528 assert_eq!(ik.highest_normal_version().unwrap().version, "0.4.0");
529 }
530}