autopush_common/
errors.rs1use std::fmt::{self, Display};
4use std::io;
5use std::num;
6
7use actix_web::{
8 dev::ServiceResponse, http::StatusCode, middleware::ErrorHandlerResponse, HttpResponse,
9 HttpResponseBuilder, ResponseError,
10};
11use backtrace::Backtrace;
13use serde::ser::{Serialize, SerializeMap, Serializer};
14use thiserror::Error;
15
16#[cfg(feature = "reliable_report")]
17use redis::RedisError;
18
19pub type Result<T> = std::result::Result<T, ApcError>;
20
21pub fn render_404<B>(
23 res: ServiceResponse<B>,
24) -> std::result::Result<ErrorHandlerResponse<B>, actix_web::Error> {
25 let resp = HttpResponseBuilder::new(StatusCode::NOT_FOUND).finish();
27 Ok(ErrorHandlerResponse::Response(
28 res.into_response(resp).map_into_right_body(),
29 ))
30}
31
32#[derive(Debug)]
34pub struct ApcError {
35 pub kind: ApcErrorKind,
36 pub backtrace: Box<Backtrace>,
37}
38
39impl Display for ApcError {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 self.kind.fmt(f)
42 }
43}
44
45impl std::error::Error for ApcError {
46 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
47 self.kind.source()
48 }
49}
50
51impl<T> From<T> for ApcError
54where
55 ApcErrorKind: From<T>,
56{
57 fn from(item: T) -> Self {
58 ApcError {
59 kind: ApcErrorKind::from(item),
60 backtrace: Box::new(Backtrace::new()), }
62 }
63}
64
65impl ResponseError for ApcError {
67 fn status_code(&self) -> StatusCode {
68 self.kind.status()
69 }
70
71 fn error_response(&self) -> HttpResponse {
72 let mut builder = HttpResponse::build(self.status_code());
73 builder.json(self)
74 }
75}
76
77impl Serialize for ApcError {
78 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
79 where
80 S: Serializer,
81 {
82 let status = self.kind.status();
83 let mut map = serializer.serialize_map(Some(5))?;
84
85 map.serialize_entry("code", &status.as_u16())?;
86 map.serialize_entry("error", &status.canonical_reason())?;
87 map.serialize_entry("message", &self.kind.to_string())?;
88 map.end()
90 }
91}
92
93#[derive(Error, Debug)]
94pub enum ApcErrorKind {
95 #[error(transparent)]
96 Io(#[from] io::Error),
97 #[error(transparent)]
98 UuidError(#[from] uuid::Error),
99 #[error(transparent)]
100 ParseIntError(#[from] num::ParseIntError),
101 #[error(transparent)]
102 ParseUrlError(#[from] url::ParseError),
103 #[error(transparent)]
104 ConfigError(#[from] config::ConfigError),
105 #[cfg(feature = "reliable_report")]
106 #[error(transparent)]
107 RedisError(#[from] RedisError),
108 #[error("Broadcast Error: {0}")]
109 BroadcastError(String),
110 #[error("Payload Error: {0}")]
111 PayloadError(String),
112 #[error("General Error: {0}")]
113 GeneralError(String),
114 #[error("Client not connected")]
115 ClientNotConnected,
116 #[error("Client notification channel full")]
117 ChannelFull,
118}
119
120impl ApcErrorKind {
121 pub fn status(&self) -> StatusCode {
123 match self {
124 Self::ParseIntError(_) | Self::ParseUrlError(_) => StatusCode::BAD_REQUEST,
125 _ => StatusCode::INTERNAL_SERVER_ERROR,
126 }
127 }
128
129 pub fn is_sentry_event(&self) -> bool {
130 match self {
131 Self::PayloadError(_) => false,
134 Self::ChannelFull => false,
136 _ => true,
137 }
138 }
139
140 pub fn metric_label(&self) -> Option<&'static str> {
141 match self {
143 Self::PayloadError(_) => Some("payload"),
144 Self::ChannelFull => Some("client.channel.full"),
145 _ => None,
146 }
147 }
148}
149
150pub trait ReportableError: std::error::Error {
152 fn reportable_source(&self) -> Option<&(dyn ReportableError + 'static)> {
157 None
158 }
159
160 fn backtrace(&self) -> Option<&Backtrace> {
162 None
163 }
164 fn is_sentry_event(&self) -> bool {
166 true
167 }
168
169 fn metric_label(&self) -> Option<&'static str> {
172 None
173 }
174
175 fn tags(&self) -> Vec<(&str, String)> {
177 vec![]
178 }
179
180 fn extras(&self) -> Vec<(&str, String)> {
183 vec![]
184 }
185}
186
187impl ReportableError for ApcError {
188 fn backtrace(&self) -> Option<&Backtrace> {
189 Some(&self.backtrace)
190 }
191
192 fn is_sentry_event(&self) -> bool {
193 self.kind.is_sentry_event()
194 }
195
196 fn metric_label(&self) -> Option<&'static str> {
197 self.kind.metric_label()
198 }
199}