Skip to main content

tap_http/
error.rs

1//! Error handling for the TAP HTTP server.
2//!
3//! This module provides a comprehensive error handling system for the TAP HTTP server.
4//! It defines error types for various failure scenarios and provides conversions
5//! from common error types to the tap-http error type.
6
7use thiserror::Error;
8use warp::Reply;
9
10/// Result type for tap-http operations.
11pub type Result<T> = std::result::Result<T, Error>;
12
13/// Error types for the TAP HTTP server.
14#[derive(Error, Debug)]
15pub enum Error {
16    /// Invalid DIDComm message format.
17    #[error("Invalid DIDComm message: {0}")]
18    DIDComm(String),
19
20    /// Message validation error.
21    #[error("Message validation failed: {0}")]
22    Validation(String),
23
24    /// Message authentication error.
25    #[error("Message authentication failed: {0}")]
26    Authentication(String),
27
28    /// JSON serialization/deserialization error.
29    #[error("JSON error: {0}")]
30    Json(String),
31
32    /// HTTP server error.
33    #[error("HTTP error: {0}")]
34    Http(String),
35
36    /// TAP Node error.
37    #[error("TAP Node error: {0}")]
38    Node(String),
39
40    /// Configuration error.
41    #[error("Configuration error: {0}")]
42    Config(String),
43
44    /// IO error.
45    #[error("IO error: {0}")]
46    Io(#[from] std::io::Error),
47
48    /// Rate limiting error.
49    #[error("Rate limit exceeded: {0}")]
50    RateLimit(String),
51
52    /// TLS error.
53    #[error("TLS error: {0}")]
54    Tls(String),
55
56    /// Unknown error.
57    #[error("Unknown error: {0}")]
58    Unknown(String),
59}
60
61/// Error severity for logging and reporting.
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub enum ErrorSeverity {
64    /// Informational errors (e.g., validation failures)
65    Info,
66    /// Warning-level errors (e.g., rate limiting)
67    Warning,
68    /// Critical errors (e.g., server failures)
69    Critical,
70}
71
72impl Error {
73    /// Returns the HTTP status code that should be used for this error.
74    pub fn status_code(&self) -> warp::http::StatusCode {
75        use warp::http::StatusCode;
76
77        match self {
78            Error::DIDComm(_) | Error::Validation(_) | Error::Json(_) => StatusCode::BAD_REQUEST,
79            Error::Authentication(_) => StatusCode::UNAUTHORIZED,
80            Error::RateLimit(_) => StatusCode::TOO_MANY_REQUESTS,
81            Error::Node(_) | Error::Unknown(_) | Error::Io(_) => StatusCode::INTERNAL_SERVER_ERROR,
82            Error::Config(_) => StatusCode::SERVICE_UNAVAILABLE,
83            Error::Http(_) => StatusCode::BAD_GATEWAY,
84            Error::Tls(_) => StatusCode::INTERNAL_SERVER_ERROR,
85        }
86    }
87
88    /// Returns the severity level of this error.
89    pub fn severity(&self) -> ErrorSeverity {
90        match self {
91            Error::DIDComm(_) | Error::Validation(_) | Error::Json(_) => ErrorSeverity::Info,
92            Error::RateLimit(_) | Error::Authentication(_) => ErrorSeverity::Warning,
93            Error::Node(_)
94            | Error::Http(_)
95            | Error::Config(_)
96            | Error::Tls(_)
97            | Error::Unknown(_)
98            | Error::Io(_) => ErrorSeverity::Critical,
99        }
100    }
101
102    /// Creates an error response for this error.
103    pub fn to_response(&self) -> warp::reply::Response {
104        let status = self.status_code();
105        let message = self.to_string();
106        let error_type = match self {
107            Error::DIDComm(_) => "didcomm_error",
108            Error::Validation(_) => "validation_error",
109            Error::Authentication(_) => "authentication_error",
110            Error::Json(_) => "json_error",
111            Error::Http(_) => "http_error",
112            Error::Node(_) => "node_error",
113            Error::Config(_) => "configuration_error",
114            Error::Io(_) => "io_error",
115            Error::RateLimit(_) => "rate_limit_error",
116            Error::Tls(_) => "tls_error",
117            Error::Unknown(_) => "unknown_error",
118        };
119
120        warp::reply::with_status(
121            warp::reply::json(&serde_json::json!({
122                "status": "error",
123                "error": {
124                    "type": error_type,
125                    "message": message,
126                }
127            })),
128            status,
129        )
130        .into_response()
131    }
132}
133
134impl From<serde_json::Error> for Error {
135    fn from(err: serde_json::Error) -> Self {
136        Error::Json(err.to_string())
137    }
138}
139
140impl From<tap_msg::error::Error> for Error {
141    fn from(err: tap_msg::error::Error) -> Self {
142        Error::DIDComm(err.to_string())
143    }
144}
145
146impl From<tap_node::error::Error> for Error {
147    fn from(err: tap_node::error::Error) -> Self {
148        Error::Node(err.to_string())
149    }
150}
151
152impl warp::reject::Reject for Error {}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157
158    #[test]
159    fn test_error_display() {
160        let error = Error::DIDComm("test error".to_string());
161        assert_eq!(error.to_string(), "Invalid DIDComm message: test error");
162
163        let error = Error::Node("node error".to_string());
164        assert_eq!(error.to_string(), "TAP Node error: node error");
165
166        let error = Error::Http("server error".to_string());
167        assert_eq!(error.to_string(), "HTTP error: server error");
168
169        let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
170        let error = Error::from(io_error);
171        assert!(error.to_string().contains("file not found"));
172    }
173
174    #[test]
175    fn test_error_conversions() {
176        let json_error = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
177        let error = Error::from(json_error);
178        assert!(matches!(error, Error::Json(_)));
179    }
180}