use std::io;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum GpbError {
#[error("Encoding failed: {0}")]
Encode(#[from] EncodeError),
#[error("Decoding failed: {0}")]
Decode(#[from] DecodeError),
#[error("I/O error: {0}")]
Io(#[from] io::Error),
#[error("Invalid message format: {0}")]
InvalidFormat(String),
}
#[derive(Error, Debug)]
pub enum EncodeError {
#[error("Buffer too small: need {needed} bytes, have {available}")]
BufferTooSmall {
needed: usize,
available: usize,
},
#[error("Invalid field value: field {field_id}, reason: {reason}")]
InvalidFieldValue {
field_id: u32,
reason: String,
},
#[error("Missing required field: {field_id}")]
MissingRequiredField {
field_id: u32,
},
#[error("Unsupported message type: {message_type}")]
UnsupportedMessageType {
message_type: String,
},
#[error("Prost encoding error: {0}")]
Prost(#[from] prost::EncodeError),
}
#[derive(Error, Debug)]
pub enum DecodeError {
#[error("Truncated buffer: expected at least {expected} bytes, got {actual}")]
TruncatedBuffer {
expected: usize,
actual: usize,
},
#[error("Invalid wire format: {reason}")]
InvalidWireFormat {
reason: String,
},
#[error("Unknown field: tag {tag}")]
UnknownField {
tag: u32,
},
#[error("Field validation failed: field {field_id}, value: {value:?}")]
FieldValidationFailed {
field_id: u32,
value: String,
},
#[error("Checksum mismatch: expected {expected}, calculated {actual}")]
ChecksumMismatch {
expected: u32,
actual: u32,
},
#[error("Prost decoding error: {0}")]
Prost(#[from] prost::DecodeError),
}
impl GpbError {
pub fn is_recoverable(&self) -> bool {
match self {
GpbError::Encode(EncodeError::BufferTooSmall { .. }) => true,
GpbError::Decode(DecodeError::TruncatedBuffer { .. }) => true,
GpbError::Io(_) => true,
_ => false,
}
}
pub fn severity(&self) -> ErrorSeverity {
match self {
GpbError::Encode(EncodeError::InvalidFieldValue { .. }) => ErrorSeverity::High,
GpbError::Encode(EncodeError::MissingRequiredField { .. }) => ErrorSeverity::High,
GpbError::Decode(DecodeError::ChecksumMismatch { .. }) => ErrorSeverity::Critical,
GpbError::Decode(DecodeError::InvalidWireFormat { .. }) => ErrorSeverity::High,
GpbError::Decode(DecodeError::TruncatedBuffer { .. }) => ErrorSeverity::Medium,
GpbError::Encode(EncodeError::BufferTooSmall { .. }) => ErrorSeverity::Low,
_ => ErrorSeverity::Medium,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorSeverity {
Low,
Medium,
High,
Critical,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_recoverability() {
let recoverable = GpbError::Encode(EncodeError::BufferTooSmall {
needed: 100,
available: 50,
});
assert!(recoverable.is_recoverable());
let non_recoverable = GpbError::Encode(EncodeError::MissingRequiredField { field_id: 35 });
assert!(!non_recoverable.is_recoverable());
}
#[test]
fn test_error_severity() {
let critical = GpbError::Decode(DecodeError::ChecksumMismatch {
expected: 123,
actual: 456,
});
assert_eq!(critical.severity(), ErrorSeverity::Critical);
let low = GpbError::Encode(EncodeError::BufferTooSmall {
needed: 100,
available: 50,
});
assert_eq!(low.severity(), ErrorSeverity::Low);
}
}