datafusion-odata 54.0.0

OData protocol adapter for Apache Datafusion
Documentation
use datafusion::arrow::datatypes::DataType;
use std::string::FromUtf8Error;

///////////////////////////////////////////////////////////////////////////////

#[derive(thiserror::Error, Debug)]
pub enum ODataError {
    #[error(transparent)]
    BadRequest(#[from] BadRequest),
    #[error(transparent)]
    UnsupportedDataType(#[from] UnsupportedDataType),
    #[error(transparent)]
    FromUtf8Error(#[from] FromUtf8Error),
    #[error(transparent)]
    UnsupportedFeature(#[from] UnsupportedFeature),
    #[error(transparent)]
    UnsupportedNetProtocol(#[from] UnsupportedNetProtocol),
    #[error(transparent)]
    CollectionNotFound(#[from] CollectionNotFound),
    #[error(transparent)]
    CollectionAddressNotAssigned(#[from] CollectionAddressNotAssigned),
    #[error(transparent)]
    KeyColumnNotAssigned(#[from] KeyColumnNotAssigned),
    #[error(transparent)]
    Internal(InternalError),
}

impl ODataError {
    pub fn internal(error: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>) -> Self {
        Self::Internal(InternalError::new(error))
    }

    pub fn bad_request(
        error: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
    ) -> Self {
        Self::BadRequest(BadRequest::new(error))
    }

    pub fn handle_no_table_as_collection_not_found(
        collection: impl Into<String>,
        err: datafusion::error::DataFusionError,
    ) -> Self {
        match err {
            datafusion::error::DataFusionError::Plan(e) if e.contains("No table named") => {
                Self::CollectionNotFound(CollectionNotFound::new(collection))
            }
            _ => Self::internal(err),
        }
    }
}

impl axum::response::IntoResponse for ODataError {
    fn into_response(self) -> axum::response::Response {
        match self {
            Self::Internal(_) | Self::FromUtf8Error(_) => {
                (http::StatusCode::INTERNAL_SERVER_ERROR, "Internal error").into_response()
            }
            Self::BadRequest(e) => e.into_response(),
            Self::CollectionNotFound(e) => e.into_response(),
            Self::UnsupportedDataType(e) => e.into_response(),
            Self::UnsupportedFeature(e) => e.into_response(),
            Self::CollectionAddressNotAssigned(e) => e.into_response(),
            Self::KeyColumnNotAssigned(e) => e.into_response(),
            Self::UnsupportedNetProtocol(e) => e.into_response(),
        }
    }
}

///////////////////////////////////////////////////////////////////////////////

#[derive(thiserror::Error, Debug)]
#[error("Internal error")]
pub struct InternalError {
    #[source]
    pub source: Box<dyn std::error::Error + Send + Sync + 'static>,
}

impl InternalError {
    pub fn new(error: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>) -> Self {
        Self {
            source: error.into(),
        }
    }
}

///////////////////////////////////////////////////////////////////////////////

#[derive(thiserror::Error, Debug)]
#[error("{source}")]
pub struct BadRequest {
    #[source]
    pub source: Box<dyn std::error::Error + Send + Sync + 'static>,
}

impl BadRequest {
    pub fn new(error: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>) -> Self {
        Self {
            source: error.into(),
        }
    }
}

impl axum::response::IntoResponse for BadRequest {
    fn into_response(self) -> axum::response::Response {
        (http::StatusCode::BAD_REQUEST, self.to_string()).into_response()
    }
}

///////////////////////////////////////////////////////////////////////////////

#[derive(thiserror::Error, Debug)]
#[error("Collection {collection} not found")]
pub struct CollectionNotFound {
    pub collection: String,
}

impl CollectionNotFound {
    pub fn new(collection: impl Into<String>) -> Self {
        Self {
            collection: collection.into(),
        }
    }
}

impl axum::response::IntoResponse for CollectionNotFound {
    fn into_response(self) -> axum::response::Response {
        (http::StatusCode::NOT_FOUND, self.to_string()).into_response()
    }
}

///////////////////////////////////////////////////////////////////////////////

#[derive(thiserror::Error, Debug)]
#[error("Key column not assigned")]
pub struct KeyColumnNotAssigned;

impl axum::response::IntoResponse for KeyColumnNotAssigned {
    fn into_response(self) -> axum::response::Response {
        (http::StatusCode::NOT_IMPLEMENTED, self.to_string()).into_response()
    }
}

///////////////////////////////////////////////////////////////////////////////

#[derive(thiserror::Error, Debug)]
#[error("Collection address not assigned")]
pub struct CollectionAddressNotAssigned;

impl axum::response::IntoResponse for CollectionAddressNotAssigned {
    fn into_response(self) -> axum::response::Response {
        (http::StatusCode::NOT_IMPLEMENTED, self.to_string()).into_response()
    }
}

///////////////////////////////////////////////////////////////////////////////

#[derive(thiserror::Error, Debug)]
#[error("Unsupported data type: {data_type}")]
pub struct UnsupportedDataType {
    pub data_type: DataType,
}

impl UnsupportedDataType {
    pub fn new(data_type: DataType) -> Self {
        Self { data_type }
    }
}

impl axum::response::IntoResponse for UnsupportedDataType {
    fn into_response(self) -> axum::response::Response {
        (http::StatusCode::NOT_IMPLEMENTED, self.to_string()).into_response()
    }
}

///////////////////////////////////////////////////////////////////////////////

#[derive(thiserror::Error, Debug)]
#[error("Unsupported net protocol: {url}")]
pub struct UnsupportedNetProtocol {
    pub url: String,
}

impl UnsupportedNetProtocol {
    pub fn new(url: String) -> Self {
        Self { url }
    }
}

impl axum::response::IntoResponse for UnsupportedNetProtocol {
    fn into_response(self) -> axum::response::Response {
        (http::StatusCode::NOT_IMPLEMENTED, self.to_string()).into_response()
    }
}

///////////////////////////////////////////////////////////////////////////////

#[derive(thiserror::Error, Debug)]
#[error("Unsupported feature: {feature}")]
pub struct UnsupportedFeature {
    pub feature: String,
}

impl UnsupportedFeature {
    pub fn new(feature: impl Into<String>) -> Self {
        Self {
            feature: feature.into(),
        }
    }
}

impl axum::response::IntoResponse for UnsupportedFeature {
    fn into_response(self) -> axum::response::Response {
        (http::StatusCode::NOT_IMPLEMENTED, self.to_string()).into_response()
    }
}

///////////////////////////////////////////////////////////////////////////////

impl From<quick_xml::Error> for ODataError {
    fn from(error: quick_xml::Error) -> Self {
        ODataError::Internal(InternalError::new(error))
    }
}

impl From<quick_xml::encoding::EncodingError> for ODataError {
    fn from(error: quick_xml::encoding::EncodingError) -> Self {
        ODataError::Internal(InternalError::new(error))
    }
}

impl From<std::io::Error> for ODataError {
    fn from(error: std::io::Error) -> Self {
        ODataError::Internal(InternalError::new(error))
    }
}