summaryrefslogtreecommitdiff
path: root/src/nibble/parse/mod.rs
blob: 13c4c0e56c522145cab8e7e702670f2a4e509ad3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
pub mod iter;

use self::iter::Peek;
use self::iter::PeekExt;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ParseError<E> {
    EndOfFile,
    InvalidCharacter(char),
    Other(E),
}

impl<U> ParseError<U> {
    pub fn map<T: Into<U>>(other: ParseError<T>) -> Self {
        match other {
            ParseError::EndOfFile => Self::EndOfFile,
            ParseError::InvalidCharacter(c) => Self::InvalidCharacter(c),
            ParseError::Other(t) => Self::Other(t.into())
        }
    }
}

/// Parse a value from an iterator.
pub trait Parse: Sized {
    /// The associated error that can be returned from parsing.
    type Err;
    /// Parses `iter` to return a value of this type.
    ///
    /// This method may not reach the end of `iter`. If so, it must not consume any
    /// characters in the stream that do not form part of the value.
    ///
    /// If parsing succeeds, return the value inside [`Ok`]. Otherwise, when the
    /// iterator is ill-formatted, return an error specific to the inside [`Err`].
    /// The error type is specific to the implementation of the trait.
    fn parse<I: PeekExt<Item = char>>(iter: I) -> Result<Self, ParseError<Self::Err>>;
}

/// Consumes the next item in `iter` if it equals `c`. Otherwise, returns an
/// appropriate error without advancing the iterator.
///
/// # Examples
/// Basic usage
/// ```
/// use chomp::nibble::parse::{requires, Parse, ParseError};
/// use std::convert::Infallible;
///
/// let s = "hello".to_owned();
/// let mut iter = s.chars().peekable();
/// assert_eq!(requires::<_, Infallible>(&mut iter, 'h'), Ok(()));
/// assert_eq!(requires::<_, Infallible>(&mut iter, 'e'), Ok(()));
/// assert_eq!(requires::<_, Infallible>(&mut iter, 'z'), Err(ParseError::InvalidCharacter('l')));
/// assert_eq!(iter.next(), Some('l'));
/// assert_eq!(iter.next(), Some('l'));
/// assert_eq!(iter.next(), Some('o'));
/// assert_eq!(requires::<_, Infallible>(iter, '!'), Err(ParseError::EndOfFile));
/// ```
pub fn requires<I: Peek<Item = char>, E>(mut iter: I, c: char) -> Result<(), ParseError<E>> {
    iter.next_if_eq(&c)
        .ok_or(ParseError::EndOfFile)?
        .map_err(|&c| ParseError::InvalidCharacter(c))
}