@@ -388,10 +388,10 @@ impl Version {
388388
389389 /// Returns the local segments in this version, if any exist.
390390 #[ inline]
391- pub fn local ( & self ) -> & [ LocalSegment ] {
391+ pub fn local ( & self ) -> LocalVersionSlice {
392392 match * self . inner {
393393 VersionInner :: Small { ref small } => small. local ( ) ,
394- VersionInner :: Full { ref full } => & full. local ,
394+ VersionInner :: Full { ref full } => full. local . as_slice ( ) ,
395395 }
396396 }
397397
@@ -530,15 +530,28 @@ impl Version {
530530 /// Set the local segments and return the updated version.
531531 #[ inline]
532532 #[ must_use]
533- pub fn with_local ( mut self , value : Vec < LocalSegment > ) -> Self {
533+ pub fn with_local_segments ( mut self , value : Vec < LocalSegment > ) -> Self {
534534 if value. is_empty ( ) {
535535 self . without_local ( )
536536 } else {
537- self . make_full ( ) . local = value;
537+ self . make_full ( ) . local = LocalVersion :: Segments ( value) ;
538538 self
539539 }
540540 }
541541
542+ /// Set the local version and return the updated version.
543+ #[ inline]
544+ #[ must_use]
545+ pub fn with_local ( mut self , value : LocalVersion ) -> Self {
546+ match value {
547+ LocalVersion :: Segments ( segments) => self . with_local_segments ( segments) ,
548+ LocalVersion :: Max => {
549+ self . make_full ( ) . local = value;
550+ self
551+ }
552+ }
553+ }
554+
542555 /// For PEP 440 specifier matching: "Except where specifically noted below,
543556 /// local version identifiers MUST NOT be permitted in version specifiers,
544557 /// and local version labels MUST be ignored entirely when checking if
@@ -615,7 +628,7 @@ impl Version {
615628 pre : small. pre ( ) ,
616629 post : small. post ( ) ,
617630 dev : small. dev ( ) ,
618- local : vec ! [ ] ,
631+ local : LocalVersion :: Segments ( vec ! [ ] ) ,
619632 } ;
620633 * self = Self {
621634 inner : Arc :: new ( VersionInner :: Full { full } ) ,
@@ -712,14 +725,12 @@ impl std::fmt::Display for Version {
712725 let local = if self . local ( ) . is_empty ( ) {
713726 String :: new ( )
714727 } else {
715- format ! (
716- "+{}" ,
717- self . local( )
718- . iter( )
719- . map( ToString :: to_string)
720- . collect:: <Vec <String >>( )
721- . join( "." )
722- )
728+ match self . local ( ) {
729+ LocalVersionSlice :: Segments ( _) => {
730+ format ! ( "+{}" , self . local( ) )
731+ }
732+ LocalVersionSlice :: Max => String :: new ( ) ,
733+ }
723734 } ;
724735 write ! ( f, "{epoch}{release}{pre}{post}{dev}{local}" )
725736 }
@@ -1195,10 +1206,10 @@ impl VersionSmall {
11951206
11961207 #[ inline]
11971208 #[ allow( clippy:: unused_self) ]
1198- fn local ( & self ) -> & [ LocalSegment ] {
1209+ fn local ( & self ) -> LocalVersionSlice {
11991210 // A "small" version is never used if the version has a non-zero number
12001211 // of local segments.
1201- & [ ]
1212+ LocalVersionSlice :: Segments ( & [ ] )
12021213 }
12031214
12041215 #[ inline]
@@ -1283,7 +1294,7 @@ struct VersionFull {
12831294 ///
12841295 /// Local versions allow multiple segments separated by periods, such as `deadbeef.1.2.3`, see
12851296 /// [`LocalSegment`] for details on the semantics.
1286- local : Vec < LocalSegment > ,
1297+ local : LocalVersion ,
12871298 /// An internal-only segment that does not exist in PEP 440, used to
12881299 /// represent the smallest possible version of a release, preceding any
12891300 /// `dev`, `pre`, `post` or releases.
@@ -1414,6 +1425,93 @@ impl std::fmt::Display for Prerelease {
14141425 }
14151426}
14161427
1428+ /// Either a sequence of local segments or [`LocalVersion::Sentinel`], an internal-only value that
1429+ /// compares greater than all other local versions.
1430+ #[ derive( Eq , PartialEq , Debug , Clone , Hash ) ]
1431+ #[ cfg_attr(
1432+ feature = "rkyv" ,
1433+ derive( rkyv:: Archive , rkyv:: Deserialize , rkyv:: Serialize )
1434+ ) ]
1435+ #[ cfg_attr( feature = "rkyv" , rkyv( derive( Debug , Eq , PartialEq , PartialOrd , Ord ) ) ) ]
1436+ pub enum LocalVersion {
1437+ /// A sequence of local segments.
1438+ Segments ( Vec < LocalSegment > ) ,
1439+ /// An internal-only value that compares greater to all other local versions.
1440+ Max ,
1441+ }
1442+
1443+ /// Like [`LocalVersion`], but using a slice
1444+ #[ derive( Eq , PartialEq , Debug , Clone , Hash ) ]
1445+ pub enum LocalVersionSlice < ' a > {
1446+ /// Like [`LocalVersion::Segments`]
1447+ Segments ( & ' a [ LocalSegment ] ) ,
1448+ /// Like [`LocalVersion::Sentinel`]
1449+ Max ,
1450+ }
1451+
1452+ impl LocalVersion {
1453+ /// Convert the local version segments into a slice.
1454+ pub fn as_slice ( & self ) -> LocalVersionSlice < ' _ > {
1455+ match self {
1456+ LocalVersion :: Segments ( segments) => LocalVersionSlice :: Segments ( segments) ,
1457+ LocalVersion :: Max => LocalVersionSlice :: Max ,
1458+ }
1459+ }
1460+
1461+ /// Clear the local version segments, if they exist.
1462+ pub fn clear ( & mut self ) {
1463+ match self {
1464+ Self :: Segments ( segments) => segments. clear ( ) ,
1465+ Self :: Max => * self = Self :: Segments ( Vec :: new ( ) ) ,
1466+ }
1467+ }
1468+ }
1469+
1470+ /// Output the local version identifier string.
1471+ ///
1472+ /// [`LocalVersionSlice::Max`] maps to `"[max]"` which is otherwise an illegal local
1473+ /// version because `[` and `]` are not allowed.
1474+ impl std:: fmt:: Display for LocalVersionSlice < ' _ > {
1475+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
1476+ match self {
1477+ LocalVersionSlice :: Segments ( segments) => {
1478+ for ( i, segment) in segments. iter ( ) . enumerate ( ) {
1479+ if i > 0 {
1480+ write ! ( f, "." ) ?;
1481+ }
1482+ write ! ( f, "{segment}" ) ?;
1483+ }
1484+ Ok ( ( ) )
1485+ }
1486+ LocalVersionSlice :: Max => write ! ( f, "[max]" ) ,
1487+ }
1488+ }
1489+ }
1490+
1491+ impl PartialOrd for LocalVersionSlice < ' _ > {
1492+ fn partial_cmp ( & self , other : & Self ) -> Option < Ordering > {
1493+ Some ( self . cmp ( other) )
1494+ }
1495+ }
1496+
1497+ impl Ord for LocalVersionSlice < ' _ > {
1498+ fn cmp ( & self , other : & Self ) -> Ordering {
1499+ match ( self , other) {
1500+ ( LocalVersionSlice :: Segments ( lv1) , LocalVersionSlice :: Segments ( lv2) ) => lv1. cmp ( lv2) ,
1501+ ( LocalVersionSlice :: Segments ( _) , LocalVersionSlice :: Max ) => Ordering :: Less ,
1502+ ( LocalVersionSlice :: Max , LocalVersionSlice :: Segments ( _) ) => Ordering :: Greater ,
1503+ ( LocalVersionSlice :: Max , LocalVersionSlice :: Max ) => Ordering :: Equal ,
1504+ }
1505+ }
1506+ }
1507+
1508+ impl LocalVersionSlice < ' _ > {
1509+ /// Whether the local version is absent
1510+ pub fn is_empty ( & self ) -> bool {
1511+ matches ! ( self , Self :: Segments ( & [ ] ) )
1512+ }
1513+ }
1514+
14171515/// A part of the [local version identifier](<https://peps.python.org/pep-0440/#local-version-identifiers>)
14181516///
14191517/// Local versions are a mess:
@@ -1855,7 +1953,7 @@ impl<'a> Parser<'a> {
18551953 . with_pre ( self . pre )
18561954 . with_post ( self . post )
18571955 . with_dev ( self . dev )
1858- . with_local ( self . local ) ;
1956+ . with_local ( LocalVersion :: Segments ( self . local ) ) ;
18591957 VersionPattern {
18601958 version,
18611959 wildcard : self . wildcard ,
@@ -2326,7 +2424,7 @@ pub(crate) fn compare_release(this: &[u64], other: &[u64]) -> Ordering {
23262424/// implementation
23272425///
23282426/// [pep440-suffix-ordering]: https://peps.python.org/pep-0440/#summary-of-permitted-suffixes-and-relative-ordering
2329- fn sortable_tuple ( version : & Version ) -> ( u64 , u64 , Option < u64 > , u64 , & [ LocalSegment ] ) {
2427+ fn sortable_tuple ( version : & Version ) -> ( u64 , u64 , Option < u64 > , u64 , LocalVersionSlice ) {
23302428 // If the version is a "max" version, use a post version larger than any possible post version.
23312429 let post = if version. max ( ) . is_some ( ) {
23322430 Some ( u64:: MAX )
0 commit comments