@@ -283,6 +283,36 @@ impl<V: Ord> Ranges<V> {
283283 }
284284 }
285285
286+ /// We want to use `iterator_try_collect`, but since it's unstable at the time of writing,
287+ /// we expose a public `FromIterator<(Bound<V>, Bound<V>)>` method and use this for internal
288+ /// testing.
289+ fn try_from (
290+ into_iter : impl IntoIterator < Item = ( Bound < V > , Bound < V > ) > ,
291+ ) -> Result < Self , FromIterError > {
292+ let mut iter = into_iter. into_iter ( ) ;
293+ let Some ( mut previous) = iter. next ( ) else {
294+ return Ok ( Self {
295+ segments : SmallVec :: new ( ) ,
296+ } ) ;
297+ } ;
298+ let mut segments = SmallVec :: with_capacity ( iter. size_hint ( ) . 0 ) ;
299+ for current in iter {
300+ if !valid_segment ( & previous. start_bound ( ) , & previous. end_bound ( ) ) {
301+ return Err ( FromIterError :: InvalidSegment ) ;
302+ }
303+ if !end_before_start_with_gap ( & previous. end_bound ( ) , & current. start_bound ( ) ) {
304+ return Err ( FromIterError :: OverlappingSegments ) ;
305+ }
306+ segments. push ( previous) ;
307+ previous = current;
308+ }
309+ if !valid_segment ( & previous. start_bound ( ) , & previous. end_bound ( ) ) {
310+ return Err ( FromIterError :: InvalidSegment ) ;
311+ }
312+ segments. push ( previous) ;
313+ Ok ( Self { segments } )
314+ }
315+
286316 fn check_invariants ( self ) -> Self {
287317 if cfg ! ( debug_assertions) {
288318 for p in self . segments . as_slice ( ) . windows ( 2 ) {
@@ -826,6 +856,40 @@ impl<V: Ord + Clone> Ranges<V> {
826856 }
827857}
828858
859+ /// User provided segment iterator breaks [`Ranges`] invariants.
860+ ///
861+ /// Not user accessible since `FromIterator<(Bound<V>, Bound<V>)>` panics and `iterator_try_collect`
862+ /// is unstable.
863+ #[ derive( Debug , PartialEq , Eq ) ]
864+ enum FromIterError {
865+ /// The start of a segment must be before its end, and a segment must contain at least one
866+ /// version.
867+ InvalidSegment ,
868+ /// The end of a segment is not before the start of the next segment, leaving at least one
869+ /// version space.
870+ OverlappingSegments ,
871+ }
872+
873+ impl Display for FromIterError {
874+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
875+ match self {
876+ FromIterError :: InvalidSegment => f. write_str ( "segment must be valid" ) ,
877+ FromIterError :: OverlappingSegments => {
878+ f. write_str ( "end of a segment and start of the next segment must not overlap" )
879+ }
880+ }
881+ }
882+ }
883+
884+ impl < V : Ord > FromIterator < ( Bound < V > , Bound < V > ) > for Ranges < V > {
885+ /// Construct a [`Ranges`] from an ordered list of bounds.
886+ ///
887+ /// Panics if the bounds aren't sorted, are empty or have no space to the next bound.
888+ fn from_iter < T : IntoIterator < Item = ( Bound < V > , Bound < V > ) > > ( iter : T ) -> Self {
889+ Self :: try_from ( iter) . unwrap ( )
890+ }
891+ }
892+
829893// REPORT ######################################################################
830894
831895impl < V : Display + Eq > Display for Ranges < V > {
@@ -1130,6 +1194,24 @@ pub mod tests {
11301194 }
11311195 assert!( simp. segments. len( ) <= range. segments. len( ) )
11321196 }
1197+
1198+ #[ test]
1199+ fn from_iter_valid( segments in proptest:: collection:: vec( any:: <( Bound <u32 >, Bound <u32 >) >( ) , ..30 ) ) {
1200+ match Ranges :: try_from( segments. clone( ) ) {
1201+ Ok ( ranges) => {
1202+ ranges. check_invariants( ) ;
1203+ }
1204+ Err ( _) => {
1205+ assert!(
1206+ segments
1207+ . as_slice( )
1208+ . windows( 2 )
1209+ . any( |p| !end_before_start_with_gap( & p[ 0 ] . 1 , & p[ 1 ] . 0 ) )
1210+ || segments. iter( ) . any( |( start, end) | !valid_segment( start, end) )
1211+ ) ;
1212+ }
1213+ }
1214+ }
11331215 }
11341216
11351217 #[ test]
@@ -1194,4 +1276,37 @@ pub mod tests {
11941276 version_reverse_sorted. sort ( ) ;
11951277 assert_eq ! ( version_reverse_sorted, versions) ;
11961278 }
1279+
1280+ /// Test all error conditions in [`Ranges::try_from`].
1281+ #[ test]
1282+ fn from_iter_errors ( ) {
1283+ // Unbounded in not at an end
1284+ let result = Ranges :: try_from ( [
1285+ ( Bound :: Included ( 1 ) , Bound :: Unbounded ) ,
1286+ ( Bound :: Included ( 2 ) , Bound :: Unbounded ) ,
1287+ ] ) ;
1288+ assert_eq ! ( result, Err ( FromIterError :: OverlappingSegments ) ) ;
1289+ // Not a version in between
1290+ let result = Ranges :: try_from ( [
1291+ ( Bound :: Included ( 1 ) , Bound :: Excluded ( 2 ) ) ,
1292+ ( Bound :: Included ( 2 ) , Bound :: Unbounded ) ,
1293+ ] ) ;
1294+ assert_eq ! ( result, Err ( FromIterError :: OverlappingSegments ) ) ;
1295+ // First segment
1296+ let result = Ranges :: try_from ( [ ( Bound :: Excluded ( 2 ) , Bound :: Included ( 2 ) ) ] ) ;
1297+ assert_eq ! ( result, Err ( FromIterError :: InvalidSegment ) ) ;
1298+ // Middle segment
1299+ let result = Ranges :: try_from ( [
1300+ ( Bound :: Included ( 1 ) , Bound :: Included ( 2 ) ) ,
1301+ ( Bound :: Included ( 3 ) , Bound :: Included ( 2 ) ) ,
1302+ ( Bound :: Included ( 4 ) , Bound :: Included ( 5 ) ) ,
1303+ ] ) ;
1304+ assert_eq ! ( result, Err ( FromIterError :: InvalidSegment ) ) ;
1305+ // Last segment
1306+ let result = Ranges :: try_from ( [
1307+ ( Bound :: Included ( 1 ) , Bound :: Included ( 2 ) ) ,
1308+ ( Bound :: Included ( 3 ) , Bound :: Included ( 2 ) ) ,
1309+ ] ) ;
1310+ assert_eq ! ( result, Err ( FromIterError :: InvalidSegment ) ) ;
1311+ }
11971312}
0 commit comments