Skip to main content

node_js_release_info/
error.rs

1use std::error::Error;
2use std::fmt::{Display, Formatter, Result};
3
4/// The error type returned by all fallible operations in this crate
5///
6/// Non-exhaustive: new variants may appear in a minor release
7#[derive(Debug)]
8#[non_exhaustive]
9pub enum NodeJsRelInfoError {
10    /// The operating system for the Node.js distributable you are targeting is
11    /// unrecognized - see: [`NodeJsOs`](crate::NodeJsOs) for options
12    UnrecognizedOs(String),
13    /// The CPU architecture for the Node.js distributable you are targeting is
14    /// unrecognized - see: [`NodeJsArch`](crate::NodeJsArch) for options
15    UnrecognizedArch(String),
16    /// The file extension of the Node.js distributable you are targeting is
17    /// unrecognized - see: [`NodeJsPkgExt`](crate::NodeJsPkgExt) for options
18    UnrecognizedExt(String),
19    /// The version string provided is invalid - see: [semver](https://semver.org)
20    InvalidVersion(String),
21    /// The version of Node.js you are targeting is not available
22    UnrecognizedVersion(String),
23    /// The Node.js configuration you are targeting is not available
24    UnrecognizedConfiguration(String),
25    /// Something went wrong issuing or processing the HTTP GET request to the Node.js [downloads server](https://nodejs.org/download/release/)
26    HttpError(reqwest::Error),
27}
28
29impl Error for NodeJsRelInfoError {
30    /// Exposes the underlying [`reqwest::Error`] behind
31    /// [`HttpError`](NodeJsRelInfoError::HttpError) so callers (and error
32    /// reporters like `anyhow`) can walk the full cause chain
33    fn source(&self) -> Option<&(dyn Error + 'static)> {
34        match self {
35            NodeJsRelInfoError::HttpError(e) => Some(e),
36            _ => None,
37        }
38    }
39}
40
41impl Display for NodeJsRelInfoError {
42    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
43        let message = match self {
44            NodeJsRelInfoError::UnrecognizedOs(input) => {
45                format!("unrecognized os - received: '{input}'")
46            }
47            NodeJsRelInfoError::UnrecognizedArch(input) => {
48                format!("unrecognized arch - received: '{input}'")
49            }
50            NodeJsRelInfoError::UnrecognizedExt(input) => {
51                format!("unrecognized file extension - received: '{input}'")
52            }
53            NodeJsRelInfoError::InvalidVersion(input) => {
54                format!("invalid version - received: '{input}'")
55            }
56            NodeJsRelInfoError::UnrecognizedVersion(input) => {
57                format!("unrecognized version - received: '{input}'")
58            }
59            NodeJsRelInfoError::UnrecognizedConfiguration(input) => {
60                format!("unrecognized configuration - received: '{input}'")
61            }
62            NodeJsRelInfoError::HttpError(e) => return write!(f, "{e}"),
63        };
64
65        write!(f, "{message}")
66    }
67}
68
69impl From<reqwest::Error> for NodeJsRelInfoError {
70    fn from(e: reqwest::Error) -> Self {
71        NodeJsRelInfoError::HttpError(e)
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn it_prints_expected_message_when_os_is_unrecognized() {
81        let err = NodeJsRelInfoError::UnrecognizedOs("unknown-os".to_string());
82        assert_eq!(format!("{err}"), "unrecognized os - received: 'unknown-os'");
83    }
84
85    #[test]
86    fn it_prints_expected_message_when_arch_is_unrecognized() {
87        let err = NodeJsRelInfoError::UnrecognizedArch("unknown-arch".to_string());
88        assert_eq!(
89            format!("{err}"),
90            "unrecognized arch - received: 'unknown-arch'"
91        );
92    }
93
94    #[test]
95    fn it_prints_expected_message_when_extension_is_unrecognized() {
96        let err = NodeJsRelInfoError::UnrecognizedExt("unknown-ext".to_string());
97        assert_eq!(
98            format!("{err}"),
99            "unrecognized file extension - received: 'unknown-ext'"
100        );
101    }
102
103    #[test]
104    fn it_prints_expected_message_when_version_is_invalid() {
105        let err = NodeJsRelInfoError::InvalidVersion("invalid-ver".to_string());
106        assert_eq!(
107            format!("{err}"),
108            "invalid version - received: 'invalid-ver'"
109        );
110    }
111
112    #[test]
113    fn it_prints_expected_message_when_version_is_unrecognized() {
114        let err = NodeJsRelInfoError::UnrecognizedVersion("unknown-ver".to_string());
115        assert_eq!(
116            format!("{err}"),
117            "unrecognized version - received: 'unknown-ver'"
118        );
119    }
120
121    #[test]
122    fn it_prints_expected_message_when_configuration_is_unrecognized() {
123        let err = NodeJsRelInfoError::UnrecognizedConfiguration("unknown-cfg".to_string());
124        assert_eq!(
125            format!("{err}"),
126            "unrecognized configuration - received: 'unknown-cfg'"
127        );
128    }
129
130    #[tokio::test]
131    async fn it_prints_expected_message_upon_http_error() {
132        let source = reqwest::get("not-a-url").await.unwrap_err();
133        // NOTE: `HttpError` delegates to the wrapped `reqwest::Error` verbatim
134        // so assert on that rather than on reqwest's exact wording, which
135        // changes between releases
136        let expected = source.to_string();
137        let err = NodeJsRelInfoError::from(source);
138
139        assert_eq!(format!("{err}"), expected);
140    }
141}