nginx_config/
error.rs

1use combine::easy::{Errors, Error};
2
3use tokenizer::Token;
4use position::Pos;
5
6pub type InternalError<'a> = Errors<Token<'a>, Token<'a>, Pos>;
7
8
9/// Error parsing config
10///
11/// This structure is opaque for forward compatibility. We are exploring a
12/// way to improve both error message and API.
13#[derive(Fail, Debug)]
14#[fail(display="parse error: {}", _0)]
15pub struct ParseError(Errors<String, String, Pos>);
16
17#[cfg(not(feature="fuzzy_errors"))]
18impl<'a> From<InternalError<'a>> for ParseError {
19    fn from(e: InternalError<'a>) -> ParseError {
20        ParseError(e
21            .map_token(|t| t.value.to_string())
22            .map_range(|t| t.value.to_string()))
23    }
24}
25
26fn convert(error: Error<Token, Token>) -> Error<String, String> {
27    error
28    .map_token(|t| t.value.to_string())
29    .map_range(|t| t.value.to_string())
30}
31
32#[cfg(feature="fuzzy_errors")]
33impl<'a> From<InternalError<'a>> for ParseError {
34    fn from(e: InternalError<'a>) -> ParseError {
35        use strsim::jaro_winkler;
36        use combine::easy::{Info};
37
38        let mut error_buf = Vec::new();
39        let mut expected_buf = Vec::new();
40        let mut unexpected = None;
41        // Note: we assume that "expected" will go after error
42        //       in output and that's fine
43        for item in e.errors {
44            match item {
45                Error::Expected(info) => {
46                    expected_buf.push(info);
47                    continue;
48                }
49                Error::Unexpected(ref val) => {
50                    unexpected = Some(val.to_string());
51                }
52                _ => {}
53            }
54            error_buf.push(convert(item));
55        }
56        println!("Unexpected {:?}, expected {:?}", unexpected, expected_buf);
57        if let Some(unexpected) = unexpected {
58            if expected_buf.len() > 3 {
59                let mut close = Vec::new();
60                for item in &expected_buf {
61                    match item {
62                        Info::Borrowed(item) => {
63                            let conf = jaro_winkler(&unexpected, item);
64                            if conf > 0.8 {
65                                close.push((item, conf));
66                            }
67                        }
68                        _ => {
69                            // assuming any other thing is just a text, not
70                            // expected token
71                        }
72                    }
73                }
74                close.sort_by_key(|&(_, ref x)| (10000. - 10000. * x) as u32);
75                close.truncate(3);
76                for (item, _) in &close {
77                    error_buf.push(convert(Error::Expected(
78                        Info::Borrowed(item))));
79                }
80                if close.len() < expected_buf.len() {
81                    error_buf.push(Error::Expected(Info::Owned(format!(
82                        "one of {} options",
83                        expected_buf.len() - close.len(),
84                    ))));
85                }
86            } else {
87                for e in expected_buf {
88                    error_buf.push(convert(Error::Expected(e)));
89                }
90            }
91        } else {
92            for e in expected_buf {
93                error_buf.push(convert(Error::Expected(e)));
94            }
95        }
96        return ParseError(Errors { position: e.position, errors: error_buf })
97    }
98}