Skip to content

Commit 4f24ced

Browse files
committed
Added at_most_one
1 parent 853f064 commit 4f24ced

File tree

3 files changed

+55
-0
lines changed

3 files changed

+55
-0
lines changed

src/lib.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3145,6 +3145,42 @@ pub trait Itertools : Iterator {
31453145
}
31463146
}
31473147

3148+
/// If the iterator yields no elements, Ok(None) will be returned. If the iterator yields
3149+
/// exactly one element, that element will be returned, otherwise an error will be returned
3150+
/// containing an iterator that has the same output as the input iterator.
3151+
///
3152+
/// This provides an additional layer of validation over just calling `Iterator::next()`.
3153+
/// If your assumption that there should be at most one element yielded is false this provides
3154+
/// the opportunity to detect and handle that, preventing errors at a distance.
3155+
///
3156+
/// # Examples
3157+
/// ```
3158+
/// use itertools::Itertools;
3159+
///
3160+
/// assert_eq!((0..10).filter(|&x| x == 2).at_most_one().unwrap(), Some(2));
3161+
/// assert!((0..10).filter(|&x| x > 1 && x < 4).at_most_one().unwrap_err().eq(2..4));
3162+
/// assert!((0..10).filter(|&x| x > 1 && x < 5).at_most_one().unwrap_err().eq(2..5));
3163+
/// assert_eq!((0..10).filter(|&_| false).at_most_one().unwrap(), None);
3164+
/// ```
3165+
fn at_most_one(mut self) -> Result<Option<Self::Item>, ExactlyOneError<Self>>
3166+
where
3167+
Self: Sized,
3168+
{
3169+
match self.next() {
3170+
Some(first) => {
3171+
match self.next() {
3172+
Some(second) => {
3173+
Err(ExactlyOneError::new(Some(Either::Left([first, second])), self))
3174+
}
3175+
None => {
3176+
Ok(Some(first))
3177+
}
3178+
}
3179+
}
3180+
None => Ok(None),
3181+
}
3182+
}
3183+
31483184
/// An iterator adaptor that allows the user to peek at multiple `.next()`
31493185
/// values without advancing the base iterator.
31503186
///

tests/quick.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,17 @@ quickcheck! {
12031203
}
12041204
}
12051205

1206+
quickcheck! {
1207+
fn at_most_one_i32(a: Vec<i32>) -> TestResult {
1208+
let ret = a.iter().cloned().at_most_one();
1209+
match a.len() {
1210+
0 => TestResult::from_bool(ret.unwrap() == None),
1211+
1 => TestResult::from_bool(ret.unwrap() == Some(a[0])),
1212+
_ => TestResult::from_bool(ret.unwrap_err().eq(a.iter().cloned())),
1213+
}
1214+
}
1215+
}
1216+
12061217
quickcheck! {
12071218
fn consistent_grouping_map_with_by(a: Vec<u8>, modulo: u8) -> () {
12081219
let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0`

tests/test_core.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,14 @@ fn exactly_one() {
253253
assert!((0..10).filter(|&_| false).exactly_one().unwrap_err().eq(0..0));
254254
}
255255

256+
#[test]
257+
fn at_most_one() {
258+
assert_eq!((0..10).filter(|&x| x == 2).at_most_one().unwrap(), Some(2));
259+
assert!((0..10).filter(|&x| x > 1 && x < 4).at_most_one().unwrap_err().eq(2..4));
260+
assert!((0..10).filter(|&x| x > 1 && x < 5).at_most_one().unwrap_err().eq(2..5));
261+
assert_eq!((0..10).filter(|&_| false).at_most_one().unwrap(), None);
262+
}
263+
256264
#[test]
257265
fn sum1() {
258266
let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

0 commit comments

Comments
 (0)