melib/error: add ErrorKind struct

master
Manos Pitsidianakis 2020-07-13 18:53:34 +03:00
parent c7bbf7ed7e
commit 97c76cc6a1
Signed by: Manos Pitsidianakis
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)]
pub struct ImapConnection {
pub stream: Result<ImapStream>,
@ -91,7 +95,9 @@ impl ImapStream {
if server_conf.danger_accept_invalid_certs {
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) {
a
@ -102,21 +108,27 @@ impl ImapStream {
)));
};
let mut socket = AsyncWrapper::new(Connection::Tcp(TcpStream::connect_timeout(
&addr,
std::time::Duration::new(4, 0),
)?))?;
let mut socket = AsyncWrapper::new(Connection::Tcp(
TcpStream::connect_timeout(&addr, 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 {
let mut buf = vec![0; 1024];
match server_conf.protocol {
ImapProtocol::IMAP => {
socket
ImapProtocol::IMAP => socket
.write_all(format!("M{} STARTTLS\r\n", cmd_id).as_bytes())
.await?
}
.await
.chain_err_kind(crate::error::ErrorKind::Network)?,
ImapProtocol::ManageSieve => {
socket.read(&mut buf).await?;
socket.write_all(b"STARTTLS\r\n").await?;
socket
.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);
@ -124,7 +136,10 @@ impl ImapStream {
let now = std::time::Instant::now();
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]) });
match server_conf.protocol {
ImapProtocol::IMAP => {
@ -157,7 +172,9 @@ impl ImapStream {
{
// 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);
if let Err(native_tls::HandshakeError::WouldBlock(midhandshake_stream)) =
conn_result
@ -173,12 +190,15 @@ impl ImapStream {
midhandshake_stream = Some(stream);
}
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 {
let addr = if let Ok(a) = lookup_ipv4(path, server_conf.server_port) {
@ -189,10 +209,11 @@ impl ImapStream {
&path
)));
};
AsyncWrapper::new(Connection::Tcp(TcpStream::connect_timeout(
&addr,
std::time::Duration::new(4, 0),
)?))?
AsyncWrapper::new(Connection::Tcp(
TcpStream::connect_timeout(&addr, 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 ret = ImapStream {
@ -256,7 +277,8 @@ impl ImapStream {
return Err(MeliError::new(format!(
"Could not connect to {}: server does not accept logins [LOGINDISABLED]",
&server_conf.server_hostname
)));
))
.set_err_kind(crate::error::ErrorKind::Authentication));
}
let mut capabilities = None;
@ -287,7 +309,8 @@ impl ImapStream {
return Err(MeliError::new(format!(
"Could not connect. Server replied with '{}'",
l[tag_start.len()..].trim()
)));
))
.set_err_kind(crate::error::ErrorKind::Authentication));
}
should_break = true;
}
@ -365,7 +388,7 @@ impl ImapStream {
continue;
}
Err(e) => {
return Err(MeliError::from(e));
return Err(MeliError::from(e).set_err_kind(crate::error::ErrorKind::Network));
}
}
}
@ -381,6 +404,7 @@ impl ImapStream {
}
pub async fn send_command(&mut self, command: &[u8]) -> Result<()> {
if let Err(err) = try_await(async move {
let command = command.trim();
match self.protocol {
ImapProtocol::IMAP => {
@ -405,18 +429,41 @@ impl ImapStream {
ImapProtocol::ManageSieve => {}
}
Ok(())
})
.await
{
Err(err.set_err_kind(crate::error::ErrorKind::Network))
} else {
Ok(())
}
}
pub async fn send_literal(&mut self, data: &[u8]) -> Result<()> {
if let Err(err) = try_await(async move {
self.stream.write_all(data).await?;
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<()> {
if let Err(err) = try_await(async move {
self.stream.write_all(raw).await?;
self.stream.write_all(b"\r\n").await?;
Ok(())
})
.await
{
Err(err.set_err_kind(crate::error::ErrorKind::Network))
} else {
Ok(())
}
}
}
@ -525,19 +572,40 @@ impl ImapConnection {
}
pub async fn send_command(&mut self, command: &[u8]) -> Result<()> {
self.stream.as_mut()?.send_command(command).await?;
if let Err(err) =
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<()> {
self.stream.as_mut()?.send_literal(data).await?;
if let Err(err) = try_await(async { self.stream.as_mut()?.send_literal(data).await }).await
{
if err.kind.is_network() {
self.connect().await?;
}
Err(err)
} else {
Ok(())
}
}
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 {
if err.kind.is_network() {
self.connect().await?;
}
Err(err)
} else {
Ok(())
}
}
pub async fn select_mailbox(
&mut self,

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>;
#[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)]
pub struct MeliError {
pub summary: Option<Cow<'static, str>>,
pub details: Cow<'static, str>,
pub source: Option<std::sync::Arc<dyn Error + Send + Sync + 'static>>,
pub kind: ErrorKind,
}
pub trait IntoMeliError {
fn set_err_summary<M>(self, msg: M) -> MeliError
where
M: Into<Cow<'static, str>>;
fn set_err_kind(self, kind: ErrorKind) -> MeliError;
}
pub trait ResultIntoMeliError<T> {
@ -52,6 +77,8 @@ pub trait ResultIntoMeliError<T> {
where
F: Fn() -> M,
M: Into<Cow<'static, str>>;
fn chain_err_kind(self, kind: ErrorKind) -> Result<T>;
}
impl<I: Into<MeliError>> IntoMeliError for I {
@ -63,6 +90,12 @@ impl<I: Into<MeliError>> IntoMeliError for I {
let err: MeliError = self.into();
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> {
@ -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()))
}
#[inline]
fn chain_err_kind(self, kind: ErrorKind) -> Result<T> {
self.map_err(|err| err.set_err_kind(kind))
}
}
impl MeliError {
@ -85,6 +123,7 @@ impl MeliError {
summary: None,
details: msg.into(),
source: None,
kind: ErrorKind::None,
}
}
@ -107,6 +146,11 @@ impl MeliError {
self.source = new_val;
self
}
pub fn set_kind(mut self, new_val: ErrorKind) -> MeliError {
self.kind = new_val;
self
}
}
impl fmt::Display for MeliError {