Skip to main content

macho_objc/
error.rs

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/// The ObjcErrorKind type.
13#[non_exhaustive]
14pub enum ObjcErrorKind {
15    /// The InvalidFormat variant.
16    InvalidFormat,
17    /// The OutOfBounds variant.
18    OutOfBounds,
19    /// The InvalidAddress variant.
20    InvalidAddress,
21    /// The Unsupported variant.
22    Unsupported,
23    /// The Core variant.
24    Core,
25}
26
27#[derive(Debug)]
28/// The ObjcError type.
29pub struct ObjcError {
30    /// The kind field.
31    pub kind: ObjcErrorKind,
32    /// The location field.
33    pub location: Option<OffsetSpan>,
34    /// The context field.
35    pub context: Vec<ContextFrame>,
36    message: String,
37    /// The source field.
38    pub source: Option<Box<ParseError>>,
39}
40
41impl ObjcError {
42    /// Performs new.
43    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    /// Performs format.
53    pub fn format(message: impl Into<String>) -> Self {
54        Self::new(ObjcErrorKind::InvalidFormat, message)
55    }
56    /// Performs address.
57    pub fn address(message: impl Into<String>) -> Self {
58        Self::new(ObjcErrorKind::InvalidAddress, message)
59    }
60    /// Performs unsupported.
61    pub fn unsupported(message: impl Into<String>) -> Self {
62        Self::new(ObjcErrorKind::Unsupported, message)
63    }
64    /// Performs bounds.
65    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    /// Performs with_context.
77    pub fn with_context(mut self, frame: ContextFrame) -> Self {
78        self.context.push(frame);
79        self
80    }
81    /// Performs message.
82    pub fn message(&self) -> &str {
83        &self.message
84    }
85    /// Performs code.
86    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
146/// The Result type.
147pub type Result<T> = std::result::Result<T, ObjcError>;
148/// The Error type.
149pub(crate) type Error = ObjcError;