Skip to content

Commit

Permalink
Merge #523
Browse files Browse the repository at this point in the history
523: Added at_most_one r=jswrenn a=insideoutclub



Co-authored-by: David Sanders <insideoutclub@gmail.com>
  • Loading branch information
bors[bot] and insideoutclub committed Apr 21, 2021
2 parents 5d1d4aa + 4f24ced commit 9326260
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 0 deletions.
36 changes: 36 additions & 0 deletions src/lib.rs
Expand Up @@ -3213,6 +3213,42 @@ pub trait Itertools : Iterator {
}
}

/// If the iterator yields no elements, Ok(None) will be returned. If the iterator yields
/// exactly one element, that element will be returned, otherwise an error will be returned
/// containing an iterator that has the same output as the input iterator.
///
/// This provides an additional layer of validation over just calling `Iterator::next()`.
/// If your assumption that there should be at most one element yielded is false this provides
/// the opportunity to detect and handle that, preventing errors at a distance.
///
/// # Examples
/// ```
/// use itertools::Itertools;
///
/// assert_eq!((0..10).filter(|&x| x == 2).at_most_one().unwrap(), Some(2));
/// assert!((0..10).filter(|&x| x > 1 && x < 4).at_most_one().unwrap_err().eq(2..4));
/// assert!((0..10).filter(|&x| x > 1 && x < 5).at_most_one().unwrap_err().eq(2..5));
/// assert_eq!((0..10).filter(|&_| false).at_most_one().unwrap(), None);
/// ```
fn at_most_one(mut self) -> Result<Option<Self::Item>, ExactlyOneError<Self>>
where
Self: Sized,
{
match self.next() {
Some(first) => {
match self.next() {
Some(second) => {
Err(ExactlyOneError::new(Some(Either::Left([first, second])), self))
}
None => {
Ok(Some(first))
}
}
}
None => Ok(None),
}
}

/// An iterator adaptor that allows the user to peek at multiple `.next()`
/// values without advancing the base iterator.
///
Expand Down
11 changes: 11 additions & 0 deletions tests/quick.rs
Expand Up @@ -1203,6 +1203,17 @@ quickcheck! {
}
}

quickcheck! {
fn at_most_one_i32(a: Vec<i32>) -> TestResult {
let ret = a.iter().cloned().at_most_one();
match a.len() {
0 => TestResult::from_bool(ret.unwrap() == None),
1 => TestResult::from_bool(ret.unwrap() == Some(a[0])),
_ => TestResult::from_bool(ret.unwrap_err().eq(a.iter().cloned())),
}
}
}

quickcheck! {
fn consistent_grouping_map_with_by(a: Vec<u8>, modulo: u8) -> () {
let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0`
Expand Down
8 changes: 8 additions & 0 deletions tests/test_core.rs
Expand Up @@ -276,6 +276,14 @@ fn exactly_one() {
assert!((0..10).filter(|&_| false).exactly_one().unwrap_err().eq(0..0));
}

#[test]
fn at_most_one() {
assert_eq!((0..10).filter(|&x| x == 2).at_most_one().unwrap(), Some(2));
assert!((0..10).filter(|&x| x > 1 && x < 4).at_most_one().unwrap_err().eq(2..4));
assert!((0..10).filter(|&x| x > 1 && x < 5).at_most_one().unwrap_err().eq(2..5));
assert_eq!((0..10).filter(|&_| false).at_most_one().unwrap(), None);
}

#[test]
fn sum1() {
let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
Expand Down

0 comments on commit 9326260

Please sign in to comment.