Skip to main content

switchback_protocols/
severity.rs

1//! Outcome severity mappers moved from `switchback-traits`.
2
3use switchback_traits::ResponseSeverity;
4
5/// Classify an OpenAPI / HTTP status key (`200`, `4XX`, `default`, …).
6pub fn http_status_severity(status: &str) -> ResponseSeverity {
7    let trimmed = status.trim();
8    if trimmed.eq_ignore_ascii_case("default") {
9        return ResponseSeverity::Unspecified;
10    }
11
12    if let Ok(code) = trimmed.parse::<u16>() {
13        return http_status_code_severity(code);
14    }
15
16    let upper = trimmed.to_ascii_uppercase();
17    match upper.as_str() {
18        "1XX" => ResponseSeverity::Informational,
19        "2XX" => ResponseSeverity::Success,
20        "3XX" => ResponseSeverity::Redirection,
21        "4XX" => ResponseSeverity::ClientError,
22        "5XX" => ResponseSeverity::ServerError,
23        _ => ResponseSeverity::Unspecified,
24    }
25}
26
27/// Classify a numeric HTTP status code.
28pub fn http_status_code_severity(code: u16) -> ResponseSeverity {
29    match code {
30        100..=199 => ResponseSeverity::Informational,
31        200..=299 => ResponseSeverity::Success,
32        300..=399 => ResponseSeverity::Redirection,
33        400..=499 => ResponseSeverity::ClientError,
34        500..=599 => ResponseSeverity::ServerError,
35        _ => ResponseSeverity::Unspecified,
36    }
37}
38
39/// Classify a gRPC status code (numeric `google.rpc.Code`).
40pub fn grpc_status_severity(code: i32) -> ResponseSeverity {
41    match code {
42        0 => ResponseSeverity::Success,
43        1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 15 => ResponseSeverity::ClientError,
44        12..=14 => ResponseSeverity::ServerError,
45        _ => ResponseSeverity::Unspecified,
46    }
47}
48
49/// Classify a gRPC status name (`OK`, `INVALID_ARGUMENT`, …).
50pub fn grpc_status_name_severity(name: &str) -> ResponseSeverity {
51    match name.trim().to_ascii_uppercase().as_str() {
52        "OK" => ResponseSeverity::Success,
53        "CANCELLED"
54        | "UNKNOWN"
55        | "INVALID_ARGUMENT"
56        | "DEADLINE_EXCEEDED"
57        | "NOT_FOUND"
58        | "ALREADY_EXISTS"
59        | "PERMISSION_DENIED"
60        | "RESOURCE_EXHAUSTED"
61        | "FAILED_PRECONDITION"
62        | "ABORTED"
63        | "OUT_OF_RANGE"
64        | "UNAUTHENTICATED"
65        | "UNIMPLEMENTED" => ResponseSeverity::ClientError,
66        "INTERNAL" | "UNAVAILABLE" | "DATA_LOSS" => ResponseSeverity::ServerError,
67        _ => ResponseSeverity::Unspecified,
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn http_status_classes() {
77        assert_eq!(http_status_severity("200"), ResponseSeverity::Success);
78        assert_eq!(http_status_severity("500"), ResponseSeverity::ServerError);
79    }
80
81    #[test]
82    fn grpc_status_classes() {
83        assert_eq!(grpc_status_severity(0), ResponseSeverity::Success);
84        assert_eq!(
85            grpc_status_name_severity("INTERNAL"),
86            ResponseSeverity::ServerError
87        );
88    }
89}