@@ -283,6 +283,39 @@ impl<V: Ord> Ranges<V> {
283283 }
284284 }
285285
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.
286319 fn check_invariants ( self ) -> Self {
287320 if cfg ! ( debug_assertions) {
288321 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
418451 } )
419452}
420453
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+
421479impl < V : PartialOrd > PartialOrd for Ranges < V > {
422480 /// A simple ordering scheme where we zip the segments and compare all bounds in order. If all
423481 /// bounds are equal, the longer range is considered greater. (And if all zipped bounds are
@@ -1130,6 +1188,29 @@ pub mod tests {
11301188 }
11311189 assert!( simp. segments. len( ) <= range. segments. len( ) )
11321190 }
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+ }
11331214 }
11341215
11351216 #[ test]
@@ -1194,4 +1275,37 @@ pub mod tests {
11941275 version_reverse_sorted. sort ( ) ;
11951276 assert_eq ! ( version_reverse_sorted, versions) ;
11961277 }
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+ }
11971311}
0 commit comments