1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
pub mod sink;
pub mod source;
pub mod transform;
use std::error::Error as StdError;
use std::fmt::Write as _;
use self::transform::Error as TransformError;
#[allow(missing_docs)] #[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Can't fetch data")]
Source(#[from] source::Error),
#[error("Can't transform data")]
Transform(#[from] TransformError),
#[error("Can't send data")]
Sink(#[from] sink::Error),
#[error("Google authentication error")]
GoogleOAuth2(#[from] GoogleOAuth2Error),
#[error("Error writing to the external read filter")]
ReadFilterExternalWrite(#[source] std::io::Error),
}
#[allow(missing_docs)] #[derive(thiserror::Error, Debug)]
pub enum GoogleOAuth2Error {
#[error("Error contacting Google servers for authentication")]
Post(#[source] reqwest::Error),
#[error("{0}")]
Auth(String),
}
impl Error {
#[allow(clippy::match_same_arms)]
#[must_use]
pub fn is_connection_error(&self) -> Option<&(dyn StdError + Send + Sync)> {
match self {
Error::Source(source_err) => match source_err {
source::Error::EmptySourceList => None,
source::Error::SourceListHasDifferentVariants => None,
source::Error::FileRead(_, _) => None,
source::Error::Http(http_err) => Some(http_err),
#[allow(clippy::match_wildcard_for_single_variants)]
source::Error::Email(email_err) => match &**email_err {
source::EmailError::Imap(imap_err) => Some(imap_err),
_ => None,
},
source::Error::Twitter(twitter_err) => match twitter_err {
source::TwitterError::Auth(auth_err) => match auth_err {
egg_mode::error::Error::NetError(net_err) => Some(net_err),
_ => None,
},
source::TwitterError::Other(other_err) => match other_err {
egg_mode::error::Error::NetError(net_err) => Some(net_err),
_ => None,
},
},
},
Error::Transform(tr_err) => match &tr_err.kind {
transform::Kind::Http(transform::HttpError::Other(http_err)) => Some(http_err),
_ => None,
},
Error::Sink(sink_err) => match sink_err {
sink::Error::StdoutWrite(_) => None,
sink::Error::Telegram {
source: teloxide::RequestError::Network(teloxide_network_err),
..
} => Some(teloxide_network_err),
sink::Error::Telegram { .. } => None,
},
Error::GoogleOAuth2(google_oauth2_err) => match google_oauth2_err {
GoogleOAuth2Error::Post(post_err) => Some(post_err),
GoogleOAuth2Error::Auth(_) => None,
},
Error::ReadFilterExternalWrite(_) => None,
}
}
}
pub trait ErrorChainExt {
fn display_chain(&self) -> String;
}
impl<T: StdError> ErrorChainExt for T {
#[must_use]
fn display_chain(&self) -> String {
let mut current_err: &dyn StdError = self;
let mut counter = 0;
let mut output = format!("{current_err}");
while let Some(source) = StdError::source(current_err) {
current_err = source;
counter += 1;
if counter == 1 {
let _ = write!(output, "\n\nCaused by:");
}
let _ = write!(output, "\n\t{counter}: {current_err}");
}
output
}
}