Repository: Marwes/combine (language: rust)

Issue:
easy::Errors should not require 'static input to impl std::error::Error
Because of `Any` bound `easy::Errors` are not implementing `std::error::Error` if working with `&str` or other non `'static` input.


Discussion / hints:
Probably a remnant from an early version of combine, should just be possible to remove it (PRs appreciated!)

Fix patch:
diff --git a/src/lib.rs b/src/lib.rs
index 957c9e74..65adfaf4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -200,6 +200,10 @@ pub use combinator::{
 };
 #[doc(inline)]
 pub use parser::choice::choice;
+#[doc(inline)]
+pub use parser::combinator::from_str;
+#[doc(inline)]
+pub use parser::item::tokens2;
 
 macro_rules! static_fn {
     (($($arg: pat, $arg_ty: ty),*) -> $ret: ty { $body: expr }) => { {
@@ -271,7 +275,7 @@ macro_rules! impl_token_parser {
 /// #[macro_use]
 /// extern crate combine;
 /// use combine::parser::char::digit;
-/// use combine::{any, choice, many1, Parser, Stream};
+/// use combine::{any, choice, from_str, many1, Parser, Stream};
 /// use combine::error::ParseError;
 ///
 /// parser!{
@@ -287,8 +291,7 @@ macro_rules! impl_token_parser {
 ///     {
 ///         // The body must be a block body ( `{ <block body> }`) which ends with an expression
 ///         // which evaluates to a parser
-///         let digits = many1(digit());
-///         digits.and_then(|s: String| s.parse())
+///         from_str(many1::<String, _>(digit()))
 ///     }
 /// }
 ///
@@ -565,7 +568,6 @@ macro_rules! combine_parse_partial {
         $parser.parse_mode($mode, $input, state)
     }};
     (($ignored:ty) $mode:ident $input:ident $state:ident $parser:block) => {
-
         $parser.parse_mode($mode, $input, $state)
     };
 }
@@ -720,19 +722,31 @@ pub mod combinator {
 }
 
 #[doc(hidden)]
-#[deprecated(since = "3.0.0", note = "Please use the `parser::char` module instead")]
+#[deprecated(
+    since = "3.0.0",
+    note = "Please use the `parser::char` module instead"
+)]
 pub use parser::char;
 
 #[doc(hidden)]
-#[deprecated(since = "3.0.0", note = "Please use the `parser::byte` module instead")]
+#[deprecated(
+    since = "3.0.0",
+    note = "Please use the `parser::byte` module instead"
+)]
 pub use parser::byte;
 
 #[doc(hidden)]
-#[deprecated(since = "3.0.0", note = "Please use the `parser::range` module instead")]
+#[deprecated(
+    since = "3.0.0",
+    note = "Please use the `parser::range` module instead"
+)]
 pub use parser::range;
 
 #[doc(hidden)]
-#[deprecated(since = "3.0.0", note = "Please use the `parser::regex` module instead")]
+#[deprecated(
+    since = "3.0.0",
+    note = "Please use the `parser::regex` module instead"
+)]
 #[cfg(feature = "regex")]
 pub use parser::regex;
 
diff --git a/src/parser/combinator.rs b/src/parser/combinator.rs
index e97048c0..00d39286 100644
--- a/src/parser/combinator.rs
+++ b/src/parser/combinator.rs
@@ -1,13 +1,15 @@
 //! Various combinators which do not fit anywhere else.
 
+use lib::fmt;
 use lib::marker::PhantomData;
 use lib::mem;
+use lib::str;
 
-use error::{ConsumedResult, Info, ParseError, Tracked};
+use error::{ConsumedResult, Info, ParseError, StreamError, Tracked};
 use parser::error::unexpected;
 use parser::item::value;
 use parser::ParseMode;
-use stream::{Positioned, Resetable, Stream, StreamOnce};
+use stream::{input_at_eof, Positioned, Resetable, Stream, StreamErrorFor, StreamOnce};
 use Parser;
 
 use either::Either;
