@@ -283,6 +283,39 @@ impl<V: Ord> Ranges<V> {
283
283
}
284
284
}
285
285
286
+ /// Construct from segments already fulfilling the [`Ranges`] invariants.
287
+ ///
288
+ /// 1. The segments are sorted, from lowest to highest (through `Ord`).
289
+ /// 2. Each segment contains at least one version (start < end).
290
+ /// 3. There is at least one version between two segments.
291
+ pub fn from_normalized (
292
+ into_iter : impl IntoIterator < Item = ( Bound < V > , Bound < V > ) > ,
293
+ ) -> Result < Self , FromIterError > {
294
+ let mut iter = into_iter. into_iter ( ) ;
295
+ let Some ( mut previous) = iter. next ( ) else {
296
+ return Ok ( Self {
297
+ segments : SmallVec :: new ( ) ,
298
+ } ) ;
299
+ } ;
300
+ let mut segments = SmallVec :: with_capacity ( iter. size_hint ( ) . 0 ) ;
301
+ for current in iter {
302
+ if !valid_segment ( & previous. start_bound ( ) , & previous. end_bound ( ) ) {
303
+ return Err ( FromIterError :: InvalidSegment ) ;
304
+ }
305
+ if !end_before_start_with_gap ( & previous. end_bound ( ) , & current. start_bound ( ) ) {
306
+ return Err ( FromIterError :: OverlappingSegments ) ;
307
+ }
308
+ segments. push ( previous) ;
309
+ previous = current;
310
+ }
311
+ if !valid_segment ( & previous. start_bound ( ) , & previous. end_bound ( ) ) {
312
+ return Err ( FromIterError :: InvalidSegment ) ;
313
+ }
314
+ segments. push ( previous) ;
315
+ Ok ( Self { segments } )
316
+ }
317
+
318
+ /// See `Ranges` docstring for the invariants.
286
319
fn check_invariants ( self ) -> Self {
287
320
if cfg ! ( debug_assertions) {
288
321
for p in self . segments . as_slice ( ) . windows ( 2 ) {
@@ -418,6 +451,31 @@ fn cmp_bounds_end<V: PartialOrd>(left: Bound<&V>, right: Bound<&V>) -> Option<Or
418
451
} )
419
452
}
420
453
454
+ /// User provided segment iterator breaks [`Ranges`] invariants.
455
+ ///
456
+ /// Not user accessible since `FromIterator<(Bound<V>, Bound<V>)>` panics and `iterator_try_collect`
457
+ /// is unstable.
458
+ #[ derive( Debug , PartialEq , Eq ) ]
459
+ pub enum FromIterError {
460
+ /// The start of a segment must be before its end, and a segment must contain at least one
461
+ /// version.
462
+ InvalidSegment ,
463
+ /// The end of a segment is not before the start of the next segment, leaving at least one
464
+ /// version space.
465
+ OverlappingSegments ,
466
+ }
467
+
468
+ impl Display for FromIterError {
469
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
470
+ match self {
471
+ FromIterError :: InvalidSegment => f. write_str ( "segment must be valid" ) ,
472
+ FromIterError :: OverlappingSegments => {
473
+ f. write_str ( "end of a segment and start of the next segment must not overlap" )
474
+ }
475
+ }
476
+ }
477
+ }
478
+
421
479
impl < V : PartialOrd > PartialOrd for Ranges < V > {
422
480
/// A simple ordering scheme where we zip the segments and compare all bounds in order. If all
423
481
/// bounds are equal, the longer range is considered greater. (And if all zipped bounds are
@@ -1130,6 +1188,24 @@ pub mod tests {
1130
1188
}
1131
1189
assert!( simp. segments. len( ) <= range. segments. len( ) )
1132
1190
}
1191
+
1192
+ #[ test]
1193
+ fn from_normalized_valid( segments in proptest:: collection:: vec( any:: <( Bound <u32 >, Bound <u32 >) >( ) , ..30 ) ) {
1194
+ match Ranges :: from_normalized( segments. clone( ) ) {
1195
+ Ok ( ranges) => {
1196
+ ranges. check_invariants( ) ;
1197
+ }
1198
+ Err ( _) => {
1199
+ assert!(
1200
+ segments
1201
+ . as_slice( )
1202
+ . windows( 2 )
1203
+ . any( |p| !end_before_start_with_gap( & p[ 0 ] . 1 , & p[ 1 ] . 0 ) )
1204
+ || segments. iter( ) . any( |( start, end) | !valid_segment( start, end) )
1205
+ ) ;
1206
+ }
1207
+ }
1208
+ }
1133
1209
}
1134
1210
1135
1211
#[ test]
@@ -1194,4 +1270,37 @@ pub mod tests {
1194
1270
version_reverse_sorted. sort ( ) ;
1195
1271
assert_eq ! ( version_reverse_sorted, versions) ;
1196
1272
}
1273
+
1274
+ /// Test all error conditions in [`Ranges::from_normalized`].
1275
+ #[ test]
1276
+ fn from_iter_errors ( ) {
1277
+ // Unbounded in not at an end
1278
+ let result = Ranges :: from_normalized ( [
1279
+ ( Bound :: Included ( 1 ) , Bound :: Unbounded ) ,
1280
+ ( Bound :: Included ( 2 ) , Bound :: Unbounded ) ,
1281
+ ] ) ;
1282
+ assert_eq ! ( result, Err ( FromIterError :: OverlappingSegments ) ) ;
1283
+ // Not a version in between
1284
+ let result = Ranges :: from_normalized ( [
1285
+ ( Bound :: Included ( 1 ) , Bound :: Excluded ( 2 ) ) ,
1286
+ ( Bound :: Included ( 2 ) , Bound :: Unbounded ) ,
1287
+ ] ) ;
1288
+ assert_eq ! ( result, Err ( FromIterError :: OverlappingSegments ) ) ;
1289
+ // First segment
1290
+ let result = Ranges :: from_normalized ( [ ( Bound :: Excluded ( 2 ) , Bound :: Included ( 2 ) ) ] ) ;
1291
+ assert_eq ! ( result, Err ( FromIterError :: InvalidSegment ) ) ;
1292
+ // Middle segment
1293
+ let result = Ranges :: from_normalized ( [
1294
+ ( Bound :: Included ( 1 ) , Bound :: Included ( 2 ) ) ,
1295
+ ( Bound :: Included ( 3 ) , Bound :: Included ( 2 ) ) ,
1296
+ ( Bound :: Included ( 4 ) , Bound :: Included ( 5 ) ) ,
1297
+ ] ) ;
1298
+ assert_eq ! ( result, Err ( FromIterError :: InvalidSegment ) ) ;
1299
+ // Last segment
1300
+ let result = Ranges :: from_normalized ( [
1301
+ ( Bound :: Included ( 1 ) , Bound :: Included ( 2 ) ) ,
1302
+ ( Bound :: Included ( 3 ) , Bound :: Included ( 2 ) ) ,
1303
+ ] ) ;
1304
+ assert_eq ! ( result, Err ( FromIterError :: InvalidSegment ) ) ;
1305
+ }
1197
1306
}
0 commit comments