melib/error: add ErrorKind struct

This commit is contained in:
Manos Pitsidianakis 2020-07-13 18:53:34 +03:00
parent c7bbf7ed7e
commit 97c76cc6a1
No known key found for this signature in database
GPG Key ID: 73627C2F690DF710
3 changed files with 169 additions and 56 deletions

View File

@ -64,6 +64,10 @@ impl MailboxSelection {
} }
} }
async fn try_await(cl: impl Future<Output = Result<()>> + Send) -> Result<()> {
cl.await
}
#[derive(Debug)] #[derive(Debug)]
pub struct ImapConnection { pub struct ImapConnection {
pub stream: Result<ImapStream>, pub stream: Result<ImapStream>,
@ -91,7 +95,9 @@ impl ImapStream {
if server_conf.danger_accept_invalid_certs { if server_conf.danger_accept_invalid_certs {
connector.danger_accept_invalid_certs(true); connector.danger_accept_invalid_certs(true);
} }
let connector = connector.build()?; let connector = connector
.build()
.chain_err_kind(crate::error::ErrorKind::Network)?;
let addr = if let Ok(a) = lookup_ipv4(path, server_conf.server_port) { let addr = if let Ok(a) = lookup_ipv4(path, server_conf.server_port) {
a a
@ -102,21 +108,27 @@ impl ImapStream {
))); )));
}; };
let mut socket = AsyncWrapper::new(Connection::Tcp(TcpStream::connect_timeout( let mut socket = AsyncWrapper::new(Connection::Tcp(
&addr, TcpStream::connect_timeout(&addr, std::time::Duration::new(4, 0))
std::time::Duration::new(4, 0), .chain_err_kind(crate::error::ErrorKind::Network)?,
)?))?; ))
.chain_err_kind(crate::error::ErrorKind::Network)?;
if server_conf.use_starttls { if server_conf.use_starttls {
let mut buf = vec![0; 1024]; let mut buf = vec![0; 1024];
match server_conf.protocol { match server_conf.protocol {
ImapProtocol::IMAP => { ImapProtocol::IMAP => socket
socket .write_all(format!("M{} STARTTLS\r\n", cmd_id).as_bytes())
.write_all(format!("M{} STARTTLS\r\n", cmd_id).as_bytes()) .await
.await? .chain_err_kind(crate::error::ErrorKind::Network)?,
}
ImapProtocol::ManageSieve => { ImapProtocol::ManageSieve => {
socket.read(&mut buf).await?; socket
socket.write_all(b"STARTTLS\r\n").await?; .read(&mut buf)
.await
.chain_err_kind(crate::error::ErrorKind::Network)?;
socket
.write_all(b"STARTTLS\r\n")
.await
.chain_err_kind(crate::error::ErrorKind::Network)?;
} }
} }
let mut response = String::with_capacity(1024); let mut response = String::with_capacity(1024);
@ -124,7 +136,10 @@ impl ImapStream {
let now = std::time::Instant::now(); let now = std::time::Instant::now();
while now.elapsed().as_secs() < 3 { while now.elapsed().as_secs() < 3 {
let len = socket.read(&mut buf).await?; let len = socket
.read(&mut buf)
.await
.chain_err_kind(crate::error::ErrorKind::Network)?;
response.push_str(unsafe { std::str::from_utf8_unchecked(&buf[0..len]) }); response.push_str(unsafe { std::str::from_utf8_unchecked(&buf[0..len]) });
match server_conf.protocol { match server_conf.protocol {
ImapProtocol::IMAP => { ImapProtocol::IMAP => {
@ -157,7 +172,9 @@ impl ImapStream {
{ {
// FIXME: This is blocking // FIXME: This is blocking
let socket = socket.into_inner()?; let socket = socket
.into_inner()
.chain_err_kind(crate::error::ErrorKind::Network)?;
let mut conn_result = connector.connect(path, socket); let mut conn_result = connector.connect(path, socket);
if let Err(native_tls::HandshakeError::WouldBlock(midhandshake_stream)) = if let Err(native_tls::HandshakeError::WouldBlock(midhandshake_stream)) =
conn_result conn_result
@ -173,12 +190,15 @@ impl ImapStream {
midhandshake_stream = Some(stream); midhandshake_stream = Some(stream);
} }
p => { p => {
p?; p.chain_err_kind(crate::error::ErrorKind::Network)?;
} }
} }
} }
} }
AsyncWrapper::new(Connection::Tls(conn_result?))? AsyncWrapper::new(Connection::Tls(
conn_result.chain_err_kind(crate::error::ErrorKind::Network)?,
))
.chain_err_kind(crate::error::ErrorKind::Network)?
} }
} else { } else {
let addr = if let Ok(a) = lookup_ipv4(path, server_conf.server_port) { let addr = if let Ok(a) = lookup_ipv4(path, server_conf.server_port) {
@ -189,10 +209,11 @@ impl ImapStream {
&path &path
))); )));
}; };
AsyncWrapper::new(Connection::Tcp(TcpStream::connect_timeout( AsyncWrapper::new(Connection::Tcp(
&addr, TcpStream::connect_timeout(&addr, std::time::Duration::new(4, 0))
std::time::Duration::new(4, 0), .chain_err_kind(crate::error::ErrorKind::Network)?,
)?))? ))
.chain_err_kind(crate::error::ErrorKind::Network)?
}; };
let mut res = String::with_capacity(8 * 1024); let mut res = String::with_capacity(8 * 1024);
let mut ret = ImapStream { let mut ret = ImapStream {
@ -256,7 +277,8 @@ impl ImapStream {
return Err(MeliError::new(format!( return Err(MeliError::new(format!(
"Could not connect to {}: server does not accept logins [LOGINDISABLED]", "Could not connect to {}: server does not accept logins [LOGINDISABLED]",
&server_conf.server_hostname &server_conf.server_hostname
))); ))
.set_err_kind(crate::error::ErrorKind::Authentication));
} }
let mut capabilities = None; let mut capabilities = None;
@ -287,7 +309,8 @@ impl ImapStream {
return Err(MeliError::new(format!( return Err(MeliError::new(format!(
"Could not connect. Server replied with '{}'", "Could not connect. Server replied with '{}'",
l[tag_start.len()..].trim() l[tag_start.len()..].trim()
))); ))
.set_err_kind(crate::error::ErrorKind::Authentication));
} }
should_break = true; should_break = true;
} }
@ -365,7 +388,7 @@ impl ImapStream {
continue; continue;
} }
Err(e) => { Err(e) => {
return Err(MeliError::from(e)); return Err(MeliError::from(e).set_err_kind(crate::error::ErrorKind::Network));
} }
} }
} }
@ -381,42 +404,66 @@ impl ImapStream {
} }
pub async fn send_command(&mut self, command: &[u8]) -> Result<()> { pub async fn send_command(&mut self, command: &[u8]) -> Result<()> {
let command = command.trim(); if let Err(err) = try_await(async move {
match self.protocol { let command = command.trim();
ImapProtocol::IMAP => { match self.protocol {
self.stream.write_all(b"M").await?; ImapProtocol::IMAP => {
self.stream self.stream.write_all(b"M").await?;
.write_all(self.cmd_id.to_string().as_bytes()) self.stream
.await?; .write_all(self.cmd_id.to_string().as_bytes())
self.stream.write_all(b" ").await?; .await?;
self.cmd_id += 1; self.stream.write_all(b" ").await?;
self.cmd_id += 1;
}
ImapProtocol::ManageSieve => {}
} }
ImapProtocol::ManageSieve => {}
}
self.stream.write_all(command).await?; self.stream.write_all(command).await?;
self.stream.write_all(b"\r\n").await?; self.stream.write_all(b"\r\n").await?;
match self.protocol { match self.protocol {
ImapProtocol::IMAP => { ImapProtocol::IMAP => {
debug!("sent: M{} {}", self.cmd_id - 1, unsafe { debug!("sent: M{} {}", self.cmd_id - 1, unsafe {
std::str::from_utf8_unchecked(command) std::str::from_utf8_unchecked(command)
}); });
}
ImapProtocol::ManageSieve => {}
} }
ImapProtocol::ManageSieve => {} Ok(())
})
.await
{
Err(err.set_err_kind(crate::error::ErrorKind::Network))
} else {
Ok(())
} }
Ok(())
} }
pub async fn send_literal(&mut self, data: &[u8]) -> Result<()> { pub async fn send_literal(&mut self, data: &[u8]) -> Result<()> {
self.stream.write_all(data).await?; if let Err(err) = try_await(async move {
self.stream.write_all(b"\r\n").await?; self.stream.write_all(data).await?;
Ok(()) self.stream.write_all(b"\r\n").await?;
Ok(())
})
.await
{
Err(err.set_err_kind(crate::error::ErrorKind::Network))
} else {
Ok(())
}
} }
pub async fn send_raw(&mut self, raw: &[u8]) -> Result<()> { pub async fn send_raw(&mut self, raw: &[u8]) -> Result<()> {
self.stream.write_all(raw).await?; if let Err(err) = try_await(async move {
self.stream.write_all(b"\r\n").await?; self.stream.write_all(raw).await?;
Ok(()) self.stream.write_all(b"\r\n").await?;
Ok(())
})
.await
{
Err(err.set_err_kind(crate::error::ErrorKind::Network))
} else {
Ok(())
}
} }
} }
@ -525,18 +572,39 @@ impl ImapConnection {
} }
pub async fn send_command(&mut self, command: &[u8]) -> Result<()> { pub async fn send_command(&mut self, command: &[u8]) -> Result<()> {
self.stream.as_mut()?.send_command(command).await?; if let Err(err) =
Ok(()) try_await(async { self.stream.as_mut()?.send_command(command).await }).await
{
if err.kind.is_network() {
self.connect().await?;
}
Err(err)
} else {
Ok(())
}
} }
pub async fn send_literal(&mut self, data: &[u8]) -> Result<()> { pub async fn send_literal(&mut self, data: &[u8]) -> Result<()> {
self.stream.as_mut()?.send_literal(data).await?; if let Err(err) = try_await(async { self.stream.as_mut()?.send_literal(data).await }).await
Ok(()) {
if err.kind.is_network() {
self.connect().await?;
}
Err(err)
} else {
Ok(())
}
} }
pub async fn send_raw(&mut self, raw: &[u8]) -> Result<()> { pub async fn send_raw(&mut self, raw: &[u8]) -> Result<()> {
self.stream.as_mut()?.send_raw(raw).await?; if let Err(err) = try_await(async { self.stream.as_mut()?.send_raw(raw).await }).await {
Ok(()) if err.kind.is_network() {
self.connect().await?;
}
Err(err)
} else {
Ok(())
}
} }
pub async fn select_mailbox( pub async fn select_mailbox(

View File

@ -150,5 +150,6 @@ pub fn lookup_ipv4(host: &str, port: u16) -> crate::Result<std::net::SocketAddr>
} }
} }
Err(crate::error::MeliError::new("Cannot lookup address")) Err(crate::error::MeliError::new("Cannot lookup address")
.set_kind(crate::error::ErrorKind::Network))
} }

