#![cfg_attr(
feature = "cargo-clippy",
allow(inline_always, type_complexity, too_many_arguments)
)]
#![cfg_attr(feature = "cargo-clippy", allow(clippy_lint))]
#![cfg_attr(not(feature = "std"), no_std)]
#[doc(inline)]
pub use error::{ConsumedResult, ParseError, ParseResult};
#[doc(inline)]
pub use parser::Parser;
#[doc(inline)]
pub use stream::{Positioned, RangeStream, RangeStreamOnce, Stream, StreamOnce};
#[doc(inline)]
#[allow(deprecated)]
pub use combinator::{
any, attempt, between, chainl1, chainr1, count, count_min_max, env_parser, eof, look_ahead,
many, many1, none_of, not_followed_by, one_of, optional, parser, position, satisfy,
satisfy_map, sep_by, sep_by1, sep_end_by, sep_end_by1, skip_count, skip_count_min_max,
skip_many, skip_many1, token, tokens, try, unexpected, unexpected_any, value,
};
#[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 }) => { {
fn temp($($arg: $arg_ty),*) -> $ret { $body }
temp as fn(_) -> _
} }
}
macro_rules! impl_token_parser {
($name: ident($($ty_var: ident),*), $ty: ty, $inner_type: ty) => {
#[derive(Clone)]
pub struct $name<I $(,$ty_var)*>($inner_type, PhantomData<fn (I) -> I>)
where I: Stream<Item=$ty>,
I::Error: ParseError<$ty, I::Range, I::Position>
$(, $ty_var : Parser<Input=I>)*;
impl <I $(,$ty_var)*> Parser for $name<I $(,$ty_var)*>
where I: Stream<Item=$ty>,
I::Error: ParseError<$ty, I::Range, I::Position>
$(, $ty_var : Parser<Input=I>)*
{
type Input = I;
type Output = <$inner_type as Parser>::Output;
type PartialState = <$inner_type as Parser>::PartialState;
forward_parser!(0);
}
}
}
#[macro_export]
macro_rules! parser {
(
$(#[$attr:meta])*
pub fn $name: ident [$($type_params: tt)*]( $($arg: ident : $arg_type: ty),* )
($input_type: ty) -> $output_type: ty
$parser: block
) => {
parser!{
$(#[$attr])*
pub fn $name [$($type_params)*]( $($arg : $arg_type),* )($input_type) -> $output_type
where []
$parser
}
};
(
$(#[$attr:meta])*
fn $name: ident [$($type_params: tt)*]( $($arg: ident : $arg_type: ty),* )
($input_type: ty) -> $output_type: ty
$parser: block
) => {
parser!{
$(#[$attr])*
fn $name [$($type_params)*]( $($arg : $arg_type),* )($input_type) -> $output_type
where []
$parser
}
};
(
$(#[$attr:meta])*
pub fn $name: ident [$($type_params: tt)*]( $($arg: ident : $arg_type: ty),* )
($input_type: ty) -> $output_type: ty
where [$($where_clause: tt)*]
$parser: block
) => {
combine_parser_impl!{
(pub)
struct $name;
type PartialState = (());
$(#[$attr])*
fn $name [$($type_params)*]($($arg : $arg_type),*)($input_type) -> $output_type
where [$($where_clause)*]
$parser
}
};
(
$(#[$attr:meta])*
fn $name: ident [$($type_params: tt)*]( $($arg: ident : $arg_type: ty),*)
($input_type: ty) -> $output_type: ty
where [$($where_clause: tt)*]
$parser: block
) => {
combine_parser_impl!{
()
struct $name;
type PartialState = (());
$(#[$attr])*
fn $name [$($type_params)*]($($arg : $arg_type),*)($input_type) -> $output_type
where [$($where_clause)*]
$parser
}
};
(
$(#[$derive:meta])*
pub struct $type_name: ident;
$(#[$attr:meta])*
pub fn $name: ident [$($type_params: tt)*]( $($arg: ident : $arg_type: ty),* )
($input_type: ty) -> $output_type: ty
where [$($where_clause: tt)*]
$parser: block
) => {
combine_parser_impl!{
(pub)
$(#[$derive])*
struct $type_name;
type PartialState = (());
$(#[$attr])*
fn $name [$($type_params)*]($($arg : $arg_type),*)($input_type) -> $output_type
where [$($where_clause)*]
$parser
}
};
(
$(#[$derive:meta])*
struct $type_name: ident;
$(#[$attr:meta])*
fn $name: ident [$($type_params: tt)*]( $($arg: ident : $arg_type: ty),*)
($input_type: ty) -> $output_type: ty
where [$($where_clause: tt)*]
$parser: block
) => {
combine_parser_impl!{
()
$(#[$derive])*
struct $type_name;
type PartialState = (());
$(#[$attr:meta])*
fn $name [$($type_params)*]($($arg : $arg_type),*)($input_type) -> $output_type
where [$($where_clause)*]
$parser
}
};
(
type PartialState = $partial_state: ty;
$(#[$attr:meta])*
pub fn $name: ident [$($type_params: tt)*]( $($arg: ident : $arg_type: ty),* )
($input_type: ty) -> $output_type: ty
$parser: block
) => {
parser!{
type PartialState = $partial_state;
$(#[$attr])*
pub fn $name [$($type_params)*]( $($arg : $arg_type),* )($input_type) -> $output_type
where []
$parser
}
};
(
type PartialState = $partial_state: ty;
$(#[$attr:meta])*
fn $name: ident [$($type_params: tt)*]( $($arg: ident : $arg_type: ty),* )
($input_type: ty) -> $output_type: ty
$parser: block
) => {
parser!{
type PartialState = $partial_state;
$(#[$attr])*
fn $name [$($type_params)*]( $($arg : $arg_type),* )($input_type) -> $output_type
where []
$parser
}
};
(
type PartialState = $partial_state: ty;
$(#[$attr:meta])*
pub fn $name: ident [$($type_params: tt)*]( $($arg: ident : $arg_type: ty),* )
($input_type: ty) -> $output_type: ty
where [$($where_clause: tt)*]
$parser: block
) => {
combine_parser_impl!{
(pub)
struct $name;
type PartialState = ($partial_state);
$(#[$attr])*
pub fn $name [$($type_params)*]($($arg : $arg_type),*)($input_type) -> $output_type
where [$($where_clause)*]
$parser
}
};
(
type PartialState = $partial_state: ty;
$(#[$attr:meta])*
fn $name: ident [$($type_params: tt)*]( $($arg: ident : $arg_type: ty),*)
($input_type: ty) -> $output_type: ty
where [$($where_clause: tt)*]
$parser: block
) => {
combine_parser_impl!{
()
struct $name;
type PartialState = ($partial_state);
$(#[$attr])*
fn $name [$($type_params)*]($($arg : $arg_type),*)($input_type) -> $output_type
where [$($where_clause)*]
$parser
}
};
(
$(#[$derive:meta])*
pub struct $type_name: ident;
type PartialState = $partial_state: ty;
$(#[$attr:meta])*
pub fn $name: ident [$($type_params: tt)*]( $($arg: ident : $arg_type: ty),* )
($input_type: ty) -> $output_type: ty
where [$($where_clause: tt)*]
$parser: block
) => {
combine_parser_impl!{
(pub)
$(#[$derive])*
struct $type_name;
type PartialState = ($partial_state);
$(#[$attr])*
fn $name [$($type_params)*]($($arg : $arg_type),*)($input_type) -> $output_type
where [$($where_clause)*]
$parser
}
};
(
$(#[$derive:meta])*
struct $type_name: ident;
type PartialState = $partial_state: ty;
$(#[$attr:meta])*
fn $name: ident [$($type_params: tt)*]( $($arg: ident : $arg_type: ty),*)
($input_type: ty) -> $output_type: ty
where [$($where_clause: tt)*]
$parser: block
) => {
combine_parser_impl!{
()
$(#[$derive])*
struct $type_name;
type PartialState = ($partial_state);
$(#[$attr:meta])*
fn $name [$($type_params)*]($($arg : $arg_type),*)($input_type) -> $output_type
where [$($where_clause)*]
$parser
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! combine_parse_partial {
((()) $mode:ident $input:ident $state:ident $parser:block) => {{
let _ = $state;
let mut state = Default::default();
let state = &mut state;
$parser.parse_mode($mode, $input, state)
}};
(($ignored:ty) $mode:ident $input:ident $state:ident $parser:block) => {
$parser.parse_mode($mode, $input, $state)
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! combine_parser_impl {
(
( $($pub_: tt)* )
$(#[$derive:meta])*
struct $type_name: ident;
type PartialState = ($($partial_state: tt)*);
$(#[$attr:meta])*
fn $name: ident [$($type_params: tt)*]( $($arg: ident : $arg_type: ty),*)
($input_type: ty) -> $output_type: ty
where [$($where_clause: tt)*]
$parser: block
) => {
$(#[$derive])*
#[allow(non_camel_case_types)]
$($pub_)* struct $type_name<$($type_params)*>
where <$input_type as $crate::stream::StreamOnce>::Error:
$crate::error::ParseError<
<$input_type as $crate::stream::StreamOnce>::Item,
<$input_type as $crate::stream::StreamOnce>::Range,
<$input_type as $crate::stream::StreamOnce>::Position
>,
$($where_clause)*
{
$(pub $arg : $arg_type,)*
__marker: $crate::lib::marker::PhantomData<fn ($input_type) -> $output_type>
}
#[allow(non_shorthand_field_patterns)]
impl<$($type_params)*> $crate::Parser for $type_name<$($type_params)*>
where <$input_type as $crate::stream::StreamOnce>::Error:
$crate::error::ParseError<
<$input_type as $crate::stream::StreamOnce>::Item,
<$input_type as $crate::stream::StreamOnce>::Range,
<$input_type as $crate::stream::StreamOnce>::Position
>,
$($where_clause)*
{
type Input = $input_type;
type Output = $output_type;
type PartialState = $($partial_state)*;
parse_mode!();
#[inline]
fn parse_mode_impl<M>(
&mut self,
mode: M,
input: &mut Self::Input,
state: &mut Self::PartialState,
) -> $crate::error::ConsumedResult<$output_type, $input_type>
where M: $crate::parser::ParseMode
{
let $type_name { $( $arg: ref mut $arg,)* .. } = *self;
combine_parse_partial!(($($partial_state)*) mode input state $parser)
}
#[inline]
fn add_error(
&mut self,
errors: &mut $crate::error::Tracked<
<$input_type as $crate::stream::StreamOnce>::Error
>)
{
let $type_name { $( $arg : ref mut $arg,)* .. } = *self;
let mut parser = $parser;
{
let _: &mut $crate::Parser<Input = $input_type, Output = $output_type, PartialState = _> = &mut parser;
}
parser.add_error(errors)
}
fn add_consumed_expected_error(
&mut self,
errors: &mut $crate::error::Tracked<
<$input_type as $crate::stream::StreamOnce>::Error
>)
{
let $type_name { $( $arg : ref mut $arg,)* .. } = *self;
let mut parser = $parser;
{
let _: &mut $crate::Parser<Input = $input_type, Output = $output_type, PartialState = _> = &mut parser;
}
parser.add_consumed_expected_error(errors)
}
}
$(#[$attr])*
#[inline(always)]
$($pub_)* fn $name< $($type_params)* >(
$($arg : $arg_type),*
) -> $type_name<$($type_params)*>
where <$input_type as $crate::stream::StreamOnce>::Error:
$crate::error::ParseError<
<$input_type as $crate::stream::StreamOnce>::Item,
<$input_type as $crate::stream::StreamOnce>::Range,
<$input_type as $crate::stream::StreamOnce>::Position
>,
$($where_clause)*
{
$type_name {
$($arg : $arg,)*
__marker: $crate::lib::marker::PhantomData
}
}
};
}
pub extern crate byteorder;
#[cfg(feature = "bytes")]
extern crate bytes;
pub extern crate either;
extern crate memchr;
extern crate unreachable;
macro_rules! forward_parser {
(, $($field: tt)+) => {
};
($method: ident $( $methods: ident)*, $($field: tt)*) => {
forward_parser!($method $($field)+);
forward_parser!($($methods)*, $($field)+);
};
(parse_mode $($field: tt)+) => {
#[inline]
fn parse_mode_impl<M>(
&mut self,
mode: M,
input: &mut Self::Input,
state: &mut Self::PartialState,
) -> ConsumedResult<Self::Output, Self::Input>
where
M: ParseMode,
{
self.$($field)+.parse_mode(mode, input, state).map(|(a, _)| a)
}
};
(parse_lazy $($field: tt)+) => {
fn parse_lazy(
&mut self,
input: &mut Self::Input,
) -> ConsumedResult<Self::Output, Self::Input> {
self.$($field)+.parse_lazy(input)
}
};
(parse_first $($field: tt)+) => {
fn parse_first(
&mut self,
input: &mut Self::Input,
state: &mut Self::PartialState,
) -> ConsumedResult<Self::Output, Self::Input> {
self.$($field)+.parse_first(input, state)
}
};
(parse_partial $($field: tt)+) => {
fn parse_partial(
&mut self,
input: &mut Self::Input,
state: &mut Self::PartialState,
) -> ConsumedResult<Self::Output, Self::Input> {
self.$($field)+.parse_partial(input, state)
}
};
(add_error $($field: tt)+) => {
fn add_error(&mut self, error: &mut Tracked<<Self::Input as StreamOnce>::Error>) {
self.$($field)+.add_error(error)
}
};
(add_consumed_expected_error $($field: tt)+) => {
fn add_consumed_expected_error(&mut self, error: &mut Tracked<<Self::Input as StreamOnce>::Error>) {
self.$($field)+.add_consumed_expected_error(error)
}
};
(parser_count $($field: tt)+) => {
fn parser_count(&self) -> $crate::ErrorOffset {
self.$($field)+.parser_count()
}
};
($field: tt) => {
forward_parser!(parse_lazy parse_first parse_partial add_error add_consumed_expected_error parser_count, $field);
}
}
#[doc(hidden)]
pub mod lib {
#[cfg(not(feature = "std"))]
pub use core::*;
#[cfg(feature = "std")]
pub use std::*;
}
#[cfg(feature = "std")]
#[doc(inline)]
pub use stream::easy;
#[macro_use]
pub mod error;
#[macro_use]
pub mod stream;
#[macro_use]
pub mod parser;
#[doc(hidden)]
#[deprecated(
since = "3.0.0",
note = "Please import parsers from the `parser` module and its submodules instead"
)]
pub mod combinator {
#[doc(inline)]
pub use parser::choice::*;
#[doc(inline)]
pub use parser::combinator::*;
#[doc(inline)]
pub use parser::error::*;
#[doc(inline)]
pub use parser::function::*;
#[doc(inline)]
pub use parser::item::*;
#[doc(inline)]
pub use parser::repeat::*;
#[doc(inline)]
pub use parser::sequence::*;
}
#[doc(hidden)]
#[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")]
pub use parser::byte;
#[doc(hidden)]
#[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"
)]
#[cfg(any(feature = "regex", feature = "regex-1"))]
pub use parser::regex;
#[doc(hidden)]
#[derive(Clone, PartialOrd, PartialEq, Debug, Copy)]
pub struct ErrorOffset(u8);
#[cfg(test)]
mod tests {
use super::*;
use parser::char::{char, string};
#[test]
fn chainl1_error_consume() {
fn first<T, U>(t: T, _: U) -> T {
t
}
let mut p = chainl1(string("abc"), char(',').map(|_| first));
assert!(p.parse("abc,ab").is_err());
}
#[test]
#[allow(deprecated)]
fn attempt_try() {
let mut parser = choice((
try((string("abc"), string("def"))),
try((string("abc"), string("ghi"))),
));
assert_eq!(parser.parse("abcghi"), Ok((("abc", "ghi"), "")));
}
#[test]
fn choice_strings() {
let mut fruits = [
attempt(string("Apple")),
attempt(string("Banana")),
attempt(string("Cherry")),
attempt(string("Date")),
attempt(string("Fig")),
attempt(string("Grape")),
];
let mut parser = choice(&mut fruits);
assert_eq!(parser.parse("Apple"), Ok(("Apple", "")));
assert_eq!(parser.parse("Banana"), Ok(("Banana", "")));
assert_eq!(parser.parse("Cherry"), Ok(("Cherry", "")));
assert_eq!(parser.parse("DateABC"), Ok(("Date", "ABC")));
assert_eq!(parser.parse("Fig123"), Ok(("Fig", "123")));
assert_eq!(parser.parse("GrapeApple"), Ok(("Grape", "Apple")));
}
}
#[cfg(all(feature = "std", test))]
mod std_tests {
use super::easy::Error;
use super::error::Consumed;
use super::stream::IteratorStream;
use super::*;
use parser::char::{alpha_num, char, digit, letter, spaces, string};
use stream::easy;
use stream::state::{SourcePosition, State};
#[test]
fn optional_error_consume() {
let mut p = optional(string("abc"));
let err = p.easy_parse(State::new("ab")).unwrap_err();
assert_eq!(err.position, SourcePosition { line: 1, column: 1 });
}
fn follow<I>(input: &mut I) -> ParseResult<(), I>
where
I: Stream<Item = char, Error = easy::ParseError<I>>,
I::Position: Default,
{
let before = input.checkpoint();
match input.uncons() {
Ok(c) => {
if c.is_alphanumeric() {
input.reset(before);
let e = Error::Unexpected(c.into());
Err(Consumed::Empty(
easy::Errors::new(input.position(), e).into(),
))
} else {
Ok(((), Consumed::Empty(())))
}
}
Err(_) => Ok(((), Consumed::Empty(()))),
}
}
fn integer<'a, I>(input: &mut I) -> ParseResult<i64, I>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
let (s, input) = try!(many1::<String, _>(digit())
.expected("integer")
.parse_stream(input));
let mut n = 0;
for c in s.chars() {
n = n * 10 + (c as i64 - '0' as i64);
}
Ok((n, input))
}
#[test]
fn test_integer() {
let result = parser(integer).parse("123");
assert_eq!(result, Ok((123i64, "")));
}
#[test]
fn list() {
let mut p = sep_by(parser(integer), char(','));
let result = p.parse("123,4,56");
assert_eq!(result, Ok((vec![123i64, 4, 56], "")));
}
#[test]
fn iterator() {
let result = parser(integer)
.parse(State::new(IteratorStream::new("123".chars())))
.map(|(i, mut input)| (i, input.uncons().is_err()));
assert_eq!(result, Ok((123i64, true)));
}
#[test]
fn field() {
let word = || many(alpha_num());
let spaces = spaces();
let c_decl = (word(), spaces.clone(), char(':'), spaces, word())
.map(|t| (t.0, t.4))
.parse("x: int");
assert_eq!(c_decl, Ok((("x".to_string(), "int".to_string()), "")));
}
#[test]
fn source_position() {
let source = r"
123
";
let mut parsed_state = State::with_positioner(source, SourcePosition::new());
let result = (spaces(), parser(integer), spaces())
.map(|t| t.1)
.parse_stream(&mut parsed_state);
let state = Consumed::Consumed(State {
positioner: SourcePosition { line: 3, column: 1 },
input: "",
});
assert_eq!(
result.map(|(x, c)| (x, c.map(|_| parsed_state))),
Ok((123i64, state))
);
}
#[derive(Debug, PartialEq)]
pub enum Expr {
Id(String),
Int(i64),
Array(Vec<Expr>),
Plus(Box<Expr>, Box<Expr>),
Times(Box<Expr>, Box<Expr>),
}
parser! {
fn expr[I]()(I) -> Expr
where
[I: Stream<Item = char>,]
{
let word = many1(letter()).expected("identifier");
let integer = parser(integer);
let array = between(char('['), char(']'), sep_by(expr(), char(','))).expected("[");
let paren_expr = between(char('('), char(')'), parser(term)).expected("(");
spaces()
.silent()
.with(
word.map(Expr::Id)
.or(integer.map(Expr::Int))
.or(array.map(Expr::Array))
.or(paren_expr),
)
.skip(spaces().silent())
}
}
#[test]
fn expression_basic() {
let result = sep_by(expr(), char(',')).parse("int, 100, [[], 123]");
let exprs = vec![
Expr::Id("int".to_string()),
Expr::Int(100),
Expr::Array(vec![Expr::Array(vec![]), Expr::Int(123)]),
];
assert_eq!(result, Ok((exprs, "")));
}
#[test]
fn expression_error() {
let input = r"
,123
";
let result = expr().easy_parse(State::new(input));
let err = easy::Errors {
position: SourcePosition { line: 2, column: 1 },
errors: vec![
Error::Unexpected(','.into()),
Error::Expected("integer".into()),
Error::Expected("identifier".into()),
Error::Expected("[".into()),
Error::Expected("(".into()),
],
};
assert_eq!(result, Err(err));
}
fn term<I>(input: &mut I) -> ParseResult<Expr, I>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
fn times(l: Expr, r: Expr) -> Expr {
Expr::Times(Box::new(l), Box::new(r))
}
fn plus(l: Expr, r: Expr) -> Expr {
Expr::Plus(Box::new(l), Box::new(r))
}
let mul = char('*').map(|_| times);
let add = char('+').map(|_| plus);
let factor = chainl1(expr(), mul);
chainl1(factor, add).parse_stream(input)
}
#[test]
fn operators() {
let input = r"
1 * 2 + 3 * test
";
let (result, _) = parser(term).parse(State::new(input)).unwrap();
let e1 = Expr::Times(Box::new(Expr::Int(1)), Box::new(Expr::Int(2)));
let e2 = Expr::Times(
Box::new(Expr::Int(3)),
Box::new(Expr::Id("test".to_string())),
);
assert_eq!(result, Expr::Plus(Box::new(e1), Box::new(e2)));
}
#[test]
fn error_position() {
let mut p = string("let")
.skip(parser(follow))
.map(|x| x.to_string())
.or(many1(digit()));
match p.easy_parse(State::new("le123")) {
Ok(_) => assert!(false),
Err(err) => assert_eq!(err.position, SourcePosition { line: 1, column: 1 }),
}
match p.easy_parse(State::new("let1")) {
Ok(_) => assert!(false),
Err(err) => assert_eq!(err.position, SourcePosition { line: 1, column: 4 }),
}
}
#[test]
fn sep_by_error_consume() {
let mut p = sep_by::<Vec<_>, _, _>(string("abc"), char(','));
let err = p.easy_parse(State::new("ab,abc")).unwrap_err();
assert_eq!(err.position, SourcePosition { line: 1, column: 1 });
}
#[test]
fn inner_error_consume() {
let mut p = many::<Vec<_>, _>(between(char('['), char(']'), digit()));
let result = p.easy_parse(State::new("[1][2][]"));
assert!(result.is_err(), format!("{:?}", result));
let error = result.map(|x| format!("{:?}", x)).unwrap_err();
assert_eq!(error.position, SourcePosition { line: 1, column: 8 });
}
#[test]
fn infinite_recursion_in_box_parser() {
let _: Result<(Vec<_>, _), _> = (many(Box::new(digit()))).parse("1");
}
#[test]
fn unsized_parser() {
let mut parser: Box<Parser<Input = _, Output = char, PartialState = _>> = Box::new(digit());
let borrow_parser = &mut *parser;
assert_eq!(borrow_parser.parse("1"), Ok(('1', "")));
}
#[test]
fn std_error() {
use std::error::Error as StdError;
use std::fmt;
#[derive(Debug)]
struct Error;
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "error")
}
}
impl StdError for Error {
fn description(&self) -> &str {
"error"
}
}
let result: Result<((), _), easy::Errors<char, &str, _>> =
Parser::easy_parse(&mut string("abc").and_then(|_| Err(Error)), "abc");
assert!(result.is_err());
let _ = result.map_err(|err| {
let err: Box<StdError> = Box::new(err);
err
});
}
#[test]
fn extract_std_error() {
use std::error::Error as StdError;
use std::fmt;
#[derive(Clone, PartialEq, Debug)]
struct CloneOnly(String);
#[derive(Debug)]
struct DisplayVec<T>(Vec<T>);
#[derive(Debug)]
struct ExtractedError(usize, DisplayVec<Error<CloneOnly, DisplayVec<CloneOnly>>>);
impl StdError for ExtractedError {
fn description(&self) -> &str {
"extracted error"
}
}
impl fmt::Display for CloneOnly {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl<T: fmt::Debug> fmt::Display for DisplayVec<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{:?}]", self.0)
}
}
impl fmt::Display for ExtractedError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(writeln!(f, "Parse error at {}", self.0));
Error::fmt_errors(&(self.1).0, f)
}
}
let input = &[CloneOnly("x".to_string()), CloneOnly("y".to_string())][..];
let result = token(CloneOnly("z".to_string()))
.easy_parse(input)
.map_err(|e| e.map_position(|p| p.translate_position(input)))
.map_err(|e| {
ExtractedError(
e.position,
DisplayVec(
e.errors
.into_iter()
.map(|e| e.map_range(|r| DisplayVec(r.to_owned())))
.collect(),
),
)
});
assert!(result.is_err());
let _ = result.map_err(|err| {
let s = format!("{}", err);
assert!(s.starts_with("Parse error at 0"));
assert!(s.contains("Expected"));
let err: Box<StdError> = Box::new(err);
err
});
}
}