mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-29 11:07:59 +00:00
Add rkyv implementation for Core Metadata (#15532)
## Summary Enables us to store Core Metadata in zero-copy format.
This commit is contained in:
parent
615e076beb
commit
b2c8f5ef68
8 changed files with 94 additions and 4 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -5915,6 +5915,7 @@ dependencies = [
|
||||||
"insta",
|
"insta",
|
||||||
"itertools 0.14.0",
|
"itertools 0.14.0",
|
||||||
"regex",
|
"regex",
|
||||||
|
"rkyv",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
||||||
|
|
@ -97,8 +97,21 @@ impl Default for DefaultExtras {
|
||||||
/// See:
|
/// See:
|
||||||
/// - <https://peps.python.org/pep-0685/#specification/>
|
/// - <https://peps.python.org/pep-0685/#specification/>
|
||||||
/// - <https://packaging.python.org/en/latest/specifications/name-normalization/>
|
/// - <https://packaging.python.org/en/latest/specifications/name-normalization/>
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
Clone,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Hash,
|
||||||
|
Serialize,
|
||||||
|
rkyv::Archive,
|
||||||
|
rkyv::Deserialize,
|
||||||
|
rkyv::Serialize,
|
||||||
|
)]
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
|
#[rkyv(derive(Debug))]
|
||||||
pub struct ExtraName(SmallString);
|
pub struct ExtraName(SmallString);
|
||||||
|
|
||||||
impl ExtraName {
|
impl ExtraName {
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,20 @@ use crate::{
|
||||||
/// See:
|
/// See:
|
||||||
/// - <https://peps.python.org/pep-0735/>
|
/// - <https://peps.python.org/pep-0735/>
|
||||||
/// - <https://packaging.python.org/en/latest/specifications/name-normalization/>
|
/// - <https://packaging.python.org/en/latest/specifications/name-normalization/>
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
Clone,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Hash,
|
||||||
|
rkyv::Archive,
|
||||||
|
rkyv::Deserialize,
|
||||||
|
rkyv::Serialize,
|
||||||
|
)]
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
|
#[rkyv(derive(Debug))]
|
||||||
pub struct GroupName(SmallString);
|
pub struct GroupName(SmallString);
|
||||||
|
|
||||||
impl GroupName {
|
impl GroupName {
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ boxcar = { workspace = true }
|
||||||
indexmap = { workspace = true }
|
indexmap = { workspace = true }
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
|
rkyv = { workspace = true, optional = true }
|
||||||
rustc-hash = { workspace = true }
|
rustc-hash = { workspace = true }
|
||||||
schemars = { workspace = true, optional = true }
|
schemars = { workspace = true, optional = true }
|
||||||
serde = { workspace = true, features = ["derive", "rc"] }
|
serde = { workspace = true, features = ["derive", "rc"] }
|
||||||
|
|
@ -56,5 +57,6 @@ schemars = ["dep:schemars"]
|
||||||
# be supported.
|
# be supported.
|
||||||
non-pep508-extensions = []
|
non-pep508-extensions = []
|
||||||
default = []
|
default = []
|
||||||
|
rkyv = ["dep:rkyv"]
|
||||||
# Match the API of the published crate, for compatibility.
|
# Match the API of the published crate, for compatibility.
|
||||||
serde = []
|
serde = []
|
||||||
|
|
|
||||||
|
|
@ -1045,6 +1045,41 @@ fn parse_pep508_requirement<T: Pep508Url>(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rkyv")]
|
||||||
|
/// An [`rkyv`] implementation for [`Requirement`].
|
||||||
|
impl<T: Pep508Url + Display> rkyv::Archive for Requirement<T> {
|
||||||
|
type Archived = rkyv::string::ArchivedString;
|
||||||
|
type Resolver = rkyv::string::StringResolver;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn resolve(&self, resolver: Self::Resolver, out: rkyv::Place<Self::Archived>) {
|
||||||
|
let as_str = self.to_string();
|
||||||
|
rkyv::string::ArchivedString::resolve_from_str(&as_str, resolver, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rkyv")]
|
||||||
|
impl<T: Pep508Url + Display, S> rkyv::Serialize<S> for Requirement<T>
|
||||||
|
where
|
||||||
|
S: rkyv::rancor::Fallible + rkyv::ser::Allocator + rkyv::ser::Writer + ?Sized,
|
||||||
|
S::Error: rkyv::rancor::Source,
|
||||||
|
{
|
||||||
|
fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
|
||||||
|
let as_str = self.to_string();
|
||||||
|
rkyv::string::ArchivedString::serialize_from_str(&as_str, serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rkyv")]
|
||||||
|
impl<T: Pep508Url + Display, D: rkyv::rancor::Fallible + ?Sized>
|
||||||
|
rkyv::Deserialize<Requirement<T>, D> for rkyv::string::ArchivedString
|
||||||
|
{
|
||||||
|
fn deserialize(&self, _deserializer: &mut D) -> Result<Requirement<T>, D::Error> {
|
||||||
|
// SAFETY: We only serialize valid requirements.
|
||||||
|
Ok(Requirement::<T>::from_str(self.as_str()).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
//! Half of these tests are copied from <https://github.com/pypa/packaging/pull/624>
|
//! Half of these tests are copied from <https://github.com/pypa/packaging/pull/624>
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ uv-distribution-filename = { workspace = true }
|
||||||
uv-git-types = { workspace = true }
|
uv-git-types = { workspace = true }
|
||||||
uv-normalize = { workspace = true }
|
uv-normalize = { workspace = true }
|
||||||
uv-pep440 = { workspace = true }
|
uv-pep440 = { workspace = true }
|
||||||
uv-pep508 = { workspace = true }
|
uv-pep508 = { workspace = true, features = ["rkyv"] }
|
||||||
uv-redacted = { workspace = true }
|
uv-redacted = { workspace = true }
|
||||||
uv-small-str = { workspace = true }
|
uv-small-str = { workspace = true }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,11 @@ use crate::{LenientVersionSpecifiers, MetadataError, VerbatimParsedUrl, metadata
|
||||||
/// fields that are relevant to dependency resolution.
|
/// fields that are relevant to dependency resolution.
|
||||||
///
|
///
|
||||||
/// Core Metadata 2.3 is specified in <https://packaging.python.org/specifications/core-metadata/>.
|
/// Core Metadata 2.3 is specified in <https://packaging.python.org/specifications/core-metadata/>.
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(
|
||||||
|
Serialize, Deserialize, Debug, Clone, rkyv::Archive, rkyv::Deserialize, rkyv::Serialize,
|
||||||
|
)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
#[rkyv(derive(Debug))]
|
||||||
pub struct ResolutionMetadata {
|
pub struct ResolutionMetadata {
|
||||||
// Mandatory fields
|
// Mandatory fields
|
||||||
pub name: PackageName,
|
pub name: PackageName,
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,18 @@ impl<'de> Deserialize<'de> for CoreMetadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Serialize for CoreMetadata {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
Self::Bool(is_available) => serializer.serialize_bool(*is_available),
|
||||||
|
Self::Hashes(hashes) => hashes.serialize(serializer),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl CoreMetadata {
|
impl CoreMetadata {
|
||||||
pub fn is_available(&self) -> bool {
|
pub fn is_available(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -169,6 +181,18 @@ impl<'de> Deserialize<'de> for Yanked {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Serialize for Yanked {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
Self::Bool(is_yanked) => serializer.serialize_bool(*is_yanked),
|
||||||
|
Self::Reason(reason) => serializer.serialize_str(reason.as_ref()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Yanked {
|
impl Yanked {
|
||||||
pub fn is_yanked(&self) -> bool {
|
pub fn is_yanked(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue