use thiserror::Error as ThisError;
use crate::codes;
#[derive(Debug, ThisError)]
#[non_exhaustive]
pub enum Error {
#[error("invalid .pio.json package: {0}")]
Malformed(#[source] serde_json::Error),
#[error("{0}")]
UnsupportedVersion(String),
#[error("model_kind does not match model.kind")]
ModelKindMismatch,
#[error("{0}")]
NoSuchIndex(String),
#[error("{0}")]
Payload(String),
#[error(transparent)]
Core(#[from] powerio_tx::Error),
#[error(transparent)]
Multiconductor(#[from] powerio_dist::Error),
#[error("serializing .pio.json: {0}")]
Serialize(#[source] serde_json::Error),
}
impl From<serde_json::Error> for Error {
fn from(error: serde_json::Error) -> Self {
Error::Serialize(error)
}
}
impl Error {
#[must_use]
pub fn code(&self) -> &'static str {
match self {
Error::Core(inner) => inner.code().code,
Error::Multiconductor(inner) => inner.code().code,
Error::Malformed(_) => codes::PARSE_MODULE_MALFORMED.code,
Error::UnsupportedVersion(_) => codes::PARSE_MODULE_UNSUPPORTED_VERSION.code,
Error::ModelKindMismatch => codes::VALIDATE_MODULE_MODEL_KIND_MISMATCH.code,
Error::NoSuchIndex(_) => codes::REQUEST_MODULE_NO_SUCH_INDEX.code,
Error::Payload(_) => codes::BUILD_MODULE_PAYLOAD_FAILED.code,
Error::Serialize(_) => codes::EMIT_MODULE_SERIALIZE_FAILED.code,
}
}
#[must_use]
pub fn category(&self) -> powerio_tx::ErrorCategory {
use powerio_tx::ErrorCategory as C;
match self {
Error::Core(inner) => inner.category(),
Error::Multiconductor(inner) => inner.category(),
Error::Malformed(_) | Error::UnsupportedVersion(_) => C::Parse,
Error::ModelKindMismatch | Error::NoSuchIndex(_) | Error::Payload(_) => C::Data,
Error::Serialize(_) => C::Output,
}
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod tests {
use super::*;
use powerio_tx::ErrorCategory::{Data, Parse};
#[test]
fn a_version_rejection_is_not_a_syntax_failure() {
let version = Error::UnsupportedVersion("stated 0.2.1".into());
assert_eq!(version.category(), Parse);
assert!(matches!(version, Error::UnsupportedVersion(_)));
assert!(matches!(Error::ModelKindMismatch, Error::ModelKindMismatch));
assert_eq!(Error::ModelKindMismatch.category(), Data);
}
#[test]
fn every_error_code_publishes_the_category_the_variant_reports() {
let every: Vec<Error> = vec![
powerio_tx::Error::MissingField("bus").into(),
powerio_dist::Error::UnknownFormat("xyz".into()).into(),
Error::UnsupportedVersion("stated 0.2.1".into()),
Error::ModelKindMismatch,
Error::NoSuchIndex("operating point 3".into()),
Error::Payload("empty".into()),
];
for error in &every {
assert_eq!(error.category(), error.category(), "{}", error.code());
}
}
#[test]
fn a_wrapped_hub_error_keeps_its_own_message() {
let wrapped: Error = powerio_tx::Error::MissingField("bus").into();
assert_eq!(
wrapped.to_string(),
powerio_tx::Error::MissingField("bus").to_string()
);
}
}