View File

@ -34,17 +34,42 @@ use std::sync::Arc;
pub type Result<T> = result::Result<T, MeliError>; pub type Result<T> = result::Result<T, MeliError>;
#[derive(Debug, Copy, PartialEq, Clone)]
pub enum ErrorKind {
None,
Authentication,
Network,
}
impl ErrorKind {
pub fn is_network(&self) -> bool {
match self {
ErrorKind::Network => true,
_ => false,
}
}
pub fn is_authentication(&self) -> bool {
match self {
ErrorKind::Authentication => true,
_ => false,
}
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct MeliError { pub struct MeliError {
pub summary: Option<Cow<'static, str>>, pub summary: Option<Cow<'static, str>>,
pub details: Cow<'static, str>, pub details: Cow<'static, str>,
pub source: Option<std::sync::Arc<dyn Error + Send + Sync + 'static>>, pub source: Option<std::sync::Arc<dyn Error + Send + Sync + 'static>>,
pub kind: ErrorKind,
} }
pub trait IntoMeliError { pub trait IntoMeliError {
fn set_err_summary<M>(self, msg: M) -> MeliError fn set_err_summary<M>(self, msg: M) -> MeliError
where where
M: Into<Cow<'static, str>>; M: Into<Cow<'static, str>>;
fn set_err_kind(self, kind: ErrorKind) -> MeliError;
} }
pub trait ResultIntoMeliError<T> { pub trait ResultIntoMeliError<T> {
@ -52,6 +77,8 @@ pub trait ResultIntoMeliError<T> {
where where
F: Fn() -> M, F: Fn() -> M,
M: Into<Cow<'static, str>>; M: Into<Cow<'static, str>>;
fn chain_err_kind(self, kind: ErrorKind) -> Result<T>;
} }
impl<I: Into<MeliError>> IntoMeliError for I { impl<I: Into<MeliError>> IntoMeliError for I {
@ -63,6 +90,12 @@ impl<I: Into<MeliError>> IntoMeliError for I {
let err: MeliError = self.into(); let err: MeliError = self.into();
err.set_summary(msg) err.set_summary(msg)
} }
#[inline]
fn set_err_kind(self, kind: ErrorKind) -> MeliError {
let err: MeliError = self.into();
err.set_kind(kind)
}
} }
impl<T, I: Into<MeliError>> ResultIntoMeliError<T> for std::result::Result<T, I> { impl<T, I: Into<MeliError>> ResultIntoMeliError<T> for std::result::Result<T, I> {
@ -74,6 +107,11 @@ impl<T, I: Into<MeliError>> ResultIntoMeliError<T> for std::result::Result<T, I>
{ {
self.map_err(|err| err.set_err_summary(msg_fn())) self.map_err(|err| err.set_err_summary(msg_fn()))
} }
#[inline]
fn chain_err_kind(self, kind: ErrorKind) -> Result<T> {
self.map_err(|err| err.set_err_kind(kind))
}
} }
impl MeliError { impl MeliError {
@ -85,6 +123,7 @@ impl MeliError {
summary: None, summary: None,
details: msg.into(), details: msg.into(),
source: None, source: None,
kind: ErrorKind::None,
} }
} }
@ -107,6 +146,11 @@ impl MeliError {
self.source = new_val; self.source = new_val;
self self
} }
pub fn set_kind(mut self, new_val: ErrorKind) -> MeliError {
self.kind = new_val;
self
}
} }
impl fmt::Display for MeliError { impl fmt::Display for MeliError {