1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
use std::backtrace::Backtrace;
use std::fmt;
use arrow::error::ArrowError;
/// A captured backtrace, wrapped to avoid triggering thiserror's unstable
/// `Error::provide()` codegen (which requires `error_generic_member_access`).
pub struct CapturedBacktrace(Backtrace);
impl CapturedBacktrace {
fn capture() -> Self {
Self(Backtrace::capture())
}
/// Returns the backtrace status.
pub fn status(&self) -> std::backtrace::BacktraceStatus {
self.0.status()
}
}
impl fmt::Debug for CapturedBacktrace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl fmt::Display for CapturedBacktrace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
/// The unified error type for the oxbow crate.
#[derive(Debug, thiserror::Error)]
pub enum OxbowError {
/// Invalid user-provided configuration: unknown column names, invalid field
/// definitions, invalid tag type codes, bad schema parameters, etc.
#[error("{message}")]
InvalidInput {
message: String,
trace: CapturedBacktrace,
},
/// Data does not conform to the expected format during parsing: type
/// mismatches in tag values, INFO fields, genotype fields, BED fields,
/// malformed records, etc.
#[error("{message}")]
InvalidData {
message: String,
trace: CapturedBacktrace,
},
/// A required resource was not found: missing reference sequences, missing
/// index headers, missing samples.
#[error("{message}")]
NotFound {
message: String,
trace: CapturedBacktrace,
},
/// An I/O error from the underlying reader/writer.
#[error(transparent)]
Io(std::io::Error),
/// An Arrow error from batch construction or schema operations.
#[error(transparent)]
Arrow(ArrowError),
/// An error from an upstream library (noodles, bigtools, etc.) that
/// doesn't fit the other categories.
#[error(transparent)]
External(Box<dyn std::error::Error + Send + Sync>),
}
/// A specialized `Result` type for oxbow operations.
pub type Result<T> = std::result::Result<T, OxbowError>;
impl OxbowError {
/// Creates an `InvalidInput` error with the given message.
pub fn invalid_input(message: impl Into<String>) -> Self {
Self::InvalidInput {
message: message.into(),
trace: CapturedBacktrace::capture(),
}
}
/// Creates an `InvalidData` error with the given message.
pub fn invalid_data(message: impl Into<String>) -> Self {
Self::InvalidData {
message: message.into(),
trace: CapturedBacktrace::capture(),
}
}
/// Creates a `NotFound` error with the given message.
pub fn not_found(message: impl Into<String>) -> Self {
Self::NotFound {
message: message.into(),
trace: CapturedBacktrace::capture(),
}
}
/// Returns the captured backtrace, if any.
pub fn backtrace(&self) -> Option<&CapturedBacktrace> {
match self {
Self::InvalidInput { trace, .. }
| Self::InvalidData { trace, .. }
| Self::NotFound { trace, .. } => Some(trace),
Self::Io(_) | Self::Arrow(_) | Self::External(_) => None,
}
}
/// Formats the error message with the Rust backtrace appended, if one was
/// captured. Useful for forwarding to Python exception messages.
pub fn display_with_backtrace(&self) -> String {
if let Some(bt) = self.backtrace() {
if bt.status() == std::backtrace::BacktraceStatus::Captured {
return format!("{self}\n\nRust backtrace:\n{bt}");
}
}
self.to_string()
}
}
impl From<std::io::Error> for OxbowError {
fn from(err: std::io::Error) -> Self {
Self::Io(err)
}
}
impl From<ArrowError> for OxbowError {
fn from(err: ArrowError) -> Self {
Self::Arrow(err)
}
}
impl From<OxbowError> for ArrowError {
fn from(err: OxbowError) -> Self {
// Bake the backtrace into the error message because only the string
// survives the Arrow C Stream Interface boundary to Python/pyarrow.
let msg = err.display_with_backtrace();
ArrowError::ExternalError(Box::new(std::io::Error::other(msg)))
}
}