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
use combine::easy::{Errors, Error};

use tokenizer::Token;
use position::Pos;

pub type InternalError<'a> = Errors<Token<'a>, Token<'a>, Pos>;


/// Error parsing config
///
/// This structure is opaque for forward compatibility. We are exploring a
/// way to improve both error message and API.
#[derive(Fail, Debug)]
#[fail(display="parse error: {}", _0)]
pub struct ParseError(Errors<String, String, Pos>);

#[cfg(not(feature="fuzzy_errors"))]
impl<'a> From<InternalError<'a>> for ParseError {
    fn from(e: InternalError<'a>) -> ParseError {
        ParseError(e
            .map_token(|t| t.value.to_string())
            .map_range(|t| t.value.to_string()))
    }
}

fn convert(error: Error<Token, Token>) -> Error<String, String> {
    error
    .map_token(|t| t.value.to_string())
    .map_range(|t| t.value.to_string())
}

#[cfg(feature="fuzzy_errors")]
impl<'a> From<InternalError<'a>> for ParseError {
    fn from(e: InternalError<'a>) -> ParseError {
        use strsim::jaro_winkler;
        use combine::easy::{Info};

        let mut error_buf = Vec::new();
        let mut expected_buf = Vec::new();
        let mut unexpected = None;
        // Note: we assume that "expected" will go after error
        //       in output and that's fine
        for item in e.errors {
            match item {
                Error::Expected(info) => {
                    expected_buf.push(info);
                    continue;
                }
                Error::Unexpected(ref val) => {
                    unexpected = Some(val.to_string());
                }
                _ => {}
            }
            error_buf.push(convert(item));
        }
        if let Some(unexpected) = unexpected {
            if expected_buf.len() > 3 {
                let mut close = Vec::new();
                for item in &expected_buf {
                    match item {
                        Info::Borrowed(item) => {
                            let conf = jaro_winkler(&unexpected, item);
                            if conf > 0.8 {
                                close.push((item, conf));
                            }
                        }
                        _ => {
                            // assuming any other thing is just a text, not
                            // expected token
                        }
                    }
                }
                close.sort_by_key(|&(_, ref x)| (10000. - 10000. * x) as u32);
                close.truncate(3);
                for (item, _) in &close {
                    error_buf.push(convert(Error::Expected(
                        Info::Borrowed(item))));
                }
                if close.len() < expected_buf.len() {
                    error_buf.push(Error::Expected(Info::Owned(format!(
                        "one of {} options",
                        expected_buf.len() - close.len(),
                    ))));
                }
            } else {
                for e in expected_buf {
                    error_buf.push(convert(Error::Expected(e)));
                }
            }
        } else {
            for e in expected_buf {
                error_buf.push(convert(Error::Expected(e)));
            }
        }
        return ParseError(Errors { position: e.position, errors: error_buf })
    }
}