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. #![warn(missing_docs)]
pub use snafu::ResultExt;
use snafu::Snafu;
use std::error;
pub type BoxError = Box<dyn error::Error>;
#[derive(Debug, Snafu)] //! 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.
#[snafu(visibility(pub(crate)))]
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 { pub enum PeachError {
#[snafu(display("{}", source))] /// Represents all other cases of `std::io::Error`.
JsonRpcHttp { source: jsonrpc_client_http::Error }, Io(io::Error),
#[snafu(display("{}", source))]
JsonRpcClientCore { source: jsonrpc_client_core::Error }, /// Represents a JSON-RPC core error returned from a JSON-RPC client.
#[snafu(display("{}", source))] JsonRpcClientCore(jsonrpc_client_core::Error),
Serde { source: serde_json::error::Error },
#[snafu(display("{}", source))] /// Represents a JSON-RPC core error returned from a JSON-RPC server.
PeachParseBoolError { source: std::str::ParseBoolError }, JsonRpcCore(jsonrpc_core::Error),
#[snafu(display("{}", source))]
SetConfigError { source: serde_yaml::Error }, /// Represents a JSON-RPC HTTP error returned from a JSON-RPC client.
#[snafu(display("Failed to read: {}", file))] JsonRpcHttp(jsonrpc_client_http::Error),
ReadConfigError {
source: std::io::Error, /// Represents a failure to update the nameserver.
file: String, NsUpdate {
}, /// A message describing the context of the attempted nameserver update.
#[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,
msg: String, msg: String,
}, },
#[snafu(display("Failed to save dynamic dns success log: {}", source))]
SaveDynDnsResultError { source: std::io::Error }, /// Represents a failure to parse a string slice to a boolean value.
#[snafu(display("New passwords do not match"))] ParseBool(str::ParseBoolError),
PasswordsDoNotMatch,
#[snafu(display("No admin password is set"))] /// 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, PasswordNotSet,
#[snafu(display("The supplied password was not correct"))]
InvalidPassword, /// Represents a failure to read from input. Includes the error source and the file path used
#[snafu(display("Error saving new password: {}", msg))] /// in the read attempt.
FailedToSetNewPassword { msg: String }, Read {
#[snafu(display("Error calling sbotcli: {}", msg))] /// The underlying source of the error.
SbotCliError { msg: String }, source: io::Error,
#[snafu(display("Error deleting ssb admin id, id not found"))] /// The file path for the read attempt.
SsbAdminIdNotFound { id: String }, 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 { impl std::error::Error for PeachError {
fn from(err: jsonrpc_client_http::Error) -> PeachError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
PeachError::JsonRpcHttp { source: err } 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 { impl std::fmt::Display for PeachError {
fn from(err: jsonrpc_client_core::Error) -> PeachError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
PeachError::JsonRpcClientCore { source: err } match *self {
} PeachError::Io(ref err) => err.fmt(f),
} PeachError::JsonRpcClientCore(ref err) => err.fmt(f),
PeachError::JsonRpcCore(ref err) => {
impl From<serde_json::error::Error> for PeachError { write!(f, "{:?}", err)
fn from(err: serde_json::error::Error) -> PeachError { }
PeachError::Serde { source: err } PeachError::JsonRpcHttp(ref err) => err.fmt(f),
} PeachError::NsUpdate { ref msg } => {
} write!(f, "Nameserver error: {}", msg)
}
impl From<serde_yaml::Error> for PeachError { PeachError::ParseBool(ref err) => err.fmt(f),
fn from(err: serde_yaml::Error) -> PeachError { PeachError::ParseDateTime { ref path, .. } => {
PeachError::YamlError { source: err } 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 { impl From<std::io::Error> for PeachError {
fn from(err: std::io::Error) -> PeachError { fn from(err: std::io::Error) -> PeachError {
PeachError::StdIoError { PeachError::Io(err)
source: err, }
msg: "".to_string(), }
}
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 { impl From<regex::Error> for PeachError {
fn from(err: regex::Error) -> PeachError { fn from(err: regex::Error) -> PeachError {
PeachError::RegexError { source: err } PeachError::Regex(err)
} }
} }
impl From<std::string::FromUtf8Error> for PeachError { impl From<serde_json::error::Error> for PeachError {
fn from(err: std::string::FromUtf8Error) -> PeachError { fn from(err: serde_json::error::Error) -> PeachError {
PeachError::FromUtf8Error { source: err } PeachError::SerdeJson(err)
} }
} }
impl From<std::str::Utf8Error> for PeachError { impl From<serde_yaml::Error> for PeachError {
fn from(err: std::str::Utf8Error) -> PeachError { fn from(err: serde_yaml::Error) -> PeachError {
PeachError::Utf8Error { source: err } PeachError::SerdeYaml(err)
} }
} }
impl From<chrono::ParseError> for PeachError { impl From<str::Utf8Error> for PeachError {
fn from(err: chrono::ParseError) -> PeachError { fn from(err: str::Utf8Error) -> PeachError {
PeachError::ChronoParseError { PeachError::Utf8ToStr(err)
source: err, }
msg: "".to_string(), }
}
impl From<string::FromUtf8Error> for PeachError {
fn from(err: string::FromUtf8Error) -> PeachError {
PeachError::Utf8ToString(err)
} }
} }