mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Clean up Error
enum in Metadata23
(#2835)
## Summary Rename, and remove a bunch of unused variants.
This commit is contained in:
parent
2f386ef440
commit
a0b8d1a994
3 changed files with 56 additions and 66 deletions
|
@ -1,9 +1,8 @@
|
|||
//! Derived from `pypi_types_crate`.
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use std::io;
|
||||
use std::str::FromStr;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use mailparse::{MailHeaderMap, MailParseError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
@ -39,38 +38,17 @@ pub struct Metadata23 {
|
|||
///
|
||||
/// The error type
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
/// I/O error
|
||||
#[error(transparent)]
|
||||
Io(#[from] io::Error),
|
||||
/// mail parse error
|
||||
pub enum MetadataError {
|
||||
#[error(transparent)]
|
||||
MailParse(#[from] MailParseError),
|
||||
/// TOML parse error
|
||||
#[error(transparent)]
|
||||
Toml(#[from] toml::de::Error),
|
||||
/// Metadata field not found
|
||||
#[error("metadata field {0} not found")]
|
||||
FieldNotFound(&'static str),
|
||||
/// Unknown distribution type
|
||||
#[error("unknown distribution type")]
|
||||
UnknownDistributionType,
|
||||
/// Metadata file not found
|
||||
#[error("metadata file not found")]
|
||||
MetadataNotFound,
|
||||
/// Invalid project URL (no comma)
|
||||
#[error("Invalid Project-URL field (missing comma): '{0}'")]
|
||||
InvalidProjectUrl(String),
|
||||
/// Multiple metadata files found
|
||||
#[error("found multiple metadata files: {0:?}")]
|
||||
MultipleMetadataFiles(Vec<String>),
|
||||
/// Invalid Version
|
||||
#[error("invalid version: {0}")]
|
||||
Pep440VersionError(VersionParseError),
|
||||
/// Invalid VersionSpecifier
|
||||
#[error(transparent)]
|
||||
Pep440Error(#[from] VersionSpecifiersParseError),
|
||||
/// Invalid Requirement
|
||||
#[error(transparent)]
|
||||
Pep508Error(#[from] Pep508Error),
|
||||
#[error(transparent)]
|
||||
|
@ -86,20 +64,20 @@ pub enum Error {
|
|||
/// From <https://github.com/PyO3/python-pkginfo-rs/blob/d719988323a0cfea86d4737116d7917f30e819e2/src/metadata.rs#LL78C2-L91C26>
|
||||
impl Metadata23 {
|
||||
/// Parse the [`Metadata23`] from a `METADATA` file, as included in a built distribution (wheel).
|
||||
pub fn parse_metadata(content: &[u8]) -> Result<Self, Error> {
|
||||
pub fn parse_metadata(content: &[u8]) -> Result<Self, MetadataError> {
|
||||
let headers = Headers::parse(content)?;
|
||||
|
||||
let name = PackageName::new(
|
||||
headers
|
||||
.get_first_value("Name")
|
||||
.ok_or(Error::FieldNotFound("Name"))?,
|
||||
.ok_or(MetadataError::FieldNotFound("Name"))?,
|
||||
)?;
|
||||
let version = Version::from_str(
|
||||
&headers
|
||||
.get_first_value("Version")
|
||||
.ok_or(Error::FieldNotFound("Version"))?,
|
||||
.ok_or(MetadataError::FieldNotFound("Version"))?,
|
||||
)
|
||||
.map_err(Error::Pep440VersionError)?;
|
||||
.map_err(MetadataError::Pep440VersionError)?;
|
||||
let requires_dist = headers
|
||||
.get_all_values("Requires-Dist")
|
||||
.map(|requires_dist| {
|
||||
|
@ -135,28 +113,28 @@ impl Metadata23 {
|
|||
/// Read the [`Metadata23`] from a source distribution's `PKG-INFO` file, if it uses Metadata 2.2
|
||||
/// or later _and_ none of the required fields (`Requires-Python`, `Requires-Dist`, and
|
||||
/// `Provides-Extra`) are marked as dynamic.
|
||||
pub fn parse_pkg_info(content: &[u8]) -> Result<Self, Error> {
|
||||
pub fn parse_pkg_info(content: &[u8]) -> Result<Self, MetadataError> {
|
||||
let headers = Headers::parse(content)?;
|
||||
|
||||
// To rely on a source distribution's `PKG-INFO` file, the `Metadata-Version` field must be
|
||||
// present and set to a value of at least `2.2`.
|
||||
let metadata_version = headers
|
||||
.get_first_value("Metadata-Version")
|
||||
.ok_or(Error::FieldNotFound("Metadata-Version"))?;
|
||||
.ok_or(MetadataError::FieldNotFound("Metadata-Version"))?;
|
||||
|
||||
// Parse the version into (major, minor).
|
||||
let (major, minor) = parse_version(&metadata_version)?;
|
||||
if (major, minor) < (2, 2) || (major, minor) >= (3, 0) {
|
||||
return Err(Error::UnsupportedMetadataVersion(metadata_version));
|
||||
return Err(MetadataError::UnsupportedMetadataVersion(metadata_version));
|
||||
}
|
||||
|
||||
// If any of the fields we need are marked as dynamic, we can't use the `PKG-INFO` file.
|
||||
let dynamic = headers.get_all_values("Dynamic").collect::<Vec<_>>();
|
||||
for field in dynamic {
|
||||
match field.as_str() {
|
||||
"Requires-Python" => return Err(Error::DynamicField("Requires-Python")),
|
||||
"Requires-Dist" => return Err(Error::DynamicField("Requires-Dist")),
|
||||
"Provides-Extra" => return Err(Error::DynamicField("Provides-Extra")),
|
||||
"Requires-Python" => return Err(MetadataError::DynamicField("Requires-Python")),
|
||||
"Requires-Dist" => return Err(MetadataError::DynamicField("Requires-Dist")),
|
||||
"Provides-Extra" => return Err(MetadataError::DynamicField("Provides-Extra")),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
@ -165,14 +143,14 @@ impl Metadata23 {
|
|||
let name = PackageName::new(
|
||||
headers
|
||||
.get_first_value("Name")
|
||||
.ok_or(Error::FieldNotFound("Name"))?,
|
||||
.ok_or(MetadataError::FieldNotFound("Name"))?,
|
||||
)?;
|
||||
let version = Version::from_str(
|
||||
&headers
|
||||
.get_first_value("Version")
|
||||
.ok_or(Error::FieldNotFound("Version"))?,
|
||||
.ok_or(MetadataError::FieldNotFound("Version"))?,
|
||||
)
|
||||
.map_err(Error::Pep440VersionError)?;
|
||||
.map_err(MetadataError::Pep440VersionError)?;
|
||||
|
||||
// The remaining fields are required to be present.
|
||||
let requires_dist = headers
|
||||
|
@ -208,29 +186,31 @@ impl Metadata23 {
|
|||
}
|
||||
|
||||
/// Extract the metadata from a `pyproject.toml` file, as specified in PEP 621.
|
||||
pub fn parse_pyproject_toml(contents: &str) -> Result<Self, Error> {
|
||||
pub fn parse_pyproject_toml(contents: &str) -> Result<Self, MetadataError> {
|
||||
let pyproject_toml: PyProjectToml = toml::from_str(contents)?;
|
||||
|
||||
let project = pyproject_toml
|
||||
.project
|
||||
.ok_or(Error::FieldNotFound("project"))?;
|
||||
.ok_or(MetadataError::FieldNotFound("project"))?;
|
||||
|
||||
// If any of the fields we need were declared as dynamic, we can't use the `pyproject.toml` file.
|
||||
let dynamic = project.dynamic.unwrap_or_default();
|
||||
for field in dynamic {
|
||||
match field.as_str() {
|
||||
"dependencies" => return Err(Error::DynamicField("dependencies")),
|
||||
"dependencies" => return Err(MetadataError::DynamicField("dependencies")),
|
||||
"optional-dependencies" => {
|
||||
return Err(Error::DynamicField("optional-dependencies"))
|
||||
return Err(MetadataError::DynamicField("optional-dependencies"))
|
||||
}
|
||||
"requires-python" => return Err(Error::DynamicField("requires-python")),
|
||||
"version" => return Err(Error::DynamicField("version")),
|
||||
"requires-python" => return Err(MetadataError::DynamicField("requires-python")),
|
||||
"version" => return Err(MetadataError::DynamicField("version")),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
let name = project.name;
|
||||
let version = project.version.ok_or(Error::FieldNotFound("version"))?;
|
||||
let version = project
|
||||
.version
|
||||
.ok_or(MetadataError::FieldNotFound("version"))?;
|
||||
let requires_python = project.requires_python.map(VersionSpecifiers::from);
|
||||
|
||||
// Extract the requirements.
|
||||
|
@ -309,28 +289,31 @@ pub struct Metadata10 {
|
|||
|
||||
impl Metadata10 {
|
||||
/// Parse the [`Metadata10`] from a `PKG-INFO` file, as included in a source distribution.
|
||||
pub fn parse_pkg_info(content: &[u8]) -> Result<Self, Error> {
|
||||
pub fn parse_pkg_info(content: &[u8]) -> Result<Self, MetadataError> {
|
||||
let headers = Headers::parse(content)?;
|
||||
let name = PackageName::new(
|
||||
headers
|
||||
.get_first_value("Name")
|
||||
.ok_or(Error::FieldNotFound("Name"))?,
|
||||
.ok_or(MetadataError::FieldNotFound("Name"))?,
|
||||
)?;
|
||||
Ok(Self { name })
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a `Metadata-Version` field into a (major, minor) tuple.
|
||||
fn parse_version(metadata_version: &str) -> Result<(u8, u8), Error> {
|
||||
let (major, minor) = metadata_version
|
||||
.split_once('.')
|
||||
.ok_or(Error::InvalidMetadataVersion(metadata_version.to_string()))?;
|
||||
fn parse_version(metadata_version: &str) -> Result<(u8, u8), MetadataError> {
|
||||
let (major, minor) =
|
||||
metadata_version
|
||||
.split_once('.')
|
||||
.ok_or(MetadataError::InvalidMetadataVersion(
|
||||
metadata_version.to_string(),
|
||||
))?;
|
||||
let major = major
|
||||
.parse::<u8>()
|
||||
.map_err(|_| Error::InvalidMetadataVersion(metadata_version.to_string()))?;
|
||||
.map_err(|_| MetadataError::InvalidMetadataVersion(metadata_version.to_string()))?;
|
||||
let minor = minor
|
||||
.parse::<u8>()
|
||||
.map_err(|_| Error::InvalidMetadataVersion(metadata_version.to_string()))?;
|
||||
.map_err(|_| MetadataError::InvalidMetadataVersion(metadata_version.to_string()))?;
|
||||
Ok((major, minor))
|
||||
}
|
||||
|
||||
|
@ -373,7 +356,7 @@ mod tests {
|
|||
use pep440_rs::Version;
|
||||
use uv_normalize::PackageName;
|
||||
|
||||
use crate::Error;
|
||||
use crate::MetadataError;
|
||||
|
||||
use super::Metadata23;
|
||||
|
||||
|
@ -381,11 +364,11 @@ mod tests {
|
|||
fn test_parse_metadata() {
|
||||
let s = "Metadata-Version: 1.0";
|
||||
let meta = Metadata23::parse_metadata(s.as_bytes());
|
||||
assert!(matches!(meta, Err(Error::FieldNotFound("Name"))));
|
||||
assert!(matches!(meta, Err(MetadataError::FieldNotFound("Name"))));
|
||||
|
||||
let s = "Metadata-Version: 1.0\nName: asdf";
|
||||
let meta = Metadata23::parse_metadata(s.as_bytes());
|
||||
assert!(matches!(meta, Err(Error::FieldNotFound("Version"))));
|
||||
assert!(matches!(meta, Err(MetadataError::FieldNotFound("Version"))));
|
||||
|
||||
let s = "Metadata-Version: 1.0\nName: asdf\nVersion: 1.0";
|
||||
let meta = Metadata23::parse_metadata(s.as_bytes()).unwrap();
|
||||
|
@ -404,22 +387,25 @@ mod tests {
|
|||
|
||||
let s = "Metadata-Version: 1.0\nName: =?utf-8?q?=C3=A4_space?= <x@y.org>\nVersion: 1.0";
|
||||
let meta = Metadata23::parse_metadata(s.as_bytes());
|
||||
assert!(matches!(meta, Err(Error::InvalidName(_))));
|
||||
assert!(matches!(meta, Err(MetadataError::InvalidName(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_pkg_info() {
|
||||
let s = "Metadata-Version: 2.1";
|
||||
let meta = Metadata23::parse_pkg_info(s.as_bytes());
|
||||
assert!(matches!(meta, Err(Error::UnsupportedMetadataVersion(_))));
|
||||
assert!(matches!(
|
||||
meta,
|
||||
Err(MetadataError::UnsupportedMetadataVersion(_))
|
||||
));
|
||||
|
||||
let s = "Metadata-Version: 2.2\nName: asdf";
|
||||
let meta = Metadata23::parse_pkg_info(s.as_bytes());
|
||||
assert!(matches!(meta, Err(Error::FieldNotFound("Version"))));
|
||||
assert!(matches!(meta, Err(MetadataError::FieldNotFound("Version"))));
|
||||
|
||||
let s = "Metadata-Version: 2.3\nName: asdf";
|
||||
let meta = Metadata23::parse_pkg_info(s.as_bytes());
|
||||
assert!(matches!(meta, Err(Error::FieldNotFound("Version"))));
|
||||
assert!(matches!(meta, Err(MetadataError::FieldNotFound("Version"))));
|
||||
|
||||
let s = "Metadata-Version: 2.3\nName: asdf\nVersion: 1.0";
|
||||
let meta = Metadata23::parse_pkg_info(s.as_bytes()).unwrap();
|
||||
|
@ -428,7 +414,7 @@ mod tests {
|
|||
|
||||
let s = "Metadata-Version: 2.3\nName: asdf\nVersion: 1.0\nDynamic: Requires-Dist";
|
||||
let meta = Metadata23::parse_pkg_info(s.as_bytes()).unwrap_err();
|
||||
assert!(matches!(meta, Error::DynamicField("Requires-Dist")));
|
||||
assert!(matches!(meta, MetadataError::DynamicField("Requires-Dist")));
|
||||
|
||||
let s = "Metadata-Version: 2.3\nName: asdf\nVersion: 1.0\nRequires-Dist: foo";
|
||||
let meta = Metadata23::parse_pkg_info(s.as_bytes()).unwrap();
|
||||
|
@ -444,7 +430,7 @@ mod tests {
|
|||
name = "asdf"
|
||||
"#;
|
||||
let meta = Metadata23::parse_pyproject_toml(s);
|
||||
assert!(matches!(meta, Err(Error::FieldNotFound("version"))));
|
||||
assert!(matches!(meta, Err(MetadataError::FieldNotFound("version"))));
|
||||
|
||||
let s = r#"
|
||||
[project]
|
||||
|
@ -452,7 +438,7 @@ mod tests {
|
|||
dynamic = ["version"]
|
||||
"#;
|
||||
let meta = Metadata23::parse_pyproject_toml(s);
|
||||
assert!(matches!(meta, Err(Error::DynamicField("version"))));
|
||||
assert!(matches!(meta, Err(MetadataError::DynamicField("version"))));
|
||||
|
||||
let s = r#"
|
||||
[project]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue