Skip to main content

macho_cpp/
error.rs

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