datafusion_odata/
error.rs

1use datafusion::arrow::datatypes::DataType;
2use std::string::FromUtf8Error;
3
4///////////////////////////////////////////////////////////////////////////////
5
6#[derive(thiserror::Error, Debug)]
7pub enum ODataError {
8    #[error(transparent)]
9    BadRequest(#[from] BadRequest),
10    #[error(transparent)]
11    UnsupportedDataType(#[from] UnsupportedDataType),
12    #[error(transparent)]
13    FromUtf8Error(#[from] FromUtf8Error),
14    #[error(transparent)]
15    UnsupportedFeature(#[from] UnsupportedFeature),
16    #[error(transparent)]
17    UnsupportedNetProtocol(#[from] UnsupportedNetProtocol),
18    #[error(transparent)]
19    CollectionNotFound(#[from] CollectionNotFound),
20    #[error(transparent)]
21    CollectionAddressNotAssigned(#[from] CollectionAddressNotAssigned),
22    #[error(transparent)]
23    KeyColumnNotAssigned(#[from] KeyColumnNotAssigned),
24    #[error(transparent)]
25    Internal(InternalError),
26}
27
28impl ODataError {
29    pub fn internal(error: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>) -> Self {
30        Self::Internal(InternalError::new(error))
31    }
32
33    pub fn bad_request(
34        error: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
35    ) -> Self {
36        Self::BadRequest(BadRequest::new(error))
37    }
38
39    pub fn handle_no_table_as_collection_not_found(
40        collection: impl Into<String>,
41        err: datafusion::error::DataFusionError,
42    ) -> Self {
43        match err {
44            datafusion::error::DataFusionError::Plan(e) if e.contains("No table named") => {
45                Self::CollectionNotFound(CollectionNotFound::new(collection))
46            }
47            _ => Self::internal(err),
48        }
49    }
50}
51
52impl axum::response::IntoResponse for ODataError {
53    fn into_response(self) -> axum::response::Response {
54        match self {
55            Self::Internal(_) | Self::FromUtf8Error(_) => {
56                (http::StatusCode::INTERNAL_SERVER_ERROR, "Internal error").into_response()
57            }
58            Self::BadRequest(e) => e.into_response(),
59            Self::CollectionNotFound(e) => e.into_response(),
60            Self::UnsupportedDataType(e) => e.into_response(),
61            Self::UnsupportedFeature(e) => e.into_response(),
62            Self::CollectionAddressNotAssigned(e) => e.into_response(),
63            Self::KeyColumnNotAssigned(e) => e.into_response(),
64            Self::UnsupportedNetProtocol(e) => e.into_response(),
65        }
66    }
67}
68
69///////////////////////////////////////////////////////////////////////////////
70
71#[derive(thiserror::Error, Debug)]
72#[error("Internal error")]
73pub struct InternalError {
74    #[source]
75    pub source: Box<dyn std::error::Error + Send + Sync + 'static>,
76}
77
78impl InternalError {
79    pub fn new(error: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>) -> Self {
80        Self {
81            source: error.into(),
82        }
83    }
84}
85
86///////////////////////////////////////////////////////////////////////////////
87
88#[derive(thiserror::Error, Debug)]
89#[error("{source}")]
90pub struct BadRequest {
91    #[source]
92    pub source: Box<dyn std::error::Error + Send + Sync + 'static>,
93}
94
95impl BadRequest {
96    pub fn new(error: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>) -> Self {
97        Self {
98            source: error.into(),
99        }
100    }
101}
102
103impl axum::response::IntoResponse for BadRequest {
104    fn into_response(self) -> axum::response::Response {
105        (http::StatusCode::BAD_REQUEST, self.to_string()).into_response()
106    }
107}
108
109///////////////////////////////////////////////////////////////////////////////
110
111#[derive(thiserror::Error, Debug)]
112#[error("Collection {collection} not found")]
113pub struct CollectionNotFound {
114    pub collection: String,
115}
116
117impl CollectionNotFound {
118    pub fn new(collection: impl Into<String>) -> Self {
119        Self {
120            collection: collection.into(),
121        }
122    }
123}
124
125impl axum::response::IntoResponse for CollectionNotFound {
126    fn into_response(self) -> axum::response::Response {
127        (http::StatusCode::NOT_FOUND, self.to_string()).into_response()
128    }
129}
130
131///////////////////////////////////////////////////////////////////////////////
132
133#[derive(thiserror::Error, Debug)]
134#[error("Key column not assigned")]
135pub struct KeyColumnNotAssigned;
136
137impl axum::response::IntoResponse for KeyColumnNotAssigned {
138    fn into_response(self) -> axum::response::Response {
139        (http::StatusCode::NOT_IMPLEMENTED, self.to_string()).into_response()
140    }
141}
142
143///////////////////////////////////////////////////////////////////////////////
144
145#[derive(thiserror::Error, Debug)]
146#[error("Collection address not assigned")]
147pub struct CollectionAddressNotAssigned;
148
149impl axum::response::IntoResponse for CollectionAddressNotAssigned {
150    fn into_response(self) -> axum::response::Response {
151        (http::StatusCode::NOT_IMPLEMENTED, self.to_string()).into_response()
152    }
153}
154
155///////////////////////////////////////////////////////////////////////////////
156
157#[derive(thiserror::Error, Debug)]
158#[error("Unsupported data type: {data_type}")]
159pub struct UnsupportedDataType {
160    pub data_type: DataType,
161}
162
163impl UnsupportedDataType {
164    pub fn new(data_type: DataType) -> Self {
165        Self { data_type }
166    }
167}
168
169impl axum::response::IntoResponse for UnsupportedDataType {
170    fn into_response(self) -> axum::response::Response {
171        (http::StatusCode::NOT_IMPLEMENTED, self.to_string()).into_response()
172    }
173}
174
175///////////////////////////////////////////////////////////////////////////////
176
177#[derive(thiserror::Error, Debug)]
178#[error("Unsupported net protocol: {url}")]
179pub struct UnsupportedNetProtocol {
180    pub url: String,
181}
182
183impl UnsupportedNetProtocol {
184    pub fn new(url: String) -> Self {
185        Self { url }
186    }
187}
188
189impl axum::response::IntoResponse for UnsupportedNetProtocol {
190    fn into_response(self) -> axum::response::Response {
191        (http::StatusCode::NOT_IMPLEMENTED, self.to_string()).into_response()
192    }
193}
194
195///////////////////////////////////////////////////////////////////////////////
196
197#[derive(thiserror::Error, Debug)]
198#[error("Unsupported feature: {feature}")]
199pub struct UnsupportedFeature {
200    pub feature: String,
201}
202
203impl UnsupportedFeature {
204    pub fn new(feature: impl Into<String>) -> Self {
205        Self {
206            feature: feature.into(),
207        }
208    }
209}
210
211impl axum::response::IntoResponse for UnsupportedFeature {
212    fn into_response(self) -> axum::response::Response {
213        (http::StatusCode::NOT_IMPLEMENTED, self.to_string()).into_response()
214    }
215}
216
217///////////////////////////////////////////////////////////////////////////////
218
219impl From<quick_xml::Error> for ODataError {
220    fn from(error: quick_xml::Error) -> Self {
221        ODataError::Internal(InternalError::new(error))
222    }
223}
224
225impl From<quick_xml::encoding::EncodingError> for ODataError {
226    fn from(error: quick_xml::encoding::EncodingError) -> Self {
227        ODataError::Internal(InternalError::new(error))
228    }
229}
230
231impl From<std::io::Error> for ODataError {
232    fn from(error: std::io::Error) -> Self {
233        ODataError::Internal(InternalError::new(error))
234    }
235}