Skip to main content

css_inline/
error.rs

1//! Errors that may happen during inlining.
2use cssparser::{BasicParseErrorKind, ParseError, ParseErrorKind, SourceLocation};
3use std::{
4    borrow::Cow,
5    error::Error,
6    fmt,
7    fmt::{Display, Formatter},
8    io,
9};
10
11/// Inlining error
12#[derive(Debug)]
13pub enum InlineError {
14    /// Missing stylesheet file.
15    MissingStyleSheet {
16        /// Path to the missing file.
17        path: String,
18    },
19    /// Input-output error. May happen during writing the resulting HTML or retrieving a stylesheet
20    /// from the filesystem.
21    IO(io::Error),
22    /// Network-related problem. E.g. resource is not available.
23    #[cfg(feature = "http")]
24    Network {
25        /// Original network error.
26        error: reqwest::Error,
27        /// The stylesheet location caused the error.
28        location: String,
29    },
30    /// Syntax errors or unsupported selectors.
31    ParseError(Cow<'static, str>),
32}
33
34impl From<io::Error> for InlineError {
35    fn from(error: io::Error) -> Self {
36        Self::IO(error)
37    }
38}
39
40impl Error for InlineError {
41    fn source(&self) -> Option<&(dyn Error + 'static)> {
42        match self {
43            InlineError::IO(error) => Some(error),
44            #[cfg(feature = "http")]
45            InlineError::Network { error, .. } => Some(error),
46            InlineError::MissingStyleSheet { .. } | InlineError::ParseError(_) => None,
47        }
48    }
49}
50
51impl Display for InlineError {
52    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
53        match self {
54            Self::IO(error) => error.fmt(f),
55            #[cfg(feature = "http")]
56            Self::Network { error, location } => f.write_fmt(format_args!("{error}: {location}")),
57            Self::ParseError(error) => f.write_str(error),
58            Self::MissingStyleSheet { path } => {
59                f.write_fmt(format_args!("Missing stylesheet file: {path}"))
60            }
61        }
62    }
63}
64
65impl From<(ParseError<()>, &str, SourceLocation)> for InlineError {
66    fn from(error: (ParseError<()>, &str, SourceLocation)) -> Self {
67        // `cssparser` does not store the offending token or rule name; the source slice ends with it
68        let source = error.1;
69        match error.0.kind {
70            ParseErrorKind::Basic(kind) => match kind {
71                BasicParseErrorKind::UnexpectedToken => {
72                    let mut parser = cssparser::Parser::new(source);
73                    let mut last = None;
74                    while let Ok(token) = parser.next() {
75                        last = Some(format!("{token:?}"));
76                    }
77                    match last {
78                        Some(token) => {
79                            Self::ParseError(Cow::Owned(format!("Unexpected token: {token}")))
80                        }
81                        None => Self::ParseError(Cow::Borrowed("Unexpected token")),
82                    }
83                }
84                BasicParseErrorKind::EndOfInput => Self::ParseError(Cow::Borrowed("End of input")),
85                BasicParseErrorKind::AtRuleInvalid => {
86                    let name = source
87                        .trim_start()
88                        .trim_start_matches('@')
89                        .split(|c: char| c.is_whitespace() || matches!(c, '{' | ';'))
90                        .next()
91                        .unwrap_or_default();
92                    Self::ParseError(Cow::Owned(format!("Invalid @ rule: {name}")))
93                }
94                BasicParseErrorKind::TooManyNestedBlocks => {
95                    Self::ParseError(Cow::Borrowed("Too many nested blocks"))
96                }
97                BasicParseErrorKind::AtRuleBodyInvalid => {
98                    Self::ParseError(Cow::Borrowed("Invalid @ rule body"))
99                }
100                BasicParseErrorKind::QualifiedRuleInvalid => {
101                    Self::ParseError(Cow::Borrowed("Invalid qualified rule"))
102                }
103            },
104            ParseErrorKind::Custom(()) => Self::ParseError(Cow::Borrowed("Unknown error")),
105        }
106    }
107}