1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/// Construct an ad-hoc `HttpError` from a status code, optional source error and formatted reason.
#[macro_export]
macro_rules! http_error{
    ($status_code:ident $(, source = $src:ident)? $(, reason = $($arg:tt)*)?) => {{
        let http_error: $crate::HttpError<_>
            = $crate::HttpError::from_status_code($crate::http::StatusCode::$status_code)
            $(
                .with_source_err($src)
             )?
            $(
                .with_reason(std::format!($($arg)*))
             )?;
        http_error
    }};
    ($status_code:ident $(, $($arg:tt)*)?) => {{
        let http_error: $crate::HttpError<_>
            = $crate::HttpError::from_status_code($crate::http::StatusCode::$status_code)
            $(
                .with_reason(std::format!($($arg)*))
             )?;
        http_error
    }};
}

/// Shorthand macro to return early with a `HttpError`.
#[macro_export]
macro_rules! http_error_ret {
    ($status_code:ident $(, source = $src:ident)? $(, reason = $($arg:tt)*)?) => {
        return Err($crate::http_error!($status_code $(, source = $src)? $(, reason = $($arg)*)?).into())
    };
    ($status_code:ident $(, $($arg:tt)*)?) => {
        return Err($crate::http_error!($status_code $(, $($arg)*)?).into())
    };
}

/// Shorthand macro to map to a `HttpError` from any error within `.map_err()`.
#[macro_export]
macro_rules! http_error_map_fn {
    ($status_code:ident $(, $($arg:tt)*)?) => {
        |e| -> $crate::HttpError<_> {
            #[allow(clippy::useless_conversion)]
            HttpError::from(e).with_status_code($crate::http::StatusCode::$status_code)
            $(
                .with_reason(std::format!($($arg)*))
             )?
        }
    };
}

#[cfg(test)]
mod tests {
    use crate::*;
    use anyhow::anyhow;
    use http::StatusCode;

    #[test]
    fn http_error_status_code() {
        let e: HttpError<()> = http_error!(BAD_REQUEST);
        assert_eq!(e.status_code, StatusCode::BAD_REQUEST);
    }

    #[test]
    fn http_error_only_reason() {
        let e: HttpError<()> = http_error!(BAD_REQUEST, "error {}", 1);
        assert_eq!(e.status_code, StatusCode::BAD_REQUEST);
        assert_eq!(e.reason, Some("error 1".to_string()));
    }

    #[test]
    fn http_error_status_and_source() {
        let source = anyhow!("an error");
        let e: HttpError<()> = http_error!(BAD_REQUEST, source = source);
        assert_eq!(e.status_code, StatusCode::BAD_REQUEST);
        assert!(e.source.is_some());
    }

    #[test]
    fn http_error_status_source_and_format() {
        let source = anyhow!("an error");
        let e: HttpError<()> = http_error!(BAD_REQUEST, source = source, reason = "error {}", 1);
        assert_eq!(e.status_code, StatusCode::BAD_REQUEST);
        assert!(e.source.is_some());
        assert_eq!(e.reason, Some("error 1".to_string()));
    }
}