1use std::fmt;
2
3use macho_core::{ContextFrame, OffsetSpan, ParseError};
4
5const INVALID_FORMAT_CODE: &str = "objc.format.invalid";
6const OUT_OF_BOUNDS_CODE: &str = "objc.bounds.exceeded";
7const INVALID_ADDRESS_CODE: &str = "objc.address.invalid";
8const UNSUPPORTED_INPUT_CODE: &str = "objc.input.unsupported";
9const CORE_FAILED_CODE: &str = "objc.core.failed";
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12#[non_exhaustive]
14pub enum ObjcErrorKind {
15 InvalidFormat,
17 OutOfBounds,
19 InvalidAddress,
21 Unsupported,
23 Core,
25}
26
27#[derive(Debug)]
28pub struct ObjcError {
30 pub kind: ObjcErrorKind,
32 pub location: Option<OffsetSpan>,
34 pub context: Vec<ContextFrame>,
36 message: String,
37 pub source: Option<Box<ParseError>>,
39}
40
41impl ObjcError {
42 pub fn new(kind: ObjcErrorKind, message: impl Into<String>) -> Self {
44 Self {
45 kind,
46 location: None,
47 context: Vec::new(),
48 message: message.into(),
49 source: None,
50 }
51 }
52 pub fn format(message: impl Into<String>) -> Self {
54 Self::new(ObjcErrorKind::InvalidFormat, message)
55 }
56 pub fn address(message: impl Into<String>) -> Self {
58 Self::new(ObjcErrorKind::InvalidAddress, message)
59 }
60 pub fn unsupported(message: impl Into<String>) -> Self {
62 Self::new(ObjcErrorKind::Unsupported, message)
63 }
64 pub fn bounds(offset: u64, needed: u64, available: u64) -> Self {
66 let mut error = Self::new(
67 ObjcErrorKind::OutOfBounds,
68 format!("offset {offset:#x}, needed {needed} bytes, have {available}"),
69 );
70 error.location = Some(OffsetSpan {
71 offset,
72 len: needed,
73 });
74 error
75 }
76 pub fn with_context(mut self, frame: ContextFrame) -> Self {
78 self.context.push(frame);
79 self
80 }
81 pub fn message(&self) -> &str {
83 &self.message
84 }
85 pub const fn code(&self) -> &'static str {
87 match self.kind {
88 ObjcErrorKind::InvalidFormat => INVALID_FORMAT_CODE,
89 ObjcErrorKind::OutOfBounds => OUT_OF_BOUNDS_CODE,
90 ObjcErrorKind::InvalidAddress => INVALID_ADDRESS_CODE,
91 ObjcErrorKind::Unsupported => UNSUPPORTED_INPUT_CODE,
92 ObjcErrorKind::Core => CORE_FAILED_CODE,
93 }
94 }
95}
96
97impl From<ParseError> for ObjcError {
98 fn from(source: ParseError) -> Self {
99 let mut context = source.context.clone();
100 context.push(ContextFrame::Operation { name: "objc" });
101 Self {
102 kind: ObjcErrorKind::Core,
103 location: source.location,
104 context,
105 message: source.message().to_owned(),
106 source: Some(Box::new(source)),
107 }
108 }
109}
110
111#[cfg(feature = "fixups")]
112impl From<macho_dyld::DyldError> for ObjcError {
113 fn from(source: macho_dyld::DyldError) -> Self {
114 let message = source.message().to_owned();
115 let kind = match source.kind {
116 macho_dyld::DyldErrorKind::InvalidFormat => ObjcErrorKind::InvalidFormat,
117 macho_dyld::DyldErrorKind::OutOfBounds => ObjcErrorKind::OutOfBounds,
118 macho_dyld::DyldErrorKind::InvalidAddress => ObjcErrorKind::InvalidAddress,
119 macho_dyld::DyldErrorKind::Unsupported => ObjcErrorKind::Unsupported,
120 macho_dyld::DyldErrorKind::Core => ObjcErrorKind::Core,
121 _ => ObjcErrorKind::Core,
122 };
123 let mut context = source.context;
124 context.push(ContextFrame::Operation { name: "objc" });
125 Self {
126 kind,
127 location: source.location,
128 context,
129 message,
130 source: source.source,
131 }
132 }
133}
134
135impl fmt::Display for ObjcError {
136 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
137 write!(formatter, "{}: {}", self.code(), self.message)
138 }
139}
140impl std::error::Error for ObjcError {
141 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
142 self.source.as_deref().map(|source| source as _)
143 }
144}
145
146pub type Result<T> = std::result::Result<T, ObjcError>;
148pub(crate) type Error = ObjcError;