anyhow_http/
macros.rs

1/// Construct an ad-hoc [`Error`](anyhow::Error) from a status code, optional source error and formatted reason.
2///
3/// ```
4/// # use anyhow::anyhow;
5/// # use anyhow_http::http_error;
6/// fn foo() -> anyhow::Result<()> {
7///     const CODE: i32 = 1234;
8///     Err(http_error!(BAD_REQUEST, "invalid payload, code {}", CODE))?;
9///
10///     // with source
11///     let source = anyhow!("source error");
12///     Err(http_error!(BAD_REQUEST, source = source, reason = "invalid payload, code {}", CODE))?;
13///     Ok(())
14/// }
15/// ```
16#[macro_export]
17macro_rules! http_error{
18    ($status_code:ident, $reason:literal) => {
19        ::anyhow::Error::from($crate::HttpError::from_static($crate::http::StatusCode::$status_code, $reason))
20    };
21    ($status_code:ident $(, source = $src:expr)? $(, reason = $($arg:tt)*)?) => {{
22        let http_error
23            = $crate::HttpError::from_status_code($crate::http::StatusCode::$status_code)
24            $(
25                .with_source_err($src)
26             )?
27            $(
28                .with_reason(std::format!($($arg)*))
29             )?;
30        ::anyhow::Error::from(http_error)
31    }};
32    ($status_code:ident $(, $($arg:tt)*)?) => {
33        ::anyhow::Error::from($crate::http_error!($status_code $(, reason = $($arg)*)?))
34    };
35}
36
37/// Shorthand macro to return early with an [`Error`](anyhow::Error).
38///
39/// Example:
40/// ```
41/// # use anyhow_http::http_error_bail;
42/// fn foo() -> anyhow::Result<()> {
43///     http_error_bail!(BAD_REQUEST, "invalid payload")
44/// }
45/// ```
46#[macro_export]
47macro_rules! http_error_bail {
48    ($status_code:ident $(, source = $src:expr)? $(, reason = $($arg:tt)*)?) => {
49        return Err($crate::http_error!($status_code $(, source = $src)? $(, reason = $($arg)*)?).into())
50    };
51    ($status_code:ident $(, $($arg:tt)*)?) => {
52        return Err($crate::http_error!($status_code $(, reason = $($arg)*)?).into())
53    };
54}
55
56#[cfg(test)]
57mod tests {
58
59    use crate::*;
60    use anyhow::anyhow;
61    use http::StatusCode;
62
63    #[test]
64    fn http_error_status_code() {
65        let e: HttpError = http_error!(BAD_REQUEST).into();
66        assert_eq!(e.status_code, StatusCode::BAD_REQUEST);
67    }
68
69    #[test]
70    fn http_error_only_reason() {
71        let i = 1;
72        let e: HttpError = http_error!(BAD_REQUEST, "error {}", i).into();
73        assert_eq!(e.status_code, StatusCode::BAD_REQUEST);
74        assert_eq!(e.reason, Some("error 1".into()));
75    }
76
77    #[test]
78    fn http_error_status_and_source() {
79        let source = anyhow!("an error");
80        let e: HttpError = http_error!(BAD_REQUEST, source = source).into();
81        assert_eq!(e.status_code, StatusCode::BAD_REQUEST);
82        assert!(e.source.is_some());
83    }
84
85    #[test]
86    fn http_error_status_source_and_format() {
87        let source = anyhow!("an error");
88        let e: HttpError = http_error!(BAD_REQUEST, source = source, reason = "error {}", 1).into();
89        assert_eq!(e.status_code, StatusCode::BAD_REQUEST);
90        assert!(e.source.is_some());
91        assert_eq!(e.reason, Some("error 1".into()));
92    }
93
94    #[test]
95    fn http_error_bridge() {
96        let _err: anyhow::Error = http_error!(BAD_REQUEST, "error",);
97        let _err: HttpError = http_error!(BAD_REQUEST, "error",).into();
98    }
99}