traq_bot_http/
error.rs

1//! エラー型の定義
2
3use std::fmt;
4
5use crate::macros::error_with_source;
6
7/// リクエスト処理時に発生しうるエラー型です。発生したエラーの種類は[`Error::kind`]を参照してください。
8///
9/// ## Example
10/// ```
11/// use traq_bot_http::RequestParser;
12/// use http::HeaderMap;
13///
14/// let verification_token = "verification_token";
15/// let parser = RequestParser::new(verification_token);
16/// let headers = HeaderMap::new();
17/// let body = b"";
18/// let parsed = parser.parse(&headers, body);
19/// assert!(parsed.is_err());
20/// let error = parsed.unwrap_err();
21/// println!("{error}");
22/// ```
23///
24/// [`Error::kind`]: crate::error::Error::kind
25#[must_use]
26#[derive(Debug)]
27pub struct Error {
28    kind: ErrorKind,
29    source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
30}
31
32/// 発生したエラーの種類です。 ([non-exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html))
33#[allow(clippy::module_name_repetitions)]
34#[must_use]
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
36#[non_exhaustive]
37pub enum ErrorKind {
38    /// Content-Typeがヘッダーに含まれていない
39    ContentTypeNotFound,
40    /// Content-Typeの値を読み取れなかった
41    ReadContentTypeFailed,
42    /// Content-Typeの値がapplication/jsonで始まっていない
43    ContentTypeMismatch,
44    /// X-TRAQ-BOT-TOKENがヘッダーに含まれていない
45    BotTokenNotFound,
46    /// X-TRAQ-BOT-TOKENの値を読み取れなかった
47    ReadBotTokenFailed,
48    /// X-TRAQ-BOT-TOKENの値がverification_tokenと等しくない
49    BotTokenMismatch,
50    /// X-TRAQ-BOT-EVENTがヘッダーに含まれていない
51    BotEventNotFound,
52    /// X-TRAQ-BOT-EVENTの値を読み取れなかった
53    ReadBotEventFailed,
54    /// X-TRAQ-BOT-EVENTの値がイベント名のいずれでもない
55    BotEventMismatch,
56    /// リクエストボディの値を読み取れなかった
57    ReadBodyFailed,
58    /// リクエストボディの値をパースできなかった
59    ParseBodyFailed,
60    /// イベントハンドラ実行中のエラー
61    Handler,
62}
63
64/// type alias
65pub type Result<T, E = Error> = std::result::Result<T, E>;
66
67impl Error {
68    /// 対応する[`ErrorKind`]を返します。
69    ///
70    /// ## Example
71    /// ```
72    /// use traq_bot_http::{Error, ErrorKind};
73    ///
74    /// let error_kind = ErrorKind::ContentTypeNotFound;
75    /// let error = Error::from(error_kind);
76    /// assert_eq!(error.kind(), error_kind);
77    /// ```
78    pub fn kind(&self) -> ErrorKind {
79        self.kind
80    }
81}
82
83impl Error {
84    pub(crate) fn new<E>(kind: ErrorKind, source: E) -> Self
85    where
86        E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
87    {
88        Self {
89            kind,
90            source: Some(source.into()),
91        }
92    }
93
94    error_with_source! {#[allow(dead_code)] pub(crate) ContentTypeNotFound}
95    error_with_source! {pub(crate) ReadContentTypeFailed}
96    error_with_source! {#[allow(dead_code)] pub(crate) ContentTypeMismatch}
97    error_with_source! {#[allow(dead_code)] pub(crate) BotTokenNotFound}
98    error_with_source! {pub(crate) ReadBotTokenFailed}
99    error_with_source! {#[allow(dead_code)] pub(crate) BotTokenMismatch}
100    error_with_source! {#[allow(dead_code)] pub(crate) BotEventNotFound}
101    error_with_source! {pub(crate) ReadBotEventFailed}
102    error_with_source! {pub(crate) BotEventMismatch}
103    error_with_source! {pub(crate) ReadBodyFailed}
104    error_with_source! {pub(crate) ParseBodyFailed}
105    // cfg(not(feature = "tower")) でdead_codeになる
106    error_with_source! {#[allow(dead_code)] pub(crate) Handler}
107}
108
109impl From<ErrorKind> for Error {
110    fn from(kind: ErrorKind) -> Self {
111        Self { kind, source: None }
112    }
113}
114
115impl fmt::Display for Error {
116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117        f.write_str(self.kind.as_str())
118    }
119}
120
121impl std::error::Error for Error {
122    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
123        let s = self.source.as_deref()?;
124        Some(s as &(dyn std::error::Error + 'static))
125    }
126}
127
128impl ErrorKind {
129    pub(crate) fn as_str(self) -> &'static str {
130        match self {
131            Self::ContentTypeNotFound => "Content-Type is not set",
132            Self::ReadContentTypeFailed => "Failed to read Content-Type value",
133            Self::ContentTypeMismatch => "Content-Type value is wrong; it must be application/json",
134            Self::BotTokenNotFound => "X-TRAQ-BOT-TOKEN is not set",
135            Self::ReadBotTokenFailed => "Failed to read X-TRAQ-BOT-TOKEN value",
136            Self::BotTokenMismatch => "X-TRAQ-BOT-TOKEN value is wrong",
137            Self::BotEventNotFound => "X-TRAQ-BOT-EVENT is not set",
138            Self::ReadBotEventFailed => "Failed to read X-TRAQ-BOT-EVENT value",
139            Self::BotEventMismatch => "X-TRAQ-BOT-EVENT value is wrong",
140            Self::ReadBodyFailed => "Failed to read request body",
141            Self::ParseBodyFailed => "Failed to parse request body",
142            Self::Handler => "Event handler raised an error",
143        }
144    }
145}
146
147impl fmt::Display for ErrorKind {
148    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149        f.write_str(self.as_str())
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156    use crate::macros::all_error_kinds;
157
158    fn assert_send_sync_static<T: Send + Sync + 'static>() {}
159    fn assert_display<T: std::fmt::Display>() {}
160    fn assert_error<T: std::error::Error>() {}
161
162    /// `A into B`
163    fn assert_convert<A, B>()
164    where
165        A: Into<B>,
166    {
167    }
168
169    #[test]
170    fn error_impl() {
171        assert_send_sync_static::<Error>();
172        assert_error::<Error>();
173        assert_convert::<ErrorKind, Error>();
174    }
175
176    #[test]
177    fn error_kind_impl() {
178        assert_send_sync_static::<ErrorKind>();
179        assert_display::<ErrorKind>();
180    }
181
182    macro_rules! tests_error_kind_convert {
183        ($( $kind:ident ),*) => {
184            $(
185                $crate::macros::test_error_kind_convert! {$kind}
186            )*
187        };
188    }
189
190    all_error_kinds! {tests_error_kind_convert}
191}