Skip to main content

uv_distribution/
download.rs

1use std::path::Path;
2
3use uv_cache_info::CacheInfo;
4use uv_distribution_filename::WheelFilename;
5use uv_distribution_types::{BuildInfo, BuiltDist, CachedDist, Dist, Hashed};
6use uv_metadata::{find_flat_dist_info, read_flat_wheel_metadata, read_wheel_variant};
7use uv_preview::PreviewFeature;
8use uv_pypi_types::{HashDigest, HashDigests, ResolutionMetadata};
9
10use crate::{Error, supported_variant_properties};
11
12/// Allow variant wheels from an index when the wheel variants preview is enabled.
13pub fn check_supported_wheel_variant(dist: &BuiltDist) -> Result<(), Error> {
14    let filename = dist.wheel_filename();
15    if filename.variant().is_some()
16        && (dist.index().is_none() || !uv_preview::is_enabled(PreviewFeature::WheelVariants))
17    {
18        return Err(Error::UnsupportedWheelVariant(Box::new(filename.clone())));
19    }
20    Ok(())
21}
22
23/// Check that an extracted wheel's variant properties are supported by the target system.
24pub(crate) fn check_wheel_variant_compatibility(
25    filename: &WheelFilename,
26    archive: &Path,
27) -> Result<(), Error> {
28    let Some(label) = filename.variant() else {
29        return Ok(());
30    };
31    if !uv_preview::is_enabled(PreviewFeature::WheelVariants) {
32        return Err(Error::UnsupportedWheelVariant(Box::new(filename.clone())));
33    }
34
35    let dist_info = find_flat_dist_info(filename, archive)
36        .map_err(|err| Error::WheelMetadata(archive.to_path_buf(), Box::new(err)))?;
37    let path = archive.join(format!("{dist_info}.dist-info/variant.json"));
38    let metadata = read_wheel_variant(&path, label)
39        .map_err(|err| Error::WheelMetadata(path, Box::new(err)))?;
40    if let Some(properties) =
41        metadata.unsupported_variant_properties(label, &supported_variant_properties(&metadata))
42    {
43        return Err(Error::IncompatibleWheelVariant {
44            label: label.clone(),
45            properties,
46        });
47    }
48    Ok(())
49}
50
51/// A locally available wheel.
52#[derive(Debug, Clone)]
53pub struct LocalWheel {
54    /// The remote distribution from which this wheel was downloaded.
55    pub(crate) dist: Dist,
56    /// The parsed filename.
57    pub(crate) filename: WheelFilename,
58    /// The canonicalized path in the cache directory to which the wheel was downloaded.
59    /// Typically, a directory within the archive bucket.
60    pub(crate) archive: Box<Path>,
61    /// The cache info of the wheel.
62    pub(crate) cache: CacheInfo,
63    /// The build info, if available.
64    pub(crate) build: Option<BuildInfo>,
65    /// The computed hashes of the wheel.
66    pub(crate) hashes: HashDigests,
67}
68
69impl LocalWheel {
70    /// Return the [`Dist`] from which this wheel was downloaded.
71    fn remote(&self) -> &Dist {
72        &self.dist
73    }
74
75    /// Read the [`ResolutionMetadata`] from a wheel.
76    pub(crate) fn metadata(&self) -> Result<ResolutionMetadata, Error> {
77        read_flat_wheel_metadata(&self.filename, &self.archive)
78            .map_err(|err| Error::WheelMetadata(self.archive.to_path_buf(), Box::new(err)))
79    }
80}
81
82impl Hashed for LocalWheel {
83    fn hashes(&self) -> &[HashDigest] {
84        self.hashes.as_slice()
85    }
86}
87
88/// Convert a [`LocalWheel`] into a [`CachedDist`].
89impl From<LocalWheel> for CachedDist {
90    fn from(wheel: LocalWheel) -> Self {
91        Self::from_remote(
92            wheel.dist,
93            wheel.filename,
94            wheel.hashes,
95            wheel.cache,
96            wheel.build,
97            wheel.archive,
98        )
99    }
100}
101
102impl std::fmt::Display for LocalWheel {
103    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104        write!(f, "{}", self.remote())
105    }
106}