@@ -283,6 +283,36 @@ impl<V: Ord> Ranges<V> {
283
283
}
284
284
}
285
285
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
+
286
316
fn check_invariants ( self ) -> Self {
287
317
if cfg ! ( debug_assertions) {
288
318
for p in self . segments . as_slice ( ) . windows ( 2 ) {
@@ -826,6 +856,40 @@ impl<V: Ord + Clone> Ranges<V> {
826
856
}
827
857
}
828
858
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
+
829
893
// REPORT ######################################################################
830
894
831
895
impl < V : Display + Eq > Display for Ranges < V > {
@@ -1130,6 +1194,24 @@ pub mod tests {
1130
1194
}
1131
1195
assert!( simp. segments. len( ) <= range. segments. len( ) )
1132
1196
}
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
+ }
1133
1215
}
1134
1216
1135
1217
#[ test]
@@ -1194,4 +1276,37 @@ pub mod tests {
1194
1276
version_reverse_sorted. sort ( ) ;
1195
1277
assert_eq ! ( version_reverse_sorted, versions) ;
1196
1278
}
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
+ }
1197
1312
}
0 commit comments