titanic 0.1.0

Unsinkable parser for the GGA sentence of the NMEA 0183 protocol (parses GPS positions).
Documentation
//! All errors that can happen while parsing can be found here.

use arrayvec::{ArrayVec, CapacityError};
use chrono;
use std::{io, num, str};

use lexer::STRING_LENGTH;

quick_error! {
    /// Errors that can happen while parsing.
    #[derive(Debug)]
    pub enum ParseError {
        /// Encountered an error while lexing.
        Lexer(err: LexError) {
            from()
            description(err.description())
            display("Could not parse due to an error in the lexer: {}", err)
            cause(err)
        }
        /// Found an unexpected token.
        UnexpectedToken {
            description("Unexpected token")
            display("Encountered unexpected token")
        }
        /// Found an unexptected sentence type.
        UnexpectedSentenceType {
            description("Unexpected sentence type")
            display("Encountered unexpected sentence type")
        }
        /// Failed to parse a `FloatLiteral` to a `NaiveTime`.
        Time(err: chrono::format::ParseError) {
            from()
            description("Time parsing error")
            display("Failed to parse FloatLiteral as time: {}", err)
            cause(err)
        }
        /// Failed to parse a `StringLiteral` to a `CardDir`.
        UnexpectedDir(dir: ArrayVec<[u8; STRING_LENGTH]>) {
            description("Unexpected direction")
            display("Could not parse {:?} as direction", dir)
        }
        /// Failed to convert to `str`.
        /// This should not happen if the lexer only returns ascii bytes.
        Utf8(err: str::Utf8Error) {
            from()
            description(err.description())
            display("{}", err)
            cause(err)
        }
        /// Found an invalid value.
        /// E.g. a station id not between 0 and 1023, a float with the wrong
        /// format for a coordinate, ...
        InvalidValue(msg: &'static str) {
            description("Invalid value")
            display("Invalid value, {}", msg)
        }
        /// Failed to parse an integer.
        Int(err: num::ParseIntError) {
            from()
            description(err.description())
            display("{}", err)
            cause(err)
        }
        /// Failed to parse a float.
        Float(err: num::ParseFloatError) {
            from()
            description(err.description())
            display("{}", err)
            cause(err)
        }
        /// Found an invalid coordinate, e.g. latitude outside of the range -90° to +90°.
        InvalidCoord(val: f64, max: f64) {
            description("Invalid coordinate")
            display("Invalid coordinate: {} should be between {:.0} and {:.0}", val, max*-1.0, max)
        }
        /// Found an unexpected unit.
        InvalidUnit {
            description("Unexpected unit")
            display("Found an unexpected unit")
        }
    }
}

quick_error!{
    /// Errors that can happen while lexing the raw input.
    #[derive(Debug)]
    pub enum LexError {
        /// An invalid character.
        InvalidCharacter(c: u8) {
            description("Invalid character")
            display("Encountered invalid character \"{}\"", *c as char)
        }
        /// The checksum is invalid.
        /// The two values are the expected and the actual checksum.
        /// `InvalidChecksum(expected: u8, actual: u8)`
        InvalidChecksum(expected: u8, actual: u8) {
            description("Invalid checksum")
            display("Expected checksum \"{:X}\" , found checksum \"{}\"", expected, actual)
        }
        /// I/O Error
        Io(err: io::Error) {
            from()
            description(err.description())
            display("Encountered I/O error while lexing: {}", err)
            cause(err)
        }
        /// The EOF was found in an unexpected location.
        /// This can happen in the header and the checksum, when there aren't
        /// enough bytes after the starting sign.
        UnexpectedEof(token: &'static str) {
            description("Unexpected EOF")
            display("Encountered unexpected EOF in {}", token)
        }
        /// A token could not be completed.
        /// Some reasons for that are a `'-'` without a following digit and
        /// a non hex-digit in the two chars following `'*'`.
        IncompleteToken(token: &'static str) {
            description("Incomplete token")
            display("Could not complete token of type {}", token)
        }
        /// An array overflowed while trying to push values into it.
        ArrayOverflow(err: CapacityError<u8>) {
            from()
            description(err.description())
            display("Array buffer overflow: {}", err)
            cause(err)
        }
        /// Failed to parse an integer.
        Int(err: num::ParseIntError) {
            from()
            description(err.description())
            display("{}", err)
            cause(err)
        }
        /// Failed to parse some bytes to a `str` because they are not even utf8
        /// even though only ascii is expected.
        NotEvenUtf8(err: str::Utf8Error) {
            from()
            description(err.description())
            display("Expected ascii but did not even get valid utf8: {}", err)
            cause(err)
        }
    }
}

impl From<(u8, u8)> for LexError {
    fn from((expected, actual): (u8, u8)) -> Self {
        LexError::InvalidChecksum(expected, actual)
    }
}