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}