use std::fmt;
const INVALID_FORMAT_CODE: &str = "parse.format.invalid";
const OUT_OF_BOUNDS_CODE: &str = "parse.bounds.exceeded";
const INVALID_ADDRESS_CODE: &str = "parse.address.invalid";
const INVALID_LOAD_COMMAND_CODE: &str = "parse.load_command.invalid";
const LIMIT_EXCEEDED_CODE: &str = "parse.limit.exceeded";
const UNSUPPORTED_INPUT_CODE: &str = "parse.input.unsupported";
const VALIDATION_FAILED_CODE: &str = "parse.validation.failed";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ParseErrorKind {
InvalidFormat,
OutOfBounds,
InvalidAddress,
InvalidLoadCommand,
LimitExceeded,
Unsupported,
Validation,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OffsetSpan {
pub offset: u64,
pub len: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ContextFrame {
FatArchitecture {
index: usize,
},
LoadCommand {
index: usize,
},
Operation {
name: &'static str,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseError {
pub kind: ParseErrorKind,
pub location: Option<OffsetSpan>,
pub context: Vec<ContextFrame>,
message: String,
}
impl ParseError {
pub fn new(kind: ParseErrorKind, message: impl Into<String>) -> Self {
Self {
kind,
location: None,
context: Vec::new(),
message: message.into(),
}
}
pub fn format(message: impl Into<String>) -> Self {
Self::new(ParseErrorKind::InvalidFormat, message)
}
pub fn bounds(offset: u64, needed: u64, available: u64) -> Self {
Self::new(
ParseErrorKind::OutOfBounds,
format!("offset {offset:#x}, needed {needed} bytes, have {available}"),
)
.with_location(OffsetSpan {
offset,
len: needed,
})
}
pub fn address(message: impl Into<String>) -> Self {
Self::new(ParseErrorKind::InvalidAddress, message)
}
pub fn command(message: impl Into<String>) -> Self {
Self::new(ParseErrorKind::InvalidLoadCommand, message)
}
pub fn limit(message: impl Into<String>) -> Self {
Self::new(ParseErrorKind::LimitExceeded, message)
}
pub fn unsupported(message: impl Into<String>) -> Self {
Self::new(ParseErrorKind::Unsupported, message)
}
pub fn validation(message: impl Into<String>) -> Self {
Self::new(ParseErrorKind::Validation, message)
}
pub fn with_location(mut self, location: OffsetSpan) -> Self {
self.location = Some(location);
self
}
pub fn with_context(mut self, frame: ContextFrame) -> Self {
self.context.push(frame);
self
}
pub fn message(&self) -> &str {
&self.message
}
pub const fn code(&self) -> &'static str {
match self.kind {
ParseErrorKind::InvalidFormat => INVALID_FORMAT_CODE,
ParseErrorKind::OutOfBounds => OUT_OF_BOUNDS_CODE,
ParseErrorKind::InvalidAddress => INVALID_ADDRESS_CODE,
ParseErrorKind::InvalidLoadCommand => INVALID_LOAD_COMMAND_CODE,
ParseErrorKind::LimitExceeded => LIMIT_EXCEEDED_CODE,
ParseErrorKind::Unsupported => UNSUPPORTED_INPUT_CODE,
ParseErrorKind::Validation => VALIDATION_FAILED_CODE,
}
}
}
impl fmt::Display for ParseError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}: {}", self.code(), self.message)?;
if let Some(location) = self.location {
write!(
formatter,
" at {:#x}..{:#x}",
location.offset,
location.offset.saturating_add(location.len)
)?;
}
Ok(())
}
}
impl std::error::Error for ParseError {}
pub type ParseResult<T> = core::result::Result<T, ParseError>;
pub(crate) type Error = ParseError;
pub(crate) type Result<T> = ParseResult<T>;