From d2c81090ce38d44f1856c1f70dfae6eff3585ffc Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Tue, 22 Sep 2020 11:22:51 -0600 Subject: [PATCH 1/4] Implement Error for ExactlyOneError, change interals of it a bit --- src/exactly_one_err.rs | 92 +++++++++++++++++++++++++++++++++++------- src/lib.rs | 4 +- 2 files changed, 79 insertions(+), 17 deletions(-) diff --git a/src/exactly_one_err.rs b/src/exactly_one_err.rs index e4925ffb7..03897a6e6 100644 --- a/src/exactly_one_err.rs +++ b/src/exactly_one_err.rs @@ -1,5 +1,9 @@ +use std::error::Error; +use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::iter::ExactSizeIterator; +use either::Either; + use crate::size_hint; /// Iterator returned for the error case of `IterTools::exactly_one()` @@ -10,12 +14,12 @@ use crate::size_hint; /// /// This is very similar to PutBackN except this iterator only supports 0-2 elements and does not /// use a `Vec`. -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ExactlyOneError where I: Iterator, { - first_two: (Option, Option), + first_two: Option>, inner: I, } @@ -24,9 +28,17 @@ where I: Iterator, { /// Creates a new `ExactlyOneErr` iterator. - pub(crate) fn new(first_two: (Option, Option), inner: I) -> Self { + pub(crate) fn new(first_two: Option>, inner: I) -> Self { Self { first_two, inner } } + + fn additional_len(&self) -> usize { + match self.first_two { + Some(Either::Left(_)) => 2, + Some(Either::Right(_)) => 1, + None => 0, + } + } } impl Iterator for ExactlyOneError @@ -36,23 +48,73 @@ where type Item = I::Item; fn next(&mut self) -> Option { - self.first_two - .0 - .take() - .or_else(|| self.first_two.1.take()) - .or_else(|| self.inner.next()) + match self.first_two.take() { + Some(Either::Left([first, second])) => { + self.first_two = Some(Either::Right(second)); + Some(first) + }, + Some(Either::Right(second)) => { + Some(second) + } + None => { + self.inner.next() + } + } } fn size_hint(&self) -> (usize, Option) { - let mut additional_len = 0; - if self.first_two.0.is_some() { - additional_len += 1; + size_hint::add_scalar(self.inner.size_hint(), self.additional_len()) + } +} + + +impl ExactSizeIterator for ExactlyOneError where I: ExactSizeIterator {} + +impl Display for ExactlyOneError + where I: Iterator +{ + fn fmt(&self, f: &mut Formatter) -> FmtResult { + let additional = self.additional_len(); + if additional > 0 { + write!(f, "got at least 2 elements when exactly one was expected") + } else { + write!(f, "got zero elements when exactly one was expected") } - if self.first_two.1.is_some() { - additional_len += 1; + } +} + +impl Debug for ExactlyOneError + where I: Iterator, + I::Item: Debug, +{ + fn fmt(&self, f: &mut Formatter) -> FmtResult { + match &self.first_two { + Some(Either::Left([first, second])) => { + write!(f, "ExactlyOneError[{:?}, {:?}, ...]", first, second) + }, + Some(Either::Right(second)) => { + write!(f, "ExactlyOneError[{:?}, ...]", second) + } + None => { + write!(f, "ExactlyOneError[...]") + } } - size_hint::add_scalar(self.inner.size_hint(), additional_len) } } -impl ExactSizeIterator for ExactlyOneError where I: ExactSizeIterator {} +impl Error for ExactlyOneError where I: Iterator, I::Item: Debug, {} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn question_mark_syntax_works() { + question_mark_return().unwrap_err() + } + + fn question_mark_return() -> Result<(), impl Error> { + let x = Err(ExactlyOneError::new(None, []))?; + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 4568f649b..9ce015209 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2791,14 +2791,14 @@ pub trait Itertools : Iterator { Some(first) => { match self.next() { Some(second) => { - Err(ExactlyOneError::new((Some(first), Some(second)), self)) + Err(ExactlyOneError::new(Some(Either::Left([first, second])), self)) } None => { Ok(first) } } } - None => Err(ExactlyOneError::new((None, None), self)), + None => Err(ExactlyOneError::new(None, self)), } } From 252969f7137d555ff15db3f6feaed160156eecf2 Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Tue, 22 Sep 2020 12:20:37 -0600 Subject: [PATCH 2/4] Make availability of Error impl conditional --- src/exactly_one_err.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/exactly_one_err.rs b/src/exactly_one_err.rs index 03897a6e6..56c340b9b 100644 --- a/src/exactly_one_err.rs +++ b/src/exactly_one_err.rs @@ -1,5 +1,8 @@ +#[cfg(feature = "use_std")] use std::error::Error; +#[cfg(feature = "use_std")] use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; + use std::iter::ExactSizeIterator; use either::Either; @@ -70,6 +73,7 @@ where impl ExactSizeIterator for ExactlyOneError where I: ExactSizeIterator {} +#[cfg(feature = "use_std")] impl Display for ExactlyOneError where I: Iterator { @@ -83,6 +87,7 @@ impl Display for ExactlyOneError } } +#[cfg(feature = "use_std")] impl Debug for ExactlyOneError where I: Iterator, I::Item: Debug, @@ -102,9 +107,10 @@ impl Debug for ExactlyOneError } } +#[cfg(feature = "use_std")] impl Error for ExactlyOneError where I: Iterator, I::Item: Debug, {} -#[cfg(test)] +#[cfg(all(test, feature = "use_std"))] mod tests { use super::*; From 4850993569a308d284b0db498d82aa33de7b73cd Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Tue, 22 Sep 2020 13:59:18 -0600 Subject: [PATCH 3/4] Move test to right spot, and fix conditional compilation --- src/exactly_one_err.rs | 16 ---------------- tests/test_std.rs | 11 +++++++++++ 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/exactly_one_err.rs b/src/exactly_one_err.rs index 56c340b9b..cfa244c18 100644 --- a/src/exactly_one_err.rs +++ b/src/exactly_one_err.rs @@ -1,6 +1,5 @@ #[cfg(feature = "use_std")] use std::error::Error; -#[cfg(feature = "use_std")] use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::iter::ExactSizeIterator; @@ -73,7 +72,6 @@ where impl ExactSizeIterator for ExactlyOneError where I: ExactSizeIterator {} -#[cfg(feature = "use_std")] impl Display for ExactlyOneError where I: Iterator { @@ -87,7 +85,6 @@ impl Display for ExactlyOneError } } -#[cfg(feature = "use_std")] impl Debug for ExactlyOneError where I: Iterator, I::Item: Debug, @@ -110,17 +107,4 @@ impl Debug for ExactlyOneError #[cfg(feature = "use_std")] impl Error for ExactlyOneError where I: Iterator, I::Item: Debug, {} -#[cfg(all(test, feature = "use_std"))] -mod tests { - use super::*; - #[test] - fn question_mark_syntax_works() { - question_mark_return().unwrap_err() - } - - fn question_mark_return() -> Result<(), impl Error> { - let x = Err(ExactlyOneError::new(None, []))?; - Ok(()) - } -} diff --git a/tests/test_std.rs b/tests/test_std.rs index 9918592cb..24d0f0888 100644 --- a/tests/test_std.rs +++ b/tests/test_std.rs @@ -1,6 +1,7 @@ use permutohedron; use itertools as it; use crate::it::Itertools; +use crate::it::ExactlyOneError; use crate::it::multizip; use crate::it::multipeek; use crate::it::peek_nth; @@ -913,3 +914,13 @@ fn tree_fold1() { assert_eq!(actual, expected); } } + +#[test] +fn exactly_one_question_mark_syntax_works() { + exactly_one_question_mark_return().unwrap_err(); +} + +fn exactly_one_question_mark_return() -> Result<(), ExactlyOneError>> { + [].iter().exactly_one()?; + Ok(()) +} From 8d73e8f252567a061274f054e8b7ad601633ee38 Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Tue, 22 Sep 2020 14:28:39 -0600 Subject: [PATCH 4/4] Make Debug impl a bit more useful --- src/exactly_one_err.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/exactly_one_err.rs b/src/exactly_one_err.rs index cfa244c18..63485c993 100644 --- a/src/exactly_one_err.rs +++ b/src/exactly_one_err.rs @@ -73,7 +73,7 @@ where impl ExactSizeIterator for ExactlyOneError where I: ExactSizeIterator {} impl Display for ExactlyOneError - where I: Iterator + where I: Iterator, { fn fmt(&self, f: &mut Formatter) -> FmtResult { let additional = self.additional_len(); @@ -86,25 +86,25 @@ impl Display for ExactlyOneError } impl Debug for ExactlyOneError - where I: Iterator, + where I: Iterator + Debug, I::Item: Debug, { fn fmt(&self, f: &mut Formatter) -> FmtResult { match &self.first_two { Some(Either::Left([first, second])) => { - write!(f, "ExactlyOneError[{:?}, {:?}, ...]", first, second) + write!(f, "ExactlyOneError[First: {:?}, Second: {:?}, RemainingIter: {:?}]", first, second, self.inner) }, Some(Either::Right(second)) => { - write!(f, "ExactlyOneError[{:?}, ...]", second) + write!(f, "ExactlyOneError[Second: {:?}, RemainingIter: {:?}]", second, self.inner) } None => { - write!(f, "ExactlyOneError[...]") + write!(f, "ExactlyOneError[RemainingIter: {:?}]", self.inner) } } } } #[cfg(feature = "use_std")] -impl Error for ExactlyOneError where I: Iterator, I::Item: Debug, {} +impl Error for ExactlyOneError where I: Iterator + Debug, I::Item: Debug, {}