1use crate::{location::Location, Context, Input, State};
2use std::fmt::{Debug, Display};
3
4pub type Result<R> = std::result::Result<R, Error>;
5
6#[derive(Debug)]
9pub enum Error {
10 Error {
11 message: String,
12 file: Option<String>,
13 location: Option<Location>,
14 },
15 IOError(std::io::Error),
16}
17impl Error {
20 pub fn to_locfile_str(&self) -> String {
23 match self {
24 Error::Error {
25 message,
26 file,
27 location,
28 } => {
29 let mut loc_str = String::from("Error");
30 if file.is_some() || location.is_some() {
31 loc_str.push_str(" at ");
32 }
33 if let Some(file) = file {
34 if let Some((_, file)) = file.rsplit_once('/') {
35 loc_str.push_str(file);
36 } else {
37 loc_str.push_str(file);
38 }
39 if location.is_some() {
40 loc_str.push(':');
41 }
42 }
43 if let Some(location) = location {
44 loc_str.push_str(&format!("{location:?}"));
45 }
46 format!("{}:\n\t{}", loc_str, message.replace('\n', "\n\t"))
47 }
48 Error::IOError(e) => format!("IOError: {e}"),
49 }
50 }
51}
52
53impl Display for Error {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 match self {
56 Error::Error {
57 message,
58 file,
59 location,
60 } => {
61 let mut loc_str = String::from("Error");
62 if file.is_some() || location.is_some() {
63 loc_str.push_str(" at ");
64 }
65 if let Some(file) = file {
66 loc_str.push_str(file);
67 if location.is_some() {
68 loc_str.push(':');
69 }
70 }
71 if let Some(location) = location {
72 loc_str.push_str(&format!("{location:?}"));
73 }
74 write!(f, "{}:\n\t{}", loc_str, message.replace('\n', "\n\t"))
75 }
76 Error::IOError(e) => write!(f, "IOError: {e}"),
77 }
78 }
79}
80
81impl From<std::io::Error> for Error {
82 fn from(e: std::io::Error) -> Self {
83 Error::IOError(e)
84 }
85}
86
87impl<R> From<Error> for Result<R> {
88 fn from(value: Error) -> Self {
89 Self::Err(value)
90 }
91}
92
93pub(crate) fn error_expected<'i, I, S, TK, C>(
94 input: &'i I,
95 file_name: &str,
96 context: &C,
97 expected: &[TK],
98) -> Error
99where
100 C: Context<'i, I, S, TK>,
101 I: Input + ?Sized,
102 S: State,
103 TK: Debug,
104{
105 let expected = if expected.len() > 1 {
106 format!(
107 "one of {}",
108 expected
109 .iter()
110 .map(|t| format!("{t:?}"))
111 .collect::<Vec<_>>()
112 .join(", ")
113 )
114 } else {
115 format!("{:?}", expected[0])
116 };
117 Error::Error {
118 message: format!(
119 "...{}...\nExpected {}.",
120 input.context_str(context.position()),
121 expected
122 ),
123 file: Some(file_name.to_string()),
124 location: Some(context.location()),
125 }
126}
127
128#[macro_export]
130macro_rules! err {
131 ($message:expr) => {
132 Result::from(Error::Error {
133 message: $message,
134 file: None,
135 location: None,
136 })
137 };
138 ($message:expr, $file:expr) => {
139 Result::from(Error::Error {
140 message: $message,
141 file: $file,
142 location: None,
143 })
144 };
145 ($message:expr, $file:expr, $location:expr) => {
146 Result::from(Error::Error {
147 message: $message,
148 file: $file,
149 location: $location,
150 })
151 };
152}