implement custom error type

This commit is contained in:
glyph 2021-11-25 09:56:21 +02:00
parent f17ae95b21
commit b2f7747357
1 changed files with 191 additions and 105 deletions

View File

@ -1,136 +1,222 @@
//! Basic error handling for the network, OLED, stats and dyndns JSON-RPC clients.
pub use snafu::ResultExt;
use snafu::Snafu;
use std::error;
pub type BoxError = Box<dyn error::Error>;
#![warn(missing_docs)]
#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))]
//! Error handling for various aspects of the PeachCloud system, including the network, OLED, stats and dyndns JSON-RPC clients, as well as the configuration manager, sbot client and password utilities.
use std::{io, str, string};
/// This type represents all possible errors that can occur when interacting with the PeachCloud library.
#[derive(Debug)]
pub enum PeachError {
#[snafu(display("{}", source))]
JsonRpcHttp { source: jsonrpc_client_http::Error },
#[snafu(display("{}", source))]
JsonRpcClientCore { source: jsonrpc_client_core::Error },
#[snafu(display("{}", source))]
Serde { source: serde_json::error::Error },
#[snafu(display("{}", source))]
PeachParseBoolError { source: std::str::ParseBoolError },
#[snafu(display("{}", source))]
SetConfigError { source: serde_yaml::Error },
#[snafu(display("Failed to read: {}", file))]
ReadConfigError {
source: std::io::Error,
file: String,
},
#[snafu(display("Failed to save: {}", file))]
WriteConfigError {
source: std::io::Error,
file: String,
},
#[snafu(display("Failed to save tsig key: {} {}", path, source))]
SaveTsigKeyError {
source: std::io::Error,
path: String,
},
#[snafu(display("{}", msg))]
NsUpdateError { msg: String },
#[snafu(display("Failed to run nsupdate: {}", source))]
NsCommandError { source: std::io::Error },
#[snafu(display("Failed to get public IP address: {}", source))]
GetPublicIpError { source: std::io::Error },
#[snafu(display("Failed to decode public ip: {}", source))]
DecodePublicIpError { source: std::str::Utf8Error },
#[snafu(display("Failed to decode nsupdate output: {}", source))]
DecodeNsUpdateOutputError { source: std::string::FromUtf8Error },
#[snafu(display("{}", source))]
YamlError { source: serde_yaml::Error },
#[snafu(display("{:?}", err))]
JsonRpcCore { err: jsonrpc_core::Error },
#[snafu(display("Error creating regex: {}", source))]
RegexError { source: regex::Error },
#[snafu(display("Failed to decode utf8: {}", source))]
FromUtf8Error { source: std::string::FromUtf8Error },
#[snafu(display("Encountered Utf8Error: {}", source))]
Utf8Error { source: std::str::Utf8Error },
#[snafu(display("Stdio error: {}: {}", msg, source))]
StdIoError { source: std::io::Error, msg: String },
#[snafu(display("Failed to parse time from {} {}", source, msg))]
ChronoParseError {
source: chrono::ParseError,
/// Represents all other cases of `std::io::Error`.
Io(io::Error),
/// Represents a JSON-RPC core error returned from a JSON-RPC client.
JsonRpcClientCore(jsonrpc_client_core::Error),
/// Represents a JSON-RPC core error returned from a JSON-RPC server.
JsonRpcCore(jsonrpc_core::Error),
/// Represents a JSON-RPC HTTP error returned from a JSON-RPC client.
JsonRpcHttp(jsonrpc_client_http::Error),
/// Represents a failure to update the nameserver.
NsUpdate {
/// A message describing the context of the attempted nameserver update.
msg: String,
},
#[snafu(display("Failed to save dynamic dns success log: {}", source))]
SaveDynDnsResultError { source: std::io::Error },
#[snafu(display("New passwords do not match"))]
PasswordsDoNotMatch,
#[snafu(display("No admin password is set"))]
/// Represents a failure to parse a string slice to a boolean value.
ParseBool(str::ParseBoolError),
/// Represents a failure to parse a `DateTime`. Includes the error source and the file path
/// used in the parse attempt.
ParseDateTime {
/// The underlying source of the error.
source: chrono::ParseError,
/// The file path for the parse attempt.
path: String,
},
/// Represents the submission of an incorrect admin password.
PasswordIncorrect,
/// Represents the submission of two passwords which do not match.
PasswordMismatch,
/// Represents an unset admin password (empty password hash value) in the config file.
PasswordNotSet,
#[snafu(display("The supplied password was not correct"))]
InvalidPassword,
#[snafu(display("Error saving new password: {}", msg))]
FailedToSetNewPassword { msg: String },
#[snafu(display("Error calling sbotcli: {}", msg))]
SbotCliError { msg: String },
#[snafu(display("Error deleting ssb admin id, id not found"))]
SsbAdminIdNotFound { id: String },
/// Represents a failure to read from input. Includes the error source and the file path used
/// in the read attempt.
Read {
/// The underlying source of the error.
source: io::Error,
/// The file path for the read attempt.
path: String,
},
/// Represents a failure to parse or compile a regular expression.
Regex(regex::Error),
/// Represents a failure to successfully execute an sbot command.
SbotCli {
/// The `stderr` output from the sbot command.
msg: String,
},
/// Represents a failure to serialize or deserialize JSON.
SerdeJson(serde_json::error::Error),
/// Represents a failure to serialize or deserialize YAML.
SerdeYaml(serde_yaml::Error),
/// Represents a failure to find the given SSB ID in the config file.
SsbAdminIdNotFound {
/// An SSB ID (public key).
id: String,
},
/// Represents a failure to interpret a sequence of u8 as a string slice.
Utf8ToStr(str::Utf8Error),
/// Represents a failure to interpret a sequence of u8 as a String.
Utf8ToString(string::FromUtf8Error),
/// Represents a failure to write to output. Includes the error source and the file path used
/// in the write attempt.
Write {
/// The underlying source of the error.
source: io::Error,
/// The file path for the write attemp.
path: String,
},
}
impl From<jsonrpc_client_http::Error> for PeachError {
fn from(err: jsonrpc_client_http::Error) -> PeachError {
PeachError::JsonRpcHttp { source: err }
impl std::error::Error for PeachError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self {
PeachError::Io(_) => None,
PeachError::JsonRpcClientCore(_) => None,
PeachError::JsonRpcCore(_) => None,
PeachError::JsonRpcHttp(_) => None,
PeachError::NsUpdate { .. } => None,
PeachError::ParseBool(_) => None,
PeachError::ParseDateTime { ref source, .. } => Some(source),
PeachError::PasswordIncorrect => None,
PeachError::PasswordMismatch => None,
PeachError::PasswordNotSet => None,
PeachError::Read { ref source, .. } => Some(source),
PeachError::Regex(_) => None,
PeachError::SbotCli { .. } => None,
PeachError::SerdeJson(_) => None,
PeachError::SerdeYaml(_) => None,
PeachError::SsbAdminIdNotFound { .. } => None,
PeachError::Utf8ToStr(_) => None,
PeachError::Utf8ToString(_) => None,
PeachError::Write { ref source, .. } => Some(source),
}
}
}
impl From<jsonrpc_client_core::Error> for PeachError {
fn from(err: jsonrpc_client_core::Error) -> PeachError {
PeachError::JsonRpcClientCore { source: err }
}
}
impl From<serde_json::error::Error> for PeachError {
fn from(err: serde_json::error::Error) -> PeachError {
PeachError::Serde { source: err }
}
}
impl From<serde_yaml::Error> for PeachError {
fn from(err: serde_yaml::Error) -> PeachError {
PeachError::YamlError { source: err }
impl std::fmt::Display for PeachError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
PeachError::Io(ref err) => err.fmt(f),
PeachError::JsonRpcClientCore(ref err) => err.fmt(f),
PeachError::JsonRpcCore(ref err) => {
write!(f, "{:?}", err)
}
PeachError::JsonRpcHttp(ref err) => err.fmt(f),
PeachError::NsUpdate { ref msg } => {
write!(f, "Nameserver error: {}", msg)
}
PeachError::ParseBool(ref err) => err.fmt(f),
PeachError::ParseDateTime { ref path, .. } => {
write!(f, "Date/time parse error: {}", path)
}
PeachError::PasswordIncorrect => {
write!(f, "Password error: user-supplied password is incorrect")
}
PeachError::PasswordMismatch => {
write!(f, "Password error: user-supplied passwords do not match")
}
PeachError::PasswordNotSet => {
write!(
f,
"Password error: hash value in YAML configuration file is empty"
)
}
PeachError::Read { ref path, .. } => {
write!(f, "Read error: {}", path)
}
PeachError::Regex(ref err) => err.fmt(f),
PeachError::SbotCli { ref msg } => {
write!(f, "Sbot error: {}", msg)
}
PeachError::SerdeJson(ref err) => err.fmt(f),
PeachError::SerdeYaml(ref err) => err.fmt(f),
PeachError::SsbAdminIdNotFound { ref id } => {
write!(f, "Config error: SSB admin ID `{}` not found", id)
}
PeachError::Utf8ToStr(ref err) => err.fmt(f),
PeachError::Utf8ToString(ref err) => err.fmt(f),
PeachError::Write { ref path, .. } => {
write!(f, "Write error: {}", path)
}
}
}
}
impl From<std::io::Error> for PeachError {
fn from(err: std::io::Error) -> PeachError {
PeachError::StdIoError {
source: err,
msg: "".to_string(),
}
PeachError::Io(err)
}
}
impl From<jsonrpc_client_core::Error> for PeachError {
fn from(err: jsonrpc_client_core::Error) -> PeachError {
PeachError::JsonRpcClientCore(err)
}
}
impl From<jsonrpc_client_http::Error> for PeachError {
fn from(err: jsonrpc_client_http::Error) -> PeachError {
PeachError::JsonRpcHttp(err)
}
}
impl From<str::ParseBoolError> for PeachError {
fn from(err: str::ParseBoolError) -> PeachError {
PeachError::ParseBool(err)
}
}
impl From<regex::Error> for PeachError {
fn from(err: regex::Error) -> PeachError {
PeachError::RegexError { source: err }
PeachError::Regex(err)
}
}
impl From<std::string::FromUtf8Error> for PeachError {
fn from(err: std::string::FromUtf8Error) -> PeachError {
PeachError::FromUtf8Error { source: err }
impl From<serde_json::error::Error> for PeachError {
fn from(err: serde_json::error::Error) -> PeachError {
PeachError::SerdeJson(err)
}
}
impl From<std::str::Utf8Error> for PeachError {
fn from(err: std::str::Utf8Error) -> PeachError {
PeachError::Utf8Error { source: err }
impl From<serde_yaml::Error> for PeachError {
fn from(err: serde_yaml::Error) -> PeachError {
PeachError::SerdeYaml(err)
}
}
impl From<chrono::ParseError> for PeachError {
fn from(err: chrono::ParseError) -> PeachError {
PeachError::ChronoParseError {
source: err,
msg: "".to_string(),
}
impl From<str::Utf8Error> for PeachError {
fn from(err: str::Utf8Error) -> PeachError {
PeachError::Utf8ToStr(err)
}
}
impl From<string::FromUtf8Error> for PeachError {
fn from(err: string::FromUtf8Error) -> PeachError {
PeachError::Utf8ToString(err)
}
}