mirror of
https://git.meli.delivery/meli/meli
synced 2024-11-19 03:25:38 +00:00
melib/error: add ErrorKind struct
This commit is contained in:
parent
c7bbf7ed7e
commit
97c76cc6a1
@ -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(
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user