Skip to main content

nom_kconfig/
error.rs

1use std::{fmt, path::PathBuf};
2
3use nom::Needed;
4
5use crate::KconfigInput;
6
7#[derive(Debug, PartialEq, Eq)]
8pub struct Error {
9    pub filename: PathBuf,
10    pub resolved_path: PathBuf,
11    pub parent_file: Option<PathBuf>,
12    pub line: u32,
13    pub column: usize,
14    pub kind: ErrorKind,
15    pub input: String,
16}
17
18#[derive(Debug, PartialEq, Eq)]
19pub enum ErrorKind {
20    Nom(nom::error::ErrorKind),
21    Unknown,
22    Incomplete,
23}
24
25impl fmt::Display for ErrorKind {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        match self {
28            ErrorKind::Nom(kind) => write!(f, "{}", kind.description()),
29            ErrorKind::Unknown => write!(f, "unknown"),
30            ErrorKind::Incomplete => write!(f, "incomplete"),
31        }
32    }
33}
34
35impl std::error::Error for Error {}
36
37impl From<nom::Err<nom::error::Error<KconfigInput<'_>>>> for Error {
38    fn from(value: nom::Err<nom::error::Error<KconfigInput>>) -> Self {
39        match value {
40            nom::Err::Incomplete(Needed::Size(_u)) => Error {
41                filename: Default::default(),
42                resolved_path: Default::default(),
43                parent_file: None,
44                line: 0,
45                column: 0,
46                input: "Input is not available".to_string(),
47                kind: ErrorKind::Incomplete,
48            },
49            nom::Err::Incomplete(Needed::Unknown) => Error {
50                filename: Default::default(),
51                resolved_path: Default::default(),
52                parent_file: None,
53                line: 0,
54                column: 0,
55                input: "Input is not available".to_string(),
56                kind: ErrorKind::Incomplete,
57            },
58            nom::Err::Error(error) => Error {
59                filename: error.input.extra.file.clone(),
60                resolved_path: error.input.extra.full_path(),
61                parent_file: error.input.extra.parent_file.clone(),
62                line: error.input.location_line(),
63                column: error.input.location_offset(),
64                input: error.input.fragment().to_string(),
65                kind: ErrorKind::Nom(error.code),
66            },
67            nom::Err::Failure(failure) => Error {
68                filename: failure.input.extra.file.clone(),
69                resolved_path: failure.input.extra.full_path(),
70                parent_file: failure.input.extra.parent_file.clone(),
71                line: failure.input.location_line(),
72                column: failure.input.location_offset(),
73                input: failure.input.fragment().to_string(),
74                kind: ErrorKind::Nom(failure.code),
75            },
76        }
77    }
78}
79
80impl fmt::Display for Error {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        write!(
83            f,
84            r#"error[{}]: Something went wrong while parsing at the following location:
85    Input file: {}:{}:{}
86 Resolved path: {}:{}:{}
87   Parent file: {}
88
89The parser failed at the following input:
90{}"#,
91            self.kind,
92            self.filename.display(),
93            self.line,
94            self.column,
95            self.resolved_path.display(),
96            self.line,
97            self.column,
98            self.parent_file
99                .as_ref()
100                .map(|e| e.display().to_string())
101                .unwrap_or_default(),
102            self.input.lines().take(10).collect::<Vec<_>>().join("\n"),
103        )
104    }
105}