]> Untitled Git - lemmy.git/blob - crates/utils/src/lib.rs
Show deny reason to users after a failed login. Fixes #2191 (#2206)
[lemmy.git] / crates / utils / src / lib.rs
1 #[macro_use]
2 extern crate strum_macros;
3 #[macro_use]
4 extern crate smart_default;
5
6 pub mod apub;
7 pub mod email;
8 pub mod rate_limit;
9 pub mod request;
10 pub mod settings;
11
12 pub mod claims;
13 #[cfg(test)]
14 mod test;
15 pub mod utils;
16 pub mod version;
17
18 mod sensitive;
19
20 pub use sensitive::Sensitive;
21
22 use actix_web::HttpResponse;
23 use http::StatusCode;
24 use std::{fmt, fmt::Display, time::Duration};
25 use tracing_error::SpanTrace;
26
27 pub type ConnectionId = usize;
28
29 pub const REQWEST_TIMEOUT: Duration = Duration::from_secs(10);
30
31 #[derive(PartialEq, Eq, Hash, Debug, Clone)]
32 pub struct IpAddr(pub String);
33
34 impl fmt::Display for IpAddr {
35   fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36     write!(f, "{}", self.0)
37   }
38 }
39
40 #[macro_export]
41 macro_rules! location_info {
42   () => {
43     format!(
44       "None value at {}:{}, column {}",
45       file!(),
46       line!(),
47       column!()
48     )
49   };
50 }
51
52 #[derive(serde::Serialize)]
53 struct ApiError {
54   error: String,
55 }
56
57 pub struct LemmyError {
58   pub message: Option<String>,
59   pub inner: anyhow::Error,
60   pub context: SpanTrace,
61 }
62
63 impl LemmyError {
64   /// Create LemmyError from a message, including stack trace
65   pub fn from_message(message: &str) -> Self {
66     let inner = anyhow::anyhow!("{}", message);
67     LemmyError {
68       message: Some(message.into()),
69       inner,
70       context: SpanTrace::capture(),
71     }
72   }
73
74   /// Create a LemmyError from error and message, including stack trace
75   pub fn from_error_message<E>(error: E, message: &str) -> Self
76   where
77     E: Into<anyhow::Error>,
78   {
79     LemmyError {
80       message: Some(message.into()),
81       inner: error.into(),
82       context: SpanTrace::capture(),
83     }
84   }
85
86   /// Add message to existing LemmyError (or overwrite existing error)
87   pub fn with_message(self, message: &str) -> Self {
88     LemmyError {
89       message: Some(message.into()),
90       ..self
91     }
92   }
93
94   pub fn to_json(&self) -> Result<String, Self> {
95     let api_error = match &self.message {
96       Some(error) => ApiError {
97         error: error.into(),
98       },
99       None => ApiError {
100         error: "Unknown".into(),
101       },
102     };
103
104     Ok(serde_json::to_string(&api_error)?)
105   }
106 }
107
108 impl<T> From<T> for LemmyError
109 where
110   T: Into<anyhow::Error>,
111 {
112   fn from(t: T) -> Self {
113     LemmyError {
114       message: None,
115       inner: t.into(),
116       context: SpanTrace::capture(),
117     }
118   }
119 }
120
121 impl std::fmt::Debug for LemmyError {
122   fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123     f.debug_struct("LemmyError")
124       .field("message", &self.message)
125       .field("inner", &self.inner)
126       .field("context", &"SpanTrace")
127       .finish()
128   }
129 }
130
131 impl Display for LemmyError {
132   fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
133     if let Some(message) = &self.message {
134       write!(f, "{}: ", message)?;
135     }
136     writeln!(f, "{}", self.inner)?;
137     self.context.fmt(f)
138   }
139 }
140
141 impl actix_web::error::ResponseError for LemmyError {
142   fn status_code(&self) -> StatusCode {
143     match self.inner.downcast_ref::<diesel::result::Error>() {
144       Some(diesel::result::Error::NotFound) => StatusCode::NOT_FOUND,
145       _ => StatusCode::BAD_REQUEST,
146     }
147   }
148
149   fn error_response(&self) -> HttpResponse {
150     if let Some(message) = &self.message {
151       HttpResponse::build(self.status_code()).json(ApiError {
152         error: message.into(),
153       })
154     } else {
155       HttpResponse::build(self.status_code())
156         .content_type("text/plain")
157         .body(self.inner.to_string())
158     }
159   }
160 }