use serde::{Deserialize, Serialize};
use wincode::{SchemaRead, SchemaWrite};
use crate::types::{identifiers::FaultyPeer, PeerId, PeerIndex};
#[derive(
thiserror::Error, Debug, Clone, Serialize, Deserialize, SchemaRead, SchemaWrite, PartialEq, Eq,
)]
#[repr(C)]
pub enum PrimitiveError {
#[error("Serialization Failed: {0}.")]
SerializationFailed(String),
#[error("Deserialization Failed: {0}.")]
DeserializationFailed(String),
#[error("Wrong Opening: Expected {0}, got {1}.")]
WrongOpening(String, String),
#[error("Wrong Opening for {0}: Expected {1}, got {2}.")]
WrongOpeningFor(FaultyPeer, String, String),
#[error("Wrong Echo from {0}.")]
WrongEcho(FaultyPeer),
#[error("Wrong signature: {0}.")]
WrongSignature(String),
#[error("Wrong MAC for {0}: got {1:?}.")]
WrongMACFor(FaultyPeer, String),
#[error("Wrong MAC: got {0:?}.")]
WrongMAC(String),
#[error("Wrong correlation. {0}")]
WrongCorrelation(String),
#[error("Invalid Size: expected {0} elements, got {1}")]
InvalidSize(usize, usize),
#[error("Minimum Length Error: expected at least {0} elements, got {1}")]
MinimumLength(usize, usize),
#[error("Invalid Parameters: {0}")]
InvalidParameters(String),
#[error("PeerIndex out of bounds: {0} ∉ [0, {1}].")]
InvalidPeerIndex(PeerIndex, usize),
#[error("Invalid peer ID: {0}")]
InvalidPeerId(PeerId),
#[error("Zero value sampled for type {0}")]
ZeroValueSampled(String),
}
impl From<quinn::rustls::Error> for PrimitiveError {
fn from(err: quinn::rustls::Error) -> Self {
PrimitiveError::InvalidParameters(err.to_string())
}
}
impl From<Vec<PrimitiveError>> for PrimitiveError {
fn from(errors: Vec<PrimitiveError>) -> Self {
if errors.is_empty() {
return PrimitiveError::InvalidParameters(
"Cannot aggregate an empty list of errors.".to_owned(),
);
};
if errors.len() == 1 {
return errors.into_iter().next().unwrap();
}
let (peers, receiver, invalid_errs) = errors.into_iter().fold(
(vec![], vec![], vec![]),
|(mut peers, mut receiver, mut invalid_errors), e| {
match e {
PrimitiveError::WrongMACFor(p, r) => {
peers.push(p);
receiver.push(r);
}
PrimitiveError::WrongEcho(p) => {
peers.push(p);
}
_ => invalid_errors.push(e.to_string()),
}
(peers, receiver, invalid_errors)
},
);
if !invalid_errs.is_empty() {
return PrimitiveError::InvalidParameters(
"Only WrongMACFor|WrongEcho can be aggregated. Got: ".to_owned()
+ &invalid_errs.join(", "),
);
}
match (peers, receiver.as_slice()) {
(peers, []) => PrimitiveError::WrongEcho(FaultyPeer::Multiple(peers)),
(peers, receiver) if peers.len() == receiver.len() => {
PrimitiveError::WrongMACFor(FaultyPeer::Multiple(peers), receiver.join(", "))
}
_ => PrimitiveError::InvalidParameters(
"All errors must be of the same type to be aggregated".to_owned(),
),
}
}
}
impl PrimitiveError {
pub fn blame(self, peer_to_blame: PeerIndex) -> PrimitiveError {
match self {
PrimitiveError::WrongMAC(receiver) => {
PrimitiveError::WrongMACFor(FaultyPeer::Foreign(peer_to_blame), receiver)
}
PrimitiveError::WrongMACFor(peers, receiver) => {
PrimitiveError::WrongMACFor(peers.push(peer_to_blame), receiver)
}
PrimitiveError::WrongOpening(expected, got) => {
PrimitiveError::WrongOpeningFor(FaultyPeer::Foreign(peer_to_blame), expected, got)
}
PrimitiveError::WrongOpeningFor(peers, expected, got) => {
PrimitiveError::WrongOpeningFor(peers.push(peer_to_blame), expected, got)
}
_ => self,
}
}
}