diff --git a/peach-lib/src/error.rs b/peach-lib/src/error.rs index 96e7b4b3..e309195e 100644 --- a/peach-lib/src/error.rs +++ b/peach-lib/src/error.rs @@ -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; +#![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 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 for PeachError { - fn from(err: jsonrpc_client_core::Error) -> PeachError { - PeachError::JsonRpcClientCore { source: err } - } -} - -impl From for PeachError { - fn from(err: serde_json::error::Error) -> PeachError { - PeachError::Serde { source: err } - } -} - -impl From 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 for PeachError { fn from(err: std::io::Error) -> PeachError { - PeachError::StdIoError { - source: err, - msg: "".to_string(), - } + PeachError::Io(err) + } +} + +impl From for PeachError { + fn from(err: jsonrpc_client_core::Error) -> PeachError { + PeachError::JsonRpcClientCore(err) + } +} + +impl From for PeachError { + fn from(err: jsonrpc_client_http::Error) -> PeachError { + PeachError::JsonRpcHttp(err) + } +} + +impl From for PeachError { + fn from(err: str::ParseBoolError) -> PeachError { + PeachError::ParseBool(err) } } impl From for PeachError { fn from(err: regex::Error) -> PeachError { - PeachError::RegexError { source: err } + PeachError::Regex(err) } } -impl From for PeachError { - fn from(err: std::string::FromUtf8Error) -> PeachError { - PeachError::FromUtf8Error { source: err } +impl From for PeachError { + fn from(err: serde_json::error::Error) -> PeachError { + PeachError::SerdeJson(err) } } -impl From for PeachError { - fn from(err: std::str::Utf8Error) -> PeachError { - PeachError::Utf8Error { source: err } +impl From for PeachError { + fn from(err: serde_yaml::Error) -> PeachError { + PeachError::SerdeYaml(err) } } -impl From for PeachError { - fn from(err: chrono::ParseError) -> PeachError { - PeachError::ChronoParseError { - source: err, - msg: "".to_string(), - } +impl From for PeachError { + fn from(err: str::Utf8Error) -> PeachError { + PeachError::Utf8ToStr(err) + } +} + +impl From for PeachError { + fn from(err: string::FromUtf8Error) -> PeachError { + PeachError::Utf8ToString(err) } }