1use std::fmt;
4
5const INVALID_FORMAT_CODE: &str = "parse.format.invalid";
6const OUT_OF_BOUNDS_CODE: &str = "parse.bounds.exceeded";
7const INVALID_ADDRESS_CODE: &str = "parse.address.invalid";
8const INVALID_LOAD_COMMAND_CODE: &str = "parse.load_command.invalid";
9const LIMIT_EXCEEDED_CODE: &str = "parse.limit.exceeded";
10const UNSUPPORTED_INPUT_CODE: &str = "parse.input.unsupported";
11const VALIDATION_FAILED_CODE: &str = "parse.validation.failed";
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[non_exhaustive]
16pub enum ParseErrorKind {
17 InvalidFormat,
19 OutOfBounds,
21 InvalidAddress,
23 InvalidLoadCommand,
25 LimitExceeded,
27 Unsupported,
29 Validation,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub struct OffsetSpan {
36 pub offset: u64,
38 pub len: u64,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
44#[non_exhaustive]
45pub enum ContextFrame {
46 FatArchitecture {
49 index: usize,
51 },
52 LoadCommand {
55 index: usize,
57 },
58 Operation {
61 name: &'static str,
63 },
64}
65
66#[derive(Debug, Clone, PartialEq, Eq)]
68pub struct ParseError {
69 pub kind: ParseErrorKind,
71 pub location: Option<OffsetSpan>,
73 pub context: Vec<ContextFrame>,
75 message: String,
76}
77
78impl ParseError {
79 pub fn new(kind: ParseErrorKind, message: impl Into<String>) -> Self {
81 Self {
82 kind,
83 location: None,
84 context: Vec::new(),
85 message: message.into(),
86 }
87 }
88
89 pub fn format(message: impl Into<String>) -> Self {
91 Self::new(ParseErrorKind::InvalidFormat, message)
92 }
93
94 pub fn bounds(offset: u64, needed: u64, available: u64) -> Self {
96 Self::new(
97 ParseErrorKind::OutOfBounds,
98 format!("offset {offset:#x}, needed {needed} bytes, have {available}"),
99 )
100 .with_location(OffsetSpan {
101 offset,
102 len: needed,
103 })
104 }
105
106 pub fn address(message: impl Into<String>) -> Self {
108 Self::new(ParseErrorKind::InvalidAddress, message)
109 }
110
111 pub fn command(message: impl Into<String>) -> Self {
113 Self::new(ParseErrorKind::InvalidLoadCommand, message)
114 }
115
116 pub fn limit(message: impl Into<String>) -> Self {
118 Self::new(ParseErrorKind::LimitExceeded, message)
119 }
120
121 pub fn unsupported(message: impl Into<String>) -> Self {
123 Self::new(ParseErrorKind::Unsupported, message)
124 }
125
126 pub fn validation(message: impl Into<String>) -> Self {
128 Self::new(ParseErrorKind::Validation, message)
129 }
130
131 pub fn with_location(mut self, location: OffsetSpan) -> Self {
133 self.location = Some(location);
134 self
135 }
136
137 pub fn with_context(mut self, frame: ContextFrame) -> Self {
139 self.context.push(frame);
140 self
141 }
142
143 pub fn message(&self) -> &str {
145 &self.message
146 }
147
148 pub const fn code(&self) -> &'static str {
150 match self.kind {
151 ParseErrorKind::InvalidFormat => INVALID_FORMAT_CODE,
152 ParseErrorKind::OutOfBounds => OUT_OF_BOUNDS_CODE,
153 ParseErrorKind::InvalidAddress => INVALID_ADDRESS_CODE,
154 ParseErrorKind::InvalidLoadCommand => INVALID_LOAD_COMMAND_CODE,
155 ParseErrorKind::LimitExceeded => LIMIT_EXCEEDED_CODE,
156 ParseErrorKind::Unsupported => UNSUPPORTED_INPUT_CODE,
157 ParseErrorKind::Validation => VALIDATION_FAILED_CODE,
158 }
159 }
160}
161
162impl fmt::Display for ParseError {
163 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
164 write!(formatter, "{}: {}", self.code(), self.message)?;
165 if let Some(location) = self.location {
166 write!(
167 formatter,
168 " at {:#x}..{:#x}",
169 location.offset,
170 location.offset.saturating_add(location.len)
171 )?;
172 }
173 Ok(())
174 }
175}
176
177impl std::error::Error for ParseError {}
178
179pub type ParseResult<T> = core::result::Result<T, ParseError>;
181
182pub(crate) type Error = ParseError;
183pub(crate) type Result<T> = ParseResult<T>;