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
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use std::fmt::{Display, Formatter, Result};
use std::error::Error as StdError;
use combine::ParseError;
use combine::state::SourcePosition;
use combine::primitives::Error;


/// Type representing a TOML parse error
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct TomlError {
    message: String,
}

impl TomlError {
    pub(crate) fn new(error: ParseError<SourcePosition, char, &str>, input: &str) -> Self {
        Self {
            message: format!("{}", FancyError::new(error, input)),
        }
    }

    pub(crate) fn from_unparsed(pos: SourcePosition, input: &str) -> Self {
        Self::new(
            ParseError::new(pos, CustomError::UnparsedLine.into()),
            input,
        )
    }
}

/// Displays a TOML parse error
///
/// # Example
///
/// TOML parse error at line 1, column 10
///   |
/// 1 | 00:32:00.a999999
///   |          ^
/// Unexpected `a`
/// Expected `digit`
/// While parsing a Time
/// While parsing a Date-Time
impl Display for TomlError {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(f, "{}", self.message)
    }
}

impl StdError for TomlError {
    fn description(&self) -> &'static str {
        "TOML parse error"
    }
}


#[derive(Debug)]
pub(crate) struct FancyError<'a> {
    error: ParseError<SourcePosition, char, &'a str>,
    input: &'a str,
}

impl<'a> FancyError<'a> {
    pub(crate) fn new(error: ParseError<SourcePosition, char, &'a str>, input: &'a str) -> Self {
        Self {
            error: error,
            input: input,
        }
    }
}

impl<'a> Display for FancyError<'a> {
    fn fmt(&self, f: &mut Formatter) -> Result {
        let SourcePosition { line, column } = self.error.position;

        let offset = line.to_string().len();
        let content = self.input
            .split('\n')
            .nth((line - 1) as usize)
            .expect("line");

        writeln!(f, "TOML parse error at line {}, column {}", line, column)?;

        //   |
        for _ in 0..(offset + 1) {
            write!(f, " ")?;
        }
        writeln!(f, "|")?;

        // 1 | 00:32:00.a999999
        write!(f, "{} | ", line)?;
        writeln!(f, "{}", content)?;

        //   |          ^
        for _ in 0..(offset + 1) {
            write!(f, " ")?;
        }
        write!(f, "|")?;
        for _ in 0..column {
            write!(f, " ")?;
        }
        writeln!(f, "^")?;

        Error::fmt_errors(self.error.errors.as_ref(), f)
    }
}

#[derive(Debug, Clone)]
pub(crate) enum CustomError {
    MixedArrayType { got: String, expected: String },
    DuplicateKey { key: String, table: String },
    InvalidHexEscape(u32),
    UnparsedLine,
}

impl StdError for CustomError {
    fn description(&self) -> &'static str {
        "TOML parse error"
    }
}

impl Display for CustomError {
    fn fmt(&self, f: &mut Formatter) -> Result {
        match *self {
            CustomError::MixedArrayType {
                ref got,
                ref expected,
            } => writeln!(f, "Mixed types in array: {} and {}", expected, got),
            CustomError::DuplicateKey { ref key, ref table } => {
                writeln!(f, "Duplicate key `{}` in `{}` table", key, table)
            }
            CustomError::InvalidHexEscape(ref h) => {
                writeln!(f, "Invalid hex escape code: {:x} ", h)
            }
            CustomError::UnparsedLine => writeln!(f, "Could not parse the line"),
        }
    }
}