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#[non_exhaustive]
18pub enum CppErrorKind {
19 InvalidFormat,
21 OutOfBounds,
23 InvalidAddress,
25 Unsupported,
27 Core,
29 #[cfg(feature = "fixups")]
30 Dyld,
32}
33
34#[derive(Debug)]
35#[non_exhaustive]
37pub enum CppErrorSource {
38 Parse(Box<ParseError>),
40 #[cfg(feature = "fixups")]
41 Dyld(Box<DyldError>),
43}
44
45#[derive(Debug)]
46pub struct CppError {
48 pub kind: CppErrorKind,
50 pub location: Option<OffsetSpan>,
52 pub context: Vec<ContextFrame>,
54 message: String,
55 pub source: Option<CppErrorSource>,
57}
58
59impl CppError {
60 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 pub fn format(message: impl Into<String>) -> Self {
72 Self::new(CppErrorKind::InvalidFormat, message)
73 }
74 pub fn address(message: impl Into<String>) -> Self {
76 Self::new(CppErrorKind::InvalidAddress, message)
77 }
78 pub fn unsupported(message: impl Into<String>) -> Self {
80 Self::new(CppErrorKind::Unsupported, message)
81 }
82 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 pub fn with_context(mut self, frame: ContextFrame) -> Self {
96 self.context.push(frame);
97 self
98 }
99 pub fn message(&self) -> &str {
101 &self.message
102 }
103 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
161pub type Result<T> = std::result::Result<T, CppError>;
163#[cfg(feature = "fixups")]
165pub(crate) type Error = CppError;