Skip to main content

lychee_lib/types/
request_error.rs

1use std::convert::TryFrom;
2use std::sync::LazyLock;
3use thiserror::Error;
4
5use crate::{ErrorKind, RawUri, Response, Status, Uri};
6use crate::{InputSource, ResolvedInputSource};
7
8static ERROR_URI: LazyLock<Uri> = LazyLock::new(|| Uri::try_from("error:").unwrap());
9
10/// An error which occurs while trying to construct a [`Request`] object.
11/// That is, an error which happens while trying to load links from an input
12/// source.
13#[derive(Error, Debug, PartialEq, Eq, Hash)]
14pub enum RequestError {
15    /// Unable to construct a URL for a link appearing within the given source.
16    #[error("Error building URL for {0}: {2}")]
17    CreateRequestItem(RawUri, ResolvedInputSource, #[source] ErrorKind),
18
19    /// Unable to load the content of an input source.
20    #[error("Error reading input '{0}': {1}")]
21    GetInputContent(InputSource, #[source] ErrorKind),
22
23    /// Unable to load an input source directly specified by the user.
24    #[error("Error reading user input '{0}': {1}")]
25    UserInputContent(InputSource, #[source] ErrorKind),
26}
27
28impl RequestError {
29    /// Get the underlying cause of this [`RequestError`].
30    #[must_use]
31    pub const fn error(&self) -> &ErrorKind {
32        match self {
33            Self::CreateRequestItem(_, _, e)
34            | Self::GetInputContent(_, e)
35            | Self::UserInputContent(_, e) => e,
36        }
37    }
38
39    /// Convert this [`RequestError`] into its source error.
40    #[must_use]
41    pub fn into_error(self) -> ErrorKind {
42        match self {
43            Self::CreateRequestItem(_, _, e)
44            | Self::GetInputContent(_, e)
45            | Self::UserInputContent(_, e) => e,
46        }
47    }
48
49    /// Get (a clone of) the input source within which the error happened.
50    #[must_use]
51    pub fn input_source(&self) -> InputSource {
52        match self {
53            Self::CreateRequestItem(_, src, _) => src.clone().into(),
54            Self::GetInputContent(src, _) | Self::UserInputContent(src, _) => src.clone(),
55        }
56    }
57
58    /// Convert this request error into a (failed) [`Response`] for reporting
59    /// purposes.
60    ///
61    /// # Errors
62    ///
63    /// If this `RequestError` was caused by failing to load a user-specified
64    /// input, the underlying cause of the `RequestError` will be returned
65    /// as an Err. This allows the error to be propagated back to the user.
66    pub fn into_response(self) -> Result<Response, ErrorKind> {
67        match self {
68            RequestError::UserInputContent(_, e) => Err(e),
69            e => {
70                let src = e.input_source();
71                Ok(Response::new(
72                    ERROR_URI.clone(),
73                    Status::RequestError(e),
74                    src,
75                ))
76            }
77        }
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::ERROR_URI;
84    use std::sync::LazyLock;
85
86    #[test]
87    fn test_error_url_parses() {
88        let _ = LazyLock::force(&ERROR_URI);
89    }
90}