mirror of
				https://github.com/astral-sh/uv.git
				synced 2025-10-31 12:06:13 +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", | ||||
|  "itertools 0.14.0", | ||||
|  "regex", | ||||
|  "rkyv", | ||||
|  "rustc-hash", | ||||
|  "schemars", | ||||
|  "serde", | ||||
|  |  | |||
|  | @ -97,8 +97,21 @@ impl Default for DefaultExtras { | |||
| /// See:
 | ||||
| /// - <https://peps.python.org/pep-0685/#specification/>
 | ||||
| /// - <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))] | ||||
| #[rkyv(derive(Debug))] | ||||
| pub struct ExtraName(SmallString); | ||||
| 
 | ||||
| impl ExtraName { | ||||
|  |  | |||
|  | @ -17,8 +17,20 @@ use crate::{ | |||
| /// See:
 | ||||
| /// - <https://peps.python.org/pep-0735/>
 | ||||
| /// - <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))] | ||||
| #[rkyv(derive(Debug))] | ||||
| pub struct GroupName(SmallString); | ||||
| 
 | ||||
| impl GroupName { | ||||
|  |  | |||
|  | @ -30,6 +30,7 @@ boxcar = { workspace = true } | |||
| indexmap = { workspace = true } | ||||
| itertools = { workspace = true } | ||||
| regex = { workspace = true } | ||||
| rkyv = { workspace = true, optional = true } | ||||
| rustc-hash = { workspace = true } | ||||
| schemars = { workspace = true, optional = true } | ||||
| serde = { workspace = true, features = ["derive", "rc"] } | ||||
|  | @ -56,5 +57,6 @@ schemars = ["dep:schemars"] | |||
| # be supported. | ||||
| non-pep508-extensions = [] | ||||
| default = [] | ||||
| rkyv = ["dep:rkyv"] | ||||
| # Match the API of the published crate, for compatibility. | ||||
| 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)] | ||||
| mod tests { | ||||
|     //! 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-normalize = { workspace = true } | ||||
| uv-pep440 = { workspace = true } | ||||
| uv-pep508 = { workspace = true } | ||||
| uv-pep508 = { workspace = true, features = ["rkyv"] } | ||||
| uv-redacted = { workspace = true } | ||||
| uv-small-str = { workspace = true } | ||||
| 
 | ||||
|  |  | |||
|  | @ -19,8 +19,11 @@ use crate::{LenientVersionSpecifiers, MetadataError, VerbatimParsedUrl, metadata | |||
| /// fields that are relevant to dependency resolution.
 | ||||
| ///
 | ||||
| /// 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")] | ||||
| #[rkyv(derive(Debug))] | ||||
| pub struct ResolutionMetadata { | ||||
|     // Mandatory fields
 | ||||
|     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 { | ||||
|     pub fn is_available(&self) -> bool { | ||||
|         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 { | ||||
|     pub fn is_yanked(&self) -> bool { | ||||
|         match self { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Charlie Marsh
						Charlie Marsh