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, Default)]
242#[serde(rename_all = "lowercase")]
243pub enum DependencyKind {
244 #[default]
246 Normal,
247 Dev,
249 Build,
251}
252
253#[derive(Serialize, Deserialize, Clone, Debug)]
255pub struct Crate {
256 versions: Box<[Version]>,
257}
258
259impl Crate {
260 #[inline(never)]
262 pub(crate) fn from_slice_with_context(mut bytes: &[u8], dedupe: &mut DedupeContext) -> io::Result<Crate> {
263 while bytes.last() == Some(&b'\n') {
265 bytes = &bytes[..bytes.len() - 1];
266 }
267
268 #[inline(always)]
269 fn is_newline(&c: &u8) -> bool {
270 c == b'\n'
271 }
272 let num_versions = bytes.split(is_newline).count();
273 let mut versions = Vec::with_capacity(num_versions);
274 for line in bytes.split(is_newline) {
275 let mut version: Version = serde_json::from_slice(line).map_err(io::Error::other)?;
276
277 version.build_data(dedupe);
278
279 versions.push(version);
280 }
281 if versions.is_empty() {
282 return Err(io::ErrorKind::UnexpectedEof.into());
283 }
284 debug_assert_eq!(versions.len(), versions.capacity());
285 Ok(Crate {
286 versions: versions.into_boxed_slice(),
287 })
288 }
289
290 #[inline(never)]
298 pub(crate) fn from_cache_slice(bytes: &[u8], index_version: Option<&str>) -> io::Result<Self> {
299 const CURRENT_CACHE_VERSION: u8 = 3;
300 const CURRENT_INDEX_FORMAT_VERSION: u32 = 2;
301
302 let (first_byte, mut rest) = bytes.split_first().ok_or(io::ErrorKind::UnexpectedEof)?;
304
305 match *first_byte {
306 CURRENT_CACHE_VERSION => {
308 let index_v_bytes = rest.get(..4).ok_or(io::ErrorKind::UnexpectedEof)?;
309 let index_v = u32::from_le_bytes(index_v_bytes.try_into().unwrap());
310 if index_v != CURRENT_INDEX_FORMAT_VERSION {
311 return Err(io::Error::new(
312 io::ErrorKind::Unsupported,
313 format!("wrong index format version: {index_v} (expected {CURRENT_INDEX_FORMAT_VERSION}))"),
314 ));
315 }
316 rest = &rest[4..];
317 }
318 1 => {}
320 2 => {
327 return Err(io::Error::other("potentially invalid version 2 cache entry found"));
328 }
329 version => {
330 return Err(io::Error::new(
331 io::ErrorKind::Unsupported,
332 format!("cache version '{version}' not currently supported"),
333 ));
334 }
335 }
336
337 let mut iter = crate::split(rest, 0);
338 let update = iter.next().ok_or(io::ErrorKind::UnexpectedEof)?;
339 if let Some(index_version) = index_version {
340 if update != index_version.as_bytes() {
341 return Err(io::Error::other(format!(
342 "cache out of date: current index ({index_version}) != cache ({})",
343 String::from_utf8_lossy(update)
344 )));
345 }
346 }
347
348 Self::from_version_entries_iter(iter)
349 }
350
351 pub(crate) fn from_version_entries_iter<'a, I: Iterator<Item = &'a [u8]> + 'a>(mut iter: I) -> io::Result<Crate> {
352 let mut versions = Vec::new();
353
354 let mut dedupe = DedupeContext::new();
355
356 while let Some(_version) = iter.next() {
358 let version_slice = iter.next().ok_or(io::ErrorKind::UnexpectedEof)?;
359 let mut version: Version =
360 serde_json::from_slice(version_slice).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
361
362 version.build_data(&mut dedupe);
363
364 versions.push(version);
365 }
366
367 Ok(Self {
368 versions: versions.into_boxed_slice(),
369 })
370 }
371
372 #[cfg(feature = "sparse")]
374 pub(crate) fn write_cache_entry(&self, path: &Path, version: &str) -> io::Result<()> {
375 const CURRENT_CACHE_VERSION: u8 = 3;
376 const CURRENT_INDEX_FORMAT_VERSION: u32 = 2;
377
378 let mut v = Vec::new();
379 v.push(CURRENT_CACHE_VERSION);
380 v.extend_from_slice(&CURRENT_INDEX_FORMAT_VERSION.to_le_bytes());
381 v.extend_from_slice(version.as_bytes());
382 v.push(0);
383
384 for version in self.versions() {
385 v.extend_from_slice(version.version().as_bytes());
386 v.push(0);
387 v.append(&mut serde_json::to_vec(version).unwrap());
388 v.push(0);
389 }
390
391 std::fs::write(path, v)
392 }
393
394 #[inline]
398 #[must_use]
399 pub fn versions(&self) -> &[Version] {
400 &self.versions
401 }
402
403 #[must_use]
407 pub fn highest_version(&self) -> &Version {
408 self.versions
409 .iter()
410 .max_by_key(|v| SemverVersion::parse(&v.vers).ok())
411 .unwrap()
415 }
416
417 #[must_use]
424 pub fn highest_normal_version(&self) -> Option<&Version> {
425 self.versions
426 .iter()
427 .filter(|v| !v.is_yanked())
428 .filter_map(|v| Some((v, SemverVersion::parse(&v.vers).ok()?)))
429 .filter(|(_, sem)| sem.pre.is_empty())
430 .max_by(|a, b| a.1.cmp(&b.1))
431 .map(|(v, _)| v)
432 }
433
434 #[inline]
436 #[must_use]
437 pub fn name(&self) -> &str {
438 self.versions[0].name()
439 }
440
441 #[inline]
445 #[must_use]
446 pub fn most_recent_version(&self) -> &Version {
447 &self.versions[self.versions.len() - 1]
448 }
449
450 #[inline]
454 #[must_use]
455 pub fn earliest_version(&self) -> &Version {
456 &self.versions[0]
457 }
458
459 #[cold]
463 #[doc(hidden)]
464 #[deprecated(note = "use most_recent_version")]
465 #[must_use]
466 pub fn latest_version(&self) -> &Version {
467 self.most_recent_version()
468 }
469
470 #[cold]
475 #[doc(hidden)]
476 #[deprecated(note = "use highest_normal_version")]
477 #[must_use]
478 pub fn highest_stable_version(&self) -> Option<&Version> {
479 self.versions
480 .iter()
481 .filter_map(|v| Some((v, SemverVersion::parse(&v.vers).ok()?)))
482 .filter(|(_, sem)| sem.pre.is_empty())
483 .max_by(|a, b| a.1.cmp(&b.1))
484 .map(|(v, _)| v)
485 }
486
487 #[inline]
491 pub fn new<P: AsRef<Path>>(index_path: P) -> io::Result<Crate> {
492 let lines = std::fs::read(index_path)?;
493 Self::from_slice(&lines)
494 }
495
496 #[inline]
498 pub fn from_slice(bytes: &[u8]) -> io::Result<Crate> {
499 let mut dedupe = DedupeContext::new();
500 Self::from_slice_with_context(bytes, &mut dedupe)
501 }
502}