1use crate::dedupe::DedupeContext;
2
3use crate::IndexConfig;
4use semver::Version as SemverVersion;
5use serde_derive::{Deserialize, Serialize};
6use smol_str::SmolStr;
7use std::collections::HashMap;
8use std::io;
9use std::path::Path;
10use std::sync::Arc;
11
12#[derive(Serialize, Deserialize, Clone, Debug)]
14pub struct Version {
15 name: SmolStr,
16 vers: SmolStr,
17 deps: Arc<[Dependency]>,
18 features: Arc<HashMap<String, Vec<String>>>,
19 #[serde(default, skip_serializing_if = "Option::is_none")]
22 #[allow(clippy::box_collection)]
23 features2: Option<Box<HashMap<String, Vec<String>>>>,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 links: Option<Box<SmolStr>>,
26 #[serde(default)]
27 rust_version: Option<SmolStr>,
28 #[serde(with = "hex")]
29 cksum: [u8; 32],
30 #[serde(default)]
31 yanked: bool,
32}
33
34impl Version {
35 #[inline]
37 #[must_use]
38 pub fn name(&self) -> &str {
39 &self.name
40 }
41
42 #[inline]
44 #[must_use]
45 pub fn version(&self) -> &str {
46 &self.vers
47 }
48
49 #[inline]
51 #[must_use]
52 pub fn dependencies(&self) -> &[Dependency] {
53 &self.deps
54 }
55
56 #[inline]
60 #[must_use]
61 pub fn checksum(&self) -> &[u8; 32] {
62 &self.cksum
63 }
64
65 #[inline]
70 #[must_use]
71 pub fn features(&self) -> &HashMap<String, Vec<String>> {
72 &self.features
73 }
74
75 fn build_data(&mut self, dedupe: &mut DedupeContext) {
79 if let Some(features2) = self.features2.take() {
80 if let Some(f1) = Arc::get_mut(&mut self.features) {
81 for (key, mut val) in features2.into_iter() {
82 f1.entry(key).or_insert_with(Vec::new).append(&mut val);
83 }
84 }
85 }
86
87 dedupe.deps(&mut self.deps);
89 dedupe.features(&mut self.features);
90 }
91
92 #[inline]
97 #[must_use]
98 pub fn links(&self) -> Option<&str> {
99 self.links.as_ref().map(|s| s.as_str())
100 }
101
102 #[inline]
105 #[must_use]
106 pub fn is_yanked(&self) -> bool {
107 self.yanked
108 }
109
110 #[inline]
117 #[must_use]
118 pub fn rust_version(&self) -> Option<&str> {
119 self.rust_version.as_deref()
120 }
121
122 #[must_use]
124 pub fn download_url(&self, index: &IndexConfig) -> Option<String> {
125 index.download_url(&self.name, &self.vers)
126 }
127}
128
129#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Hash)]
131pub struct Dependency {
132 name: SmolStr,
133 req: SmolStr,
134 features: Box<Box<[String]>>,
136 #[serde(skip_serializing_if = "Option::is_none")]
137 package: Option<Box<SmolStr>>,
138 #[serde(skip_serializing_if = "Option::is_none")]
139 kind: Option<DependencyKind>,
140 #[serde(skip_serializing_if = "Option::is_none")]
141 registry: Option<SmolStr>,
142 #[serde(skip_serializing_if = "Option::is_none")]
143 target: Option<Box<SmolStr>>,
144 optional: bool,
145 default_features: bool,
146}
147
148impl Dependency {
149 #[inline]
151 #[must_use]
152 pub fn name(&self) -> &str {
153 &self.name
154 }
155
156 #[inline]
158 #[must_use]
159 pub fn requirement(&self) -> &str {
160 &self.req
161 }
162
163 #[inline]
167 #[must_use]
168 pub fn features(&self) -> &[String] {
169 &self.features
170 }
171
172 #[inline]
175 #[must_use]
176 pub fn is_optional(&self) -> bool {
177 self.optional
178 }
179
180 #[inline]
182 #[must_use]
183 pub fn has_default_features(&self) -> bool {
184 self.default_features
185 }
186
187 #[inline]
189 #[must_use]
190 pub fn target(&self) -> Option<&str> {
191 self.target.as_ref().map(|s| s.as_str())
192 }
193
194 #[inline]
196 #[must_use]
197 pub fn kind(&self) -> DependencyKind {
198 self.kind.unwrap_or_default()
199 }
200
201 #[inline]
205 #[must_use]
206 pub fn registry(&self) -> Option<&str> {
207 self.registry.as_deref()
208 }
209
210 #[inline]
212 #[must_use]
213 pub fn package(&self) -> Option<&str> {
214 self.package.as_ref().map(|s| s.as_str())
215 }
216
217 #[inline]
231 #[must_use]
232 pub fn crate_name(&self) -> &str {
233 match self.package {
234 Some(ref s) => s,
235 None => self.name(),
236 }
237 }
238}
239
240#[derive(Debug, Copy, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
242#[serde(rename_all = "lowercase")]
243pub enum DependencyKind {
244 Normal,
246 Dev,
248 Build,
250}
251
252impl Default for DependencyKind {
253 fn default() -> Self {
254 Self::Normal
255 }
256}
257
258#[derive(Serialize, Deserialize, Clone, Debug)]
260pub struct Crate {
261 versions: Box<[Version]>,
262}
263
264impl Crate {
265 #[inline(never)]
267 pub(crate) fn from_slice_with_context(mut bytes: &[u8], dedupe: &mut DedupeContext) -> io::Result<Crate> {
268 while bytes.last() == Some(&b'\n') {
270 bytes = &bytes[..bytes.len() - 1];
271 }
272
273 #[inline(always)]
274 fn is_newline(&c: &u8) -> bool {
275 c == b'\n'
276 }
277 let num_versions = bytes.split(is_newline).count();
278 let mut versions = Vec::with_capacity(num_versions);
279 for line in bytes.split(is_newline) {
280 let mut version: Version =
281 serde_json::from_slice(line).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
282
283 version.build_data(dedupe);
284
285 versions.push(version);
286 }
287 if versions.is_empty() {
288 return Err(io::ErrorKind::UnexpectedEof.into());
289 }
290 debug_assert_eq!(versions.len(), versions.capacity());
291 Ok(Crate {
292 versions: versions.into_boxed_slice(),
293 })
294 }
295
296 #[inline(never)]
304 pub(crate) fn from_cache_slice(bytes: &[u8], index_version: Option<&str>) -> io::Result<Self> {
305 const CURRENT_CACHE_VERSION: u8 = 3;
306 const CURRENT_INDEX_FORMAT_VERSION: u32 = 2;
307
308 let (first_byte, mut rest) = bytes.split_first().ok_or(io::ErrorKind::UnexpectedEof)?;
310
311 match *first_byte {
312 CURRENT_CACHE_VERSION => {
314 let index_v_bytes = rest.get(..4).ok_or(io::ErrorKind::UnexpectedEof)?;
315 let index_v = u32::from_le_bytes(index_v_bytes.try_into().unwrap());
316 if index_v != CURRENT_INDEX_FORMAT_VERSION {
317 return Err(io::Error::new(
318 io::ErrorKind::Unsupported,
319 format!("wrong index format version: {index_v} (expected {CURRENT_INDEX_FORMAT_VERSION}))"),
320 ));
321 }
322 rest = &rest[4..];
323 }
324 1 => {}
326 2 => {
333 return Err(io::Error::new(
334 io::ErrorKind::Other,
335 "potentially invalid version 2 cache entry found",
336 ));
337 }
338 version => {
339 return Err(io::Error::new(
340 io::ErrorKind::Unsupported,
341 format!("cache version '{version}' not currently supported"),
342 ));
343 }
344 }
345
346 let mut iter = crate::split(rest, 0);
347 let update = iter.next().ok_or(io::ErrorKind::UnexpectedEof)?;
348 if let Some(index_version) = index_version {
349 if update != index_version.as_bytes() {
350 return Err(io::Error::new(
351 io::ErrorKind::Other,
352 format!(
353 "cache out of date: current index ({index_version}) != cache ({})",
354 String::from_utf8_lossy(update)
355 ),
356 ));
357 }
358 }
359
360 Self::from_version_entries_iter(iter)
361 }
362
363 pub(crate) fn from_version_entries_iter<'a, I: Iterator<Item = &'a [u8]> + 'a>(mut iter: I) -> io::Result<Crate> {
364 let mut versions = Vec::new();
365
366 let mut dedupe = DedupeContext::new();
367
368 while let Some(_version) = iter.next() {
370 let version_slice = iter.next().ok_or(io::ErrorKind::UnexpectedEof)?;
371 let mut version: Version =
372 serde_json::from_slice(version_slice).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
373
374 version.build_data(&mut dedupe);
375
376 versions.push(version);
377 }
378
379 Ok(Self {
380 versions: versions.into_boxed_slice(),
381 })
382 }
383
384 #[cfg(feature = "sparse")]
386 pub(crate) fn write_cache_entry(&self, path: &Path, version: &str) -> io::Result<()> {
387 const CURRENT_CACHE_VERSION: u8 = 3;
388 const CURRENT_INDEX_FORMAT_VERSION: u32 = 2;
389
390 let mut v = Vec::new();
391 v.push(CURRENT_CACHE_VERSION);
392 v.extend_from_slice(&CURRENT_INDEX_FORMAT_VERSION.to_le_bytes());
393 v.extend_from_slice(version.as_bytes());
394 v.push(0);
395
396 for version in self.versions() {
397 v.extend_from_slice(version.version().as_bytes());
398 v.push(0);
399 v.append(&mut serde_json::to_vec(version).unwrap());
400 v.push(0);
401 }
402
403 std::fs::write(path, v)
404 }
405
406 #[inline]
410 #[must_use]
411 pub fn versions(&self) -> &[Version] {
412 &self.versions
413 }
414
415 #[must_use]
419 pub fn highest_version(&self) -> &Version {
420 self.versions
421 .iter()
422 .max_by_key(|v| SemverVersion::parse(&v.vers).ok())
423 .unwrap()
427 }
428
429 #[must_use]
436 pub fn highest_normal_version(&self) -> Option<&Version> {
437 self.versions
438 .iter()
439 .filter(|v| !v.is_yanked())
440 .filter_map(|v| Some((v, SemverVersion::parse(&v.vers).ok()?)))
441 .filter(|(_, sem)| sem.pre.is_empty())
442 .max_by(|a, b| a.1.cmp(&b.1))
443 .map(|(v, _)| v)
444 }
445
446 #[inline]
448 #[must_use]
449 pub fn name(&self) -> &str {
450 self.versions[0].name()
451 }
452
453 #[inline]
457 #[must_use]
458 pub fn most_recent_version(&self) -> &Version {
459 &self.versions[self.versions.len() - 1]
460 }
461
462 #[inline]
466 #[must_use]
467 pub fn earliest_version(&self) -> &Version {
468 &self.versions[0]
469 }
470
471 #[cold]
475 #[doc(hidden)]
476 #[deprecated(note = "use most_recent_version")]
477 #[must_use]
478 pub fn latest_version(&self) -> &Version {
479 self.most_recent_version()
480 }
481
482 #[cold]
487 #[doc(hidden)]
488 #[deprecated(note = "use highest_normal_version")]
489 #[must_use]
490 pub fn highest_stable_version(&self) -> Option<&Version> {
491 self.versions
492 .iter()
493 .filter_map(|v| Some((v, SemverVersion::parse(&v.vers).ok()?)))
494 .filter(|(_, sem)| sem.pre.is_empty())
495 .max_by(|a, b| a.1.cmp(&b.1))
496 .map(|(v, _)| v)
497 }
498
499 #[inline]
503 pub fn new<P: AsRef<Path>>(index_path: P) -> io::Result<Crate> {
504 let lines = std::fs::read(index_path)?;
505 Self::from_slice(&lines)
506 }
507
508 #[inline]
510 pub fn from_slice(bytes: &[u8]) -> io::Result<Crate> {
511 let mut dedupe = DedupeContext::new();
512 Self::from_slice_with_context(bytes, &mut dedupe)
513 }
514}