mirror of
https://github.com/astral-sh/uv.git
synced 2025-09-03 17:20:42 +00:00
Avoid replicating core-metadata field on File
struct (#12159)
## Summary A long-standing TODO: we don't need to store three copies of this just to ensure that Serde considers all three fields.
This commit is contained in:
parent
c806a627f3
commit
d2b9ffdc9e
3 changed files with 676 additions and 704 deletions
File diff suppressed because it is too large
Load diff
|
@ -47,8 +47,6 @@ impl File {
|
||||||
dist_info_metadata: file
|
dist_info_metadata: file
|
||||||
.core_metadata
|
.core_metadata
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.or(file.dist_info_metadata.as_ref())
|
|
||||||
.or(file.data_dist_info_metadata.as_ref())
|
|
||||||
.is_some_and(CoreMetadata::is_available),
|
.is_some_and(CoreMetadata::is_available),
|
||||||
filename: file.filename,
|
filename: file.filename,
|
||||||
hashes: HashDigests::from(file.hashes),
|
hashes: HashDigests::from(file.hashes),
|
||||||
|
|
|
@ -35,23 +35,11 @@ fn sorted_simple_json_files<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<File>
|
||||||
/// A single (remote) file belonging to a package, either a wheel or a source distribution.
|
/// A single (remote) file belonging to a package, either a wheel or a source distribution.
|
||||||
///
|
///
|
||||||
/// <https://peps.python.org/pep-0691/#project-detail>
|
/// <https://peps.python.org/pep-0691/#project-detail>
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub struct File {
|
pub struct File {
|
||||||
// PEP 714-renamed field, followed by PEP 691-compliant field, followed by non-PEP 691-compliant
|
|
||||||
// alias used by PyPI.
|
|
||||||
//
|
|
||||||
// TODO(charlie): Use a single value here and move this into the deserializer, to save space.
|
|
||||||
pub core_metadata: Option<CoreMetadata>,
|
pub core_metadata: Option<CoreMetadata>,
|
||||||
pub dist_info_metadata: Option<CoreMetadata>,
|
|
||||||
pub data_dist_info_metadata: Option<CoreMetadata>,
|
|
||||||
pub filename: SmallString,
|
pub filename: SmallString,
|
||||||
pub hashes: Hashes,
|
pub hashes: Hashes,
|
||||||
/// There are a number of invalid specifiers on PyPI, so we first try to parse it into a
|
|
||||||
/// [`VersionSpecifiers`] according to spec (PEP 440), then a [`LenientVersionSpecifiers`] with
|
|
||||||
/// fixup for some common problems and if this still fails, we skip the file when creating a
|
|
||||||
/// version map.
|
|
||||||
#[serde(default, deserialize_with = "deserialize_version_specifiers_lenient")]
|
|
||||||
pub requires_python: Option<Result<VersionSpecifiers, VersionSpecifiersParseError>>,
|
pub requires_python: Option<Result<VersionSpecifiers, VersionSpecifiersParseError>>,
|
||||||
pub size: Option<u64>,
|
pub size: Option<u64>,
|
||||||
pub upload_time: Option<Timestamp>,
|
pub upload_time: Option<Timestamp>,
|
||||||
|
@ -59,43 +47,75 @@ pub struct File {
|
||||||
pub yanked: Option<Box<Yanked>>,
|
pub yanked: Option<Box<Yanked>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_version_specifiers_lenient<'de, D>(
|
impl<'de> Deserialize<'de> for File {
|
||||||
deserializer: D,
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
) -> Result<Option<Result<VersionSpecifiers, VersionSpecifiersParseError>>, D::Error>
|
where
|
||||||
where
|
D: Deserializer<'de>,
|
||||||
D: Deserializer<'de>,
|
{
|
||||||
{
|
struct FileVisitor;
|
||||||
struct Visitor;
|
|
||||||
|
|
||||||
impl<'de> serde::de::Visitor<'de> for Visitor {
|
impl<'de> serde::de::Visitor<'de> for FileVisitor {
|
||||||
type Value = Option<Result<VersionSpecifiers, VersionSpecifiersParseError>>;
|
type Value = File;
|
||||||
|
|
||||||
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
f.write_str("a string representing a version specifier")
|
formatter.write_str("a map containing file metadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
|
||||||
|
where
|
||||||
|
M: serde::de::MapAccess<'de>,
|
||||||
|
{
|
||||||
|
let mut core_metadata = None;
|
||||||
|
let mut filename = None;
|
||||||
|
let mut hashes = None;
|
||||||
|
let mut requires_python = None;
|
||||||
|
let mut size = None;
|
||||||
|
let mut upload_time = None;
|
||||||
|
let mut url = None;
|
||||||
|
let mut yanked = None;
|
||||||
|
|
||||||
|
while let Some(key) = access.next_key::<String>()? {
|
||||||
|
match key.as_str() {
|
||||||
|
"core-metadata" | "dist-info-metadata" | "data-dist-info-metadata" => {
|
||||||
|
if core_metadata.is_none() {
|
||||||
|
core_metadata = access.next_value()?;
|
||||||
|
} else {
|
||||||
|
let _: serde::de::IgnoredAny = access.next_value()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"filename" => filename = Some(access.next_value()?),
|
||||||
|
"hashes" => hashes = Some(access.next_value()?),
|
||||||
|
"requires-python" => {
|
||||||
|
requires_python = access.next_value::<Option<&str>>()?.map(|s| {
|
||||||
|
LenientVersionSpecifiers::from_str(s).map(VersionSpecifiers::from)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
"size" => size = Some(access.next_value()?),
|
||||||
|
"upload-time" => upload_time = Some(access.next_value()?),
|
||||||
|
"url" => url = Some(access.next_value()?),
|
||||||
|
"yanked" => yanked = Some(access.next_value()?),
|
||||||
|
_ => {
|
||||||
|
let _: serde::de::IgnoredAny = access.next_value()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(File {
|
||||||
|
core_metadata,
|
||||||
|
filename: filename
|
||||||
|
.ok_or_else(|| serde::de::Error::missing_field("filename"))?,
|
||||||
|
hashes: hashes.ok_or_else(|| serde::de::Error::missing_field("hashes"))?,
|
||||||
|
requires_python,
|
||||||
|
size,
|
||||||
|
upload_time,
|
||||||
|
url: url.ok_or_else(|| serde::de::Error::missing_field("url"))?,
|
||||||
|
yanked,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
|
deserializer.deserialize_map(FileVisitor)
|
||||||
Ok(Some(
|
|
||||||
LenientVersionSpecifiers::from_str(v).map(VersionSpecifiers::from),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
deserializer.deserialize_str(Visitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_none<E>(self) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deserializer.deserialize_option(Visitor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue