mirror of
				https://github.com/astral-sh/uv.git
				synced 2025-10-29 19:17:26 +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
	
	 Charlie Marsh
						Charlie Marsh