@@ -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,29 @@ 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
+ }
1209
+
1210
+ #[ test]
1211
+ fn from_iter_valid( segments in proptest:: collection:: vec( any:: <( Bound <u32 >, Bound <u32 >) >( ) , ..30 ) ) {
1212
+ Ranges :: from_iter( segments. clone( ) ) . check_invariants( ) ;
1213
+ }
1133
1214
}
1134
1215
1135
1216
#[ test]
@@ -1194,4 +1275,37 @@ pub mod tests {
1194
1275
version_reverse_sorted. sort ( ) ;
1195
1276
assert_eq ! ( version_reverse_sorted, versions) ;
1196
1277
}
1278
+
1279
+ /// Test all error conditions in [`Ranges::from_normalized`].
1280
+ #[ test]
1281
+ fn from_iter_errors ( ) {
1282
+ // Unbounded in not at an end
1283
+ let result = Ranges :: from_normalized ( [
1284
+ ( Bound :: Included ( 1 ) , Bound :: Unbounded ) ,
1285
+ ( Bound :: Included ( 2 ) , Bound :: Unbounded ) ,
1286
+ ] ) ;
1287
+ assert_eq ! ( result, Err ( FromIterError :: OverlappingSegments ) ) ;
1288
+ // Not a version in between
1289
+ let result = Ranges :: from_normalized ( [
1290
+ ( Bound :: Included ( 1 ) , Bound :: Excluded ( 2 ) ) ,
1291
+ ( Bound :: Included ( 2 ) , Bound :: Unbounded ) ,
1292
+ ] ) ;
1293
+ assert_eq ! ( result, Err ( FromIterError :: OverlappingSegments ) ) ;
1294
+ // First segment
1295
+ let result = Ranges :: from_normalized ( [ ( Bound :: Excluded ( 2 ) , Bound :: Included ( 2 ) ) ] ) ;
1296
+ assert_eq ! ( result, Err ( FromIterError :: InvalidSegment ) ) ;
1297
+ // Middle segment
1298
+ let result = Ranges :: from_normalized ( [
1299
+ ( Bound :: Included ( 1 ) , Bound :: Included ( 2 ) ) ,
1300
+ ( Bound :: Included ( 3 ) , Bound :: Included ( 2 ) ) ,
1301
+ ( Bound :: Included ( 4 ) , Bound :: Included ( 5 ) ) ,
1302
+ ] ) ;
1303
+ assert_eq ! ( result, Err ( FromIterError :: InvalidSegment ) ) ;
1304
+ // Last segment
1305
+ let result = Ranges :: from_normalized ( [
1306
+ ( Bound :: Included ( 1 ) , Bound :: Included ( 2 ) ) ,
1307
+ ( Bound :: Included ( 3 ) , Bound :: Included ( 2 ) ) ,
1308
+ ] ) ;
1309
+ assert_eq ! ( result, Err ( FromIterError :: InvalidSegment ) ) ;
1310
+ }
1197
1311
}
0 commit comments