@@ -301,18 +303,31 @@ where
         M: ParseMode,
     {
         let position = input.position();
+        let checkpoint = input.checkpoint();
         match self.0.parse_mode(mode, input, state) {
             EmptyOk(o) => match (self.1)(o) {
                 Ok(o) => EmptyOk(o),
-                Err(err) => EmptyErr(
-                    <Self::Input as StreamOnce>::Error::from_error(position, err.into()).into(),
-                ),
+                Err(err) => {
+                    let err = <Self::Input as StreamOnce>::Error::from_error(position, err.into());
+
+                    if input.is_partial() && input_at_eof(input) {
+                        input.reset(checkpoint);
+                        ConsumedErr(err)
+                    } else {
+                        EmptyErr(err.into())
+                    }
+                }
             },
             ConsumedOk(o) => match (self.1)(o) {
                 Ok(o) => ConsumedOk(o),
-                Err(err) => ConsumedErr(
-                    <Self::Input as StreamOnce>::Error::from_error(position, err.into()).into(),
-                ),
+                Err(err) => {
+                    if input.is_partial() && input_at_eof(input) {
+                        input.reset(checkpoint);
+                    }
+                    ConsumedErr(
+                        <Self::Input as StreamOnce>::Error::from_error(position, err.into()).into(),
+                    )
+                }
             },
             EmptyErr(err) => EmptyErr(err),
             ConsumedErr(err) => ConsumedErr(err),
@@ -867,3 +882,111 @@ where
 {
     Lazy(p)
 }
+
+mod internal {
+    pub trait Sealed {}
+}
+
+use self::internal::Sealed;
+
+pub trait StrLike: Sealed {
+    fn from_utf8(&self) -> Result<&str, ()>;
+}
+
+#[cfg(feature = "std")]
+impl Sealed for String {}
+#[cfg(feature = "std")]
+impl StrLike for String {
+    fn from_utf8(&self) -> Result<&str, ()> {
+        Ok(self)
+    }
+}
+
+impl<'a> Sealed for &'a str {}
+impl<'a> StrLike for &'a str {
+    fn from_utf8(&self) -> Result<&str, ()> {
+        Ok(*self)
+    }
+}
+
+impl Sealed for str {}
+impl StrLike for str {
+    fn from_utf8(&self) -> Result<&str, ()> {
+        Ok(self)
+    }
+}
+
+#[cfg(feature = "std")]
+impl Sealed for Vec<u8> {}
+#[cfg(feature = "std")]
+impl StrLike for Vec<u8> {
+    fn from_utf8(&self) -> Result<&str, ()> {
+        (**self).from_utf8()
+    }
+}
+
+impl<'a> Sealed for &'a [u8] {}
+impl<'a> StrLike for &'a [u8] {
+    fn from_utf8(&self) -> Result<&str, ()> {
+        (**self).from_utf8()
+    }
+}
+
+impl Sealed for [u8] {}
+impl StrLike for [u8] {
+    fn from_utf8(&self) -> Result<&str, ()> {
+        str::from_utf8(self).map_err(|_| ())
+    }
+}
+
+parser!{
+pub struct FromStr;
+type PartialState = P::PartialState;
+
+/// Takes a parser that outputs a string like value (`&str`, `String`, `&[u8]` or `Vec<u8>`) and parses it
+/// using `std::str::FromStr`. Errors if the output of `parser` is not UTF-8 or if
+/// `FromStr::from_str` returns an error.
+///
+/// ```
+/// # extern crate combine;
+/// # use combine::parser::range;
+/// # use combine::parser::repeat::many1;
+/// # use combine::parser::combinator::from_str;
+/// # use combine::char;
+/// # use combine::*;
+/// # fn main() {
+/// let mut parser = from_str(many1::<String, _>(char::digit()));
+/// let result = parser.parse("12345\r\n");
+/// assert_eq!(result, Ok((12345i32, "\r\n")));
+///
+/// // Range parsers work as well
+/// let mut parser = from_str(range::take_while1(|c: char| c.is_digit(10)));
+/// let result = parser.parse("12345\r\n");
+/// assert_eq!(result, Ok((12345i32, "\r\n")));
+///
+/// // As do parsers that work with bytes
+/// let digits = || range::take_while1(|b: u8| b >= b'0' && b <= b'9');
+/// let mut parser = from_str(range::recognize((
+///     digits(),
+///     byte::byte(b'.'),
+///     digits(),
+/// )));
+/// let result = parser.parse(&b"123.45\r\n"[..]);
+/// assert_eq!(result, Ok((123.45f64, &b"\r\n"[..])));
+/// # }
+/// ```
+pub fn from_str[O, P](parser: P)(P::Input) -> O
+where [
+    P: Parser,
+    P::Output: StrLike,
+    O: str::FromStr,
+    O::Err: fmt::Display,
+]
+{
+    parser.and_then(|r| {
+        r.from_utf8()
+            .map_err(|_| StreamErrorFor::<P::Input>::expected_static_message("UTF-8"))
+            .and_then(|s| s.parse().map_err(StreamErrorFor::<P::Input>::message_message))
+    })
+}
+}
diff --git a/src/parser/item.rs b/src/parser/item.rs
index ae406d2e..7b615f5a 100644
--- a/src/parser/item.rs
+++ b/src/parser/item.rs
@@ -331,6 +331,107 @@ where
     }
 }
 
