mirror of https://github.com/chipsenkbeil/distant
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
316 lines
10 KiB
Rust
316 lines
10 KiB
Rust
use std::io;
|
|
|
|
use derive_more::Display;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
/// General purpose error type that can be sent across the wire
|
|
#[derive(Clone, Debug, Display, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[display(fmt = "{kind}: {description}")]
|
|
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
|
pub struct Error {
|
|
/// Label describing the kind of error
|
|
pub kind: ErrorKind,
|
|
|
|
/// Description of the error itself
|
|
pub description: String,
|
|
}
|
|
|
|
impl std::error::Error for Error {}
|
|
|
|
impl Error {
|
|
/// Produces an [`io::Error`] from this error.
|
|
pub fn to_io_error(&self) -> io::Error {
|
|
io::Error::new(self.kind.into(), self.description.to_string())
|
|
}
|
|
}
|
|
|
|
impl<'a> From<&'a str> for Error {
|
|
fn from(x: &'a str) -> Self {
|
|
Self::from(x.to_string())
|
|
}
|
|
}
|
|
|
|
impl From<String> for Error {
|
|
fn from(x: String) -> Self {
|
|
Self {
|
|
kind: ErrorKind::Other,
|
|
description: x,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<io::Error> for Error {
|
|
fn from(x: io::Error) -> Self {
|
|
Self {
|
|
kind: ErrorKind::from(x.kind()),
|
|
description: x.to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Error> for io::Error {
|
|
fn from(x: Error) -> Self {
|
|
Self::new(x.kind.into(), x.description)
|
|
}
|
|
}
|
|
|
|
/// All possible kinds of errors that can be returned
|
|
#[derive(Copy, Clone, Debug, Display, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
|
pub enum ErrorKind {
|
|
/// An entity was not found, often a file
|
|
NotFound,
|
|
|
|
/// The operation lacked the necessary privileges to complete
|
|
PermissionDenied,
|
|
|
|
/// The connection was refused by the remote server
|
|
ConnectionRefused,
|
|
|
|
/// The connection was reset by the remote server
|
|
ConnectionReset,
|
|
|
|
/// The connection was aborted (terminated) by the remote server
|
|
ConnectionAborted,
|
|
|
|
/// The network operation failed because it was not connected yet
|
|
NotConnected,
|
|
|
|
/// A socket address could not be bound because the address is already in use elsewhere
|
|
AddrInUse,
|
|
|
|
/// A nonexistent interface was requested or the requested address was not local
|
|
AddrNotAvailable,
|
|
|
|
/// The operation failed because a pipe was closed
|
|
BrokenPipe,
|
|
|
|
/// An entity already exists, often a file
|
|
AlreadyExists,
|
|
|
|
/// The operation needs to block to complete, but the blocking operation was requested to not
|
|
/// occur
|
|
WouldBlock,
|
|
|
|
/// A parameter was incorrect
|
|
InvalidInput,
|
|
|
|
/// Data not valid for the operation were encountered
|
|
InvalidData,
|
|
|
|
/// The I/O operation's timeout expired, causing it to be cancelled
|
|
TimedOut,
|
|
|
|
/// An error returned when an operation could not be completed because a
|
|
/// call to `write` returned `Ok(0)`
|
|
WriteZero,
|
|
|
|
/// This operation was interrupted
|
|
Interrupted,
|
|
|
|
/// Any I/O error not part of this list
|
|
Other,
|
|
|
|
/// An error returned when an operation could not be completed because an "end of file" was
|
|
/// reached prematurely
|
|
UnexpectedEof,
|
|
|
|
/// This operation is unsupported on this platform
|
|
Unsupported,
|
|
|
|
/// An operation could not be completed, because it failed to allocate enough memory
|
|
OutOfMemory,
|
|
|
|
/// When a loop is encountered when walking a directory
|
|
Loop,
|
|
|
|
/// When a task is cancelled
|
|
TaskCancelled,
|
|
|
|
/// When a task panics
|
|
TaskPanicked,
|
|
|
|
/// Catchall for an error that has no specific type
|
|
Unknown,
|
|
}
|
|
|
|
impl From<io::ErrorKind> for ErrorKind {
|
|
fn from(kind: io::ErrorKind) -> Self {
|
|
match kind {
|
|
io::ErrorKind::NotFound => Self::NotFound,
|
|
io::ErrorKind::PermissionDenied => Self::PermissionDenied,
|
|
io::ErrorKind::ConnectionRefused => Self::ConnectionRefused,
|
|
io::ErrorKind::ConnectionReset => Self::ConnectionReset,
|
|
io::ErrorKind::ConnectionAborted => Self::ConnectionAborted,
|
|
io::ErrorKind::NotConnected => Self::NotConnected,
|
|
io::ErrorKind::AddrInUse => Self::AddrInUse,
|
|
io::ErrorKind::AddrNotAvailable => Self::AddrNotAvailable,
|
|
io::ErrorKind::BrokenPipe => Self::BrokenPipe,
|
|
io::ErrorKind::AlreadyExists => Self::AlreadyExists,
|
|
io::ErrorKind::WouldBlock => Self::WouldBlock,
|
|
io::ErrorKind::InvalidInput => Self::InvalidInput,
|
|
io::ErrorKind::InvalidData => Self::InvalidData,
|
|
io::ErrorKind::TimedOut => Self::TimedOut,
|
|
io::ErrorKind::WriteZero => Self::WriteZero,
|
|
io::ErrorKind::Interrupted => Self::Interrupted,
|
|
io::ErrorKind::Other => Self::Other,
|
|
io::ErrorKind::OutOfMemory => Self::OutOfMemory,
|
|
io::ErrorKind::UnexpectedEof => Self::UnexpectedEof,
|
|
io::ErrorKind::Unsupported => Self::Unsupported,
|
|
|
|
// This exists because io::ErrorKind is non_exhaustive
|
|
_ => Self::Unknown,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<ErrorKind> for io::ErrorKind {
|
|
fn from(kind: ErrorKind) -> Self {
|
|
match kind {
|
|
ErrorKind::NotFound => Self::NotFound,
|
|
ErrorKind::PermissionDenied => Self::PermissionDenied,
|
|
ErrorKind::ConnectionRefused => Self::ConnectionRefused,
|
|
ErrorKind::ConnectionReset => Self::ConnectionReset,
|
|
ErrorKind::ConnectionAborted => Self::ConnectionAborted,
|
|
ErrorKind::NotConnected => Self::NotConnected,
|
|
ErrorKind::AddrInUse => Self::AddrInUse,
|
|
ErrorKind::AddrNotAvailable => Self::AddrNotAvailable,
|
|
ErrorKind::BrokenPipe => Self::BrokenPipe,
|
|
ErrorKind::AlreadyExists => Self::AlreadyExists,
|
|
ErrorKind::WouldBlock => Self::WouldBlock,
|
|
ErrorKind::InvalidInput => Self::InvalidInput,
|
|
ErrorKind::InvalidData => Self::InvalidData,
|
|
ErrorKind::TimedOut => Self::TimedOut,
|
|
ErrorKind::WriteZero => Self::WriteZero,
|
|
ErrorKind::Interrupted => Self::Interrupted,
|
|
ErrorKind::Other => Self::Other,
|
|
ErrorKind::OutOfMemory => Self::OutOfMemory,
|
|
ErrorKind::UnexpectedEof => Self::UnexpectedEof,
|
|
ErrorKind::Unsupported => Self::Unsupported,
|
|
_ => Self::Other,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
mod error {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn should_be_able_to_serialize_to_json() {
|
|
let error = Error {
|
|
kind: ErrorKind::AddrInUse,
|
|
description: "some description".to_string(),
|
|
};
|
|
|
|
let value = serde_json::to_value(error).unwrap();
|
|
assert_eq!(
|
|
value,
|
|
serde_json::json!({
|
|
"kind": "addr_in_use",
|
|
"description": "some description",
|
|
})
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn should_be_able_to_deserialize_from_json() {
|
|
let value = serde_json::json!({
|
|
"kind": "addr_in_use",
|
|
"description": "some description",
|
|
});
|
|
|
|
let error: Error = serde_json::from_value(value).unwrap();
|
|
assert_eq!(
|
|
error,
|
|
Error {
|
|
kind: ErrorKind::AddrInUse,
|
|
description: "some description".to_string(),
|
|
}
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn should_be_able_to_serialize_to_msgpack() {
|
|
let error = Error {
|
|
kind: ErrorKind::AddrInUse,
|
|
description: "some description".to_string(),
|
|
};
|
|
|
|
// NOTE: We don't actually check the output here because it's an implementation detail
|
|
// and could change as we change how serialization is done. This is merely to verify
|
|
// that we can serialize since there are times when serde fails to serialize at
|
|
// runtime.
|
|
let _ = rmp_serde::encode::to_vec_named(&error).unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn should_be_able_to_deserialize_from_msgpack() {
|
|
// NOTE: It may seem odd that we are serializing just to deserialize, but this is to
|
|
// verify that we are not corrupting or preventing issues when serializing on a
|
|
// client/server and then trying to deserialize on the other side. This has happened
|
|
// enough times with minor changes that we need tests to verify.
|
|
let buf = rmp_serde::encode::to_vec_named(&Error {
|
|
kind: ErrorKind::AddrInUse,
|
|
description: "some description".to_string(),
|
|
})
|
|
.unwrap();
|
|
|
|
let error: Error = rmp_serde::decode::from_slice(&buf).unwrap();
|
|
assert_eq!(
|
|
error,
|
|
Error {
|
|
kind: ErrorKind::AddrInUse,
|
|
description: "some description".to_string(),
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
mod error_kind {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn should_be_able_to_serialize_to_json() {
|
|
let kind = ErrorKind::AddrInUse;
|
|
|
|
let value = serde_json::to_value(kind).unwrap();
|
|
assert_eq!(value, serde_json::json!("addr_in_use"));
|
|
}
|
|
|
|
#[test]
|
|
fn should_be_able_to_deserialize_from_json() {
|
|
let value = serde_json::json!("addr_in_use");
|
|
|
|
let kind: ErrorKind = serde_json::from_value(value).unwrap();
|
|
assert_eq!(kind, ErrorKind::AddrInUse);
|
|
}
|
|
|
|
#[test]
|
|
fn should_be_able_to_serialize_to_msgpack() {
|
|
let kind = ErrorKind::AddrInUse;
|
|
|
|
// NOTE: We don't actually check the output here because it's an implementation detail
|
|
// and could change as we change how serialization is done. This is merely to verify
|
|
// that we can serialize since there are times when serde fails to serialize at
|
|
// runtime.
|
|
let _ = rmp_serde::encode::to_vec_named(&kind).unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn should_be_able_to_deserialize_from_msgpack() {
|
|
// NOTE: It may seem odd that we are serializing just to deserialize, but this is to
|
|
// verify that we are not corrupting or causing issues when serializing on a
|
|
// client/server and then trying to deserialize on the other side. This has happened
|
|
// enough times with minor changes that we need tests to verify.
|
|
let buf = rmp_serde::encode::to_vec_named(&ErrorKind::AddrInUse).unwrap();
|
|
|
|
let kind: ErrorKind = rmp_serde::decode::from_slice(&buf).unwrap();
|
|
assert_eq!(kind, ErrorKind::AddrInUse);
|
|
}
|
|
}
|
|
}
|