1use std::{
2 error::Error,
3 fmt::{self, Display},
4 io,
5 string::FromUtf8Error,
6 sync::Arc,
7};
8
9use codemap::{Span, SpanLoc};
10
11pub type SassResult<T> = Result<T, Box<SassError>>;
12
13#[derive(Debug, Clone)]
29pub struct SassError {
30 kind: SassErrorKind,
31}
32
33impl SassError {
34 #[must_use]
35 pub fn kind(self) -> PublicSassErrorKind {
36 match self.kind {
37 SassErrorKind::ParseError {
38 message,
39 loc,
40 unicode,
41 } => PublicSassErrorKind::ParseError {
42 message,
43 loc,
44 unicode,
45 },
46 SassErrorKind::FromUtf8Error(s) => PublicSassErrorKind::FromUtf8Error(s),
47 SassErrorKind::IoError(io) => PublicSassErrorKind::IoError(io),
48 SassErrorKind::Raw(..) => unreachable!("raw errors should not be accessible by users"),
49 }
50 }
51
52 pub(crate) fn raw(self) -> (String, Span) {
53 match self.kind {
54 SassErrorKind::Raw(string, span) => (string, span),
55 e => unreachable!("unable to get raw of {:?}", e),
56 }
57 }
58
59 pub(crate) const fn from_loc(message: String, loc: SpanLoc, unicode: bool) -> Self {
60 SassError {
61 kind: SassErrorKind::ParseError {
62 message,
63 loc,
64 unicode,
65 },
66 }
67 }
68}
69
70#[non_exhaustive]
71#[derive(Debug, Clone)]
72pub enum PublicSassErrorKind {
73 ParseError {
74 message: String,
83 loc: SpanLoc,
84
85 unicode: bool,
90 },
91
92 IoError(Arc<io::Error>),
97
98 FromUtf8Error(String),
100}
101
102#[derive(Debug, Clone)]
103enum SassErrorKind {
104 Raw(String, Span),
108 ParseError {
109 message: String,
110 loc: SpanLoc,
111 unicode: bool,
112 },
113 IoError(Arc<io::Error>),
115 FromUtf8Error(String),
116}
117
118impl Display for SassError {
119 #[inline]
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 let (message, loc, unicode) = match &self.kind {
125 SassErrorKind::ParseError {
126 message,
127 loc,
128 unicode,
129 } => (message, loc, *unicode),
130 SassErrorKind::FromUtf8Error(..) => return writeln!(f, "Error: Invalid UTF-8."),
131 SassErrorKind::IoError(s) => return writeln!(f, "Error: {}", s),
132 SassErrorKind::Raw(..) => unreachable!(),
133 };
134
135 let first_bar = if unicode { '╷' } else { ',' };
136 let second_bar = if unicode { '│' } else { '|' };
137 let third_bar = if unicode { '│' } else { '|' };
138 let fourth_bar = if unicode { '╵' } else { '\'' };
139
140 let line = loc.begin.line + 1;
141 let col = loc.begin.column + 1;
142 writeln!(f, "Error: {}", message)?;
143 let padding = vec![' '; format!("{}", line).len() + 1]
144 .iter()
145 .collect::<String>();
146 writeln!(f, "{}{}", padding, first_bar)?;
147 writeln!(
148 f,
149 "{} {} {}",
150 line,
151 second_bar,
152 loc.file.source_line(loc.begin.line)
153 )?;
154 writeln!(
155 f,
156 "{}{} {}{}",
157 padding,
158 third_bar,
159 vec![' '; loc.begin.column].iter().collect::<String>(),
160 vec!['^'; loc.end.column.max(loc.begin.column) - loc.begin.column.min(loc.end.column)]
161 .iter()
162 .collect::<String>()
163 )?;
164 writeln!(f, "{}{}", padding, fourth_bar)?;
165
166 if unicode {
167 writeln!(f, "./{}:{}:{}", loc.file.name(), line, col)?;
168 } else {
169 writeln!(f, " {} {}:{} root stylesheet", loc.file.name(), line, col)?;
170 }
171 Ok(())
172 }
173}
174
175impl From<io::Error> for Box<SassError> {
176 #[inline]
177 fn from(error: io::Error) -> Box<SassError> {
178 Box::new(SassError {
179 kind: SassErrorKind::IoError(Arc::new(error)),
180 })
181 }
182}
183
184impl From<FromUtf8Error> for Box<SassError> {
185 #[inline]
186 fn from(error: FromUtf8Error) -> Box<SassError> {
187 Box::new(SassError {
188 kind: SassErrorKind::FromUtf8Error(format!(
189 "Invalid UTF-8 character \"\\x{:X?}\"",
190 error.as_bytes()[0]
191 )),
192 })
193 }
194}
195
196impl From<(&str, Span)> for Box<SassError> {
197 #[inline]
198 fn from(error: (&str, Span)) -> Box<SassError> {
199 Box::new(SassError {
200 kind: SassErrorKind::Raw(error.0.to_owned(), error.1),
201 })
202 }
203}
204
205impl From<(String, Span)> for Box<SassError> {
206 #[inline]
207 fn from(error: (String, Span)) -> Box<SassError> {
208 Box::new(SassError {
209 kind: SassErrorKind::Raw(error.0, error.1),
210 })
211 }
212}
213
214impl Error for SassError {
215 #[inline]
216 fn description(&self) -> &'static str {
217 "Sass parsing error"
218 }
219}