+#[derive(Clone)]
+pub struct Tokens2<C, T, I>
+where
+    I: Stream,
+{
+    cmp: C,
+    tokens: T,
+    _marker: PhantomData<I>,
+}
+
+impl<C, T, I> Parser for Tokens2<C, T, I>
+where
+    C: FnMut(T::Item, I::Item) -> bool,
+    T: Clone + IntoIterator,
+    I: Stream,
+{
+    type Input = I;
+    type Output = T;
+    type PartialState = ();
+
+    #[inline]
+    fn parse_lazy(&mut self, input: &mut Self::Input) -> ConsumedResult<T, I> {
+        let start = input.position();
+        let mut consumed = false;
+        for c in self.tokens.clone() {
+            match ::stream::uncons(input) {
+                ConsumedOk(other) | EmptyOk(other) => {
+                    if !(self.cmp)(c, other.clone()) {
+                        return if consumed {
+                            let mut errors = <Self::Input as StreamOnce>::Error::from_error(
+                                start,
+                                StreamError::unexpected(Info::Token(other)),
+                            );
+                            ConsumedErr(errors)
+                        } else {
+                            EmptyErr(<Self::Input as StreamOnce>::Error::empty(start).into())
+                        };
+                    }
+                    consumed = true;
+                }
+                EmptyErr(mut error) => {
+                    error.error.set_position(start);
+                    return if consumed {
+                        ConsumedErr(error.error)
+                    } else {
+                        EmptyErr(error.into())
+                    };
+                }
+                ConsumedErr(mut error) => {
+                    error.set_position(start);
+                    return ConsumedErr(error);
+                }
+            }
+        }
+        if consumed {
+            ConsumedOk(self.tokens.clone())
+        } else {
+            EmptyOk(self.tokens.clone())
+        }
+    }
+}
+
+/// Parses multiple tokens.
+///
+/// Consumes items from the input and compares them to the values from `tokens` using the
+/// comparison function `cmp`. Succeeds if all the items from `tokens` are matched in the input
+/// stream and fails otherwise.
+///
+/// ```
+/// # extern crate combine;
+/// # use combine::*;
+/// # fn main() {
+/// # #[allow(deprecated)]
+/// # use std::ascii::AsciiExt;
+/// let result = tokens2(|l, r| l.eq_ignore_ascii_case(&r), "abc".chars())
+///     .parse("AbC")
+///     .map(|x| x.0.as_str());
+/// assert_eq!(result, Ok("abc"));
+/// let result = tokens2(
+///     |&l, r| (if l < r { r - l } else { l - r }) <= 2,
+///     &b"025"[..]
+/// )
+///     .parse(&b"123"[..])
+///     .map(|x| x.0);
+/// assert_eq!(result, Ok(&b"025"[..]));
+/// # }
+/// ```
+#[inline(always)]
+pub fn tokens2<C, T, I>(cmp: C, tokens: T) -> Tokens2<C, T, I>
+where
+    C: FnMut(T::Item, I::Item) -> bool,
+    T: Clone + IntoIterator,
+    I: Stream,
+{
+    Tokens2 {
+        cmp: cmp,
+        tokens: tokens,
+        _marker: PhantomData,
+    }
+}
+
 #[derive(Copy, Clone)]
 pub struct Position<I>
 where
diff --git a/src/parser/range.rs b/src/parser/range.rs
index 406d511f..ba1481c1 100644
--- a/src/parser/range.rs
+++ b/src/parser/range.rs
@@ -142,7 +142,8 @@ where
         input: &mut Self::Input,
         state: &mut Self::PartialState,
     ) -> ConsumedResult<Self::Output, Self::Input>
-    where M: ParseMode
+    where
+        M: ParseMode,
     {
         let (ref mut distance_state, ref mut child_state) = *state;
 
@@ -533,18 +534,32 @@ mod tests {
     #[test]
     fn take_until_range_2() {
         let result = take_until_range("===").parse("if ((pointless_comparison == 3) === true) {");
-        assert_eq!(result, Ok(("if ((pointless_comparison == 3) ", "=== true) {")));
+        assert_eq!(
+            result,
+            Ok(("if ((pointless_comparison == 3) ", "=== true) {"))
+        );
     }
 
     #[test]
     fn take_until_range_unicode_1() {
-        let result = take_until_range("🦀").parse("😃 Ferris the friendly rustacean 🦀 and his snake friend 🐍");
-        assert_eq!(result, Ok(("😃 Ferris the friendly rustacean ", "🦀 and his snake friend 🐍")));
+        let result = take_until_range("🦀")
+            .parse("😃 Ferris the friendly rustacean 🦀 and his snake friend 🐍");
+        assert_eq!(
+            result,
+            Ok((
+                "😃 Ferris the friendly rustacean ",
+                "🦀 and his snake friend 🐍"
+            ))
+        );
     }
 
     #[test]
     fn take_until_range_unicode_2() {
-        let result = take_until_range("⁘⁙/⁘").parse("⚙️🛠️🦀=🏎️⁘⁙⁘⁘⁙/⁘⁘⁙/⁘");
-        assert_eq!(result, Ok(("⚙️🛠️🦀=🏎️⁘⁙⁘", "⁘⁙/⁘⁘⁙/⁘")));
+        let result = take_until_range("⁘⁙/⁘")
+            .parse("⚙️🛠️🦀=🏎️⁘⁙⁘⁘⁙/⁘⁘⁙/⁘");
+        assert_eq!(
+            result,
+            Ok(("⚙️🛠️🦀=🏎️⁘⁙⁘", "⁘⁙/⁘⁘⁙/⁘"))
+        );
     }
 }
diff --git a/src/stream/easy.rs b/src/stream/easy.rs
index 1115e2a1..31a59a3f 100644
--- a/src/stream/easy.rs
+++ b/src/stream/easy.rs
@@ -79,7 +79,6 @@
 //! ```
 //!
 //! [`Parser::easy_parse`]: ../parser/trait.Parser.html#method.easy_parse
-use std::any::Any;
 use std::error::Error as StdError;
 use std::fmt;
 
@@ -691,9 +690,9 @@ impl<I, R, P> Errors<I, R, P> {
 
 impl<I, R, P> StdError for Errors<I, R, P>
 where
-    P: fmt::Display + fmt::Debug + Any,
-    I: fmt::Display + fmt::Debug + Any,
-    R: fmt::Display + fmt::Debug + Any,
+    P: fmt::Display + fmt::Debug,
+    I: fmt::Display + fmt::Debug,
+    R: fmt::Display + fmt::Debug,
 {
     fn description(&self) -> &str {
         "parse error"
diff --git a/src/stream/mod.rs b/src/stream/mod.rs
index bf592938..63c19ada 100644
--- a/src/stream/mod.rs
+++ b/src/stream/mod.rs
@@ -139,8 +139,7 @@ impl<I> Stream for I
 where
     I: StreamOnce + Positioned + Resetable,
     I::Error: ParseError<I::Item, I::Range, I::Position>,
-{
-}
+{}
 
 #[inline]
 pub fn uncons<I>(input: &mut I) -> ConsumedResult<I::Item, I>
@@ -207,11 +206,7 @@ pub trait RangeStreamOnce: StreamOnce + Resetable {
 /// A `RangeStream` is an extension of `Stream` which allows for zero copy parsing.
 pub trait RangeStream: Stream + RangeStreamOnce {}
 
-impl<I> RangeStream for I
-where
-    I: RangeStreamOnce + Stream,
-{
-}
+impl<I> RangeStream for I where I: RangeStreamOnce + Stream {}
 
 /// A `RangeStream` which is capable of providing it's entire range.
 pub trait FullRangeStream: RangeStream {
@@ -251,7 +246,8 @@ where
     }
 }
 
-fn input_at_eof<I>(input: &mut I) -> bool
+#[doc(hidden)]
+pub fn input_at_eof<I>(input: &mut I) -> bool
 where
     I: ?Sized + Stream,
 {
@@ -363,7 +359,6 @@ where
 
     macro_rules! test_next {
         () => {
-
             match chars.next() {
                 Some(c) => {
                     if !f(c) {
@@ -476,7 +471,6 @@ where
 
     macro_rules! check {
         () => {
-
             if !f(unsafe { slice.get_unchecked(i).clone() }) {
                 found = true;
                 break;
@@ -757,7 +751,6 @@ where
 
     macro_rules! check {
         () => {
-
             if !f(unsafe { slice.get_unchecked(i) }) {
                 found = true;
                 break;


Test patch:
diff --git a/tests/async.rs b/tests/async.rs
index 4140bca1..b464703f 100644
--- a/tests/async.rs
+++ b/tests/async.rs
@@ -25,8 +25,8 @@ use futures::{Future, Stream};
 use tokio_codec::{Decoder, FramedRead};
 
 use combine::combinator::{
-    any_partial_state, any_send_partial_state, no_partial, optional, recognize, skip_many1, try,
-    AnyPartialState, AnySendPartialState,
+    any_partial_state, any_send_partial_state, from_str, no_partial, optional, recognize,
+    skip_many1, try, AnyPartialState, AnySendPartialState,
 };
 use combine::error::{ParseError, StreamError};
 use combine::parser::char::{char, digit, letter};
@@ -172,10 +172,10 @@ parser!{
     fn prefix_many_then_parser['a, I]()(I) -> String
         where [ I: RangeStream<Item = char, Range = &'a str> ]
     {
-        any_partial_state((char('#'), skip_many(char(' ')), many1(digit()))
+        let integer = from_str(many1::<String, _>(digit()));
+        any_partial_state((char('#'), skip_many(char(' ')), integer)
             .then_partial(|t| {
-                let s: &String = &t.2;
-                let c = s.parse().unwrap();
+                let c = t.2;
                 count_min_max(c, c, any())
             })
         )