atelier_data 0.0.15

Data Artifacts and I/O for the atelier-rs engine
use thiserror::Error;

/// Errors arising from WebSocket connection lifecycle.
#[derive(Error, Debug, Clone)]
pub enum WssError {
    #[error("Connection Failed")]
    WssConnection,
}

/// Errors from exchange client operations (WebSocket, HTTP, config).
#[derive(Error, Debug)]
pub enum ExchangeError {
    #[error("WebSocket connection error: {0}")]
    WebSocketError(Box<tokio_tungstenite::tungstenite::Error>),

    #[error("URL parsing error: {0}")]
    UrlParseError(#[from] url::ParseError),

    #[error("JSON deserialization error: {0}")]
    JsonError(#[from] serde_json::Error),

    #[error("Channel send error")]
    ChannelSendError,

    #[error("An IO error occurred: {0}")]
    IoError(#[from] std::io::Error),
}

impl From<tokio_tungstenite::tungstenite::Error> for ExchangeError {
    fn from(error: tokio_tungstenite::tungstenite::Error) -> Self {
        ExchangeError::WebSocketError(Box::new(error))
    }
}

/// Errors from order-book level operations (find, insert, delete, modify).
#[derive(Error, Debug, Clone)]
pub enum LevelError {
    #[error("Level not found")]
    LevelNotFound,

    #[error("Level info not available")]
    LevelInfoNotAvailable,

    #[error("Level deletion not successful")]
    LevelDeletionFailed,

    #[error("Level modification not successful")]
    LevelModificationFailed,

    #[error("Level insertion not successful")]
    LevelInsertionFailed,
}

/// Errors from order operations within a level.
#[derive(Error, Debug)]
pub enum OrderError {
    #[error("Order not found")]
    OrderNotFound,

    #[error("Order info not available")]
    OrderInfoNotAvailable,

    #[error("Order deletion not successful")]
    OrderDeletionFailed,

    #[error("Order modification not successful")]
    OrderModificationFailed,

    #[error("Order insertion not successful")]
    OrderInsertionFailed,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OrderbookError {
    /// Received delta before snapshot
    NotInitialized,
    /// Update ID gap detected (missed messages)
    SequenceGap { expected: u64, received: u64 },
    /// Symbol mismatch
    SymbolMismatch { expected: String, received: String },
    /// Failed to parse price or size
    ParseError(String),
    /// Operation on an empty Orderbook
    ContentsError(String),
}

impl std::fmt::Display for OrderbookError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::NotInitialized => {
                write!(f, "Orderbook not initialized, waiting for snapshot")
            }
            Self::SequenceGap { expected, received } => {
                write!(
                    f,
                    "Sequence gap: expected {}, received {}",
                    expected, received
                )
            }
            Self::SymbolMismatch { expected, received } => {
                write!(
                    f,
                    "Symbol mismatch: expected {}, received {}",
                    expected, received
                )
            }
            Self::ParseError(msg) => write!(f, "Parse error: {}", msg),

            Self::ContentsError(msg) => write!(f, "Orderbook contents error: {}", msg),
        }
    }
}

impl std::error::Error for OrderbookError {}

/// Errors from persistence operations
#[derive(Debug)]
pub enum PersistError {
    Io(std::io::Error),
    Json(serde_json::Error),
    #[cfg(feature = "parquet")]
    Parquet(parquet::errors::ParquetError),
    Parse(String),
    #[cfg(feature = "parquet")]
    Arrow(arrow::error::ArrowError),
    UnsupportedFormat(String),
}

impl std::fmt::Display for PersistError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Io(e) => write!(f, "IO error: {}", e),
            Self::Json(e) => write!(f, "JSON error: {}", e),
            #[cfg(feature = "parquet")]
            Self::Parquet(e) => write!(f, "Parquet error: {}", e),
            #[cfg(feature = "parquet")]
            Self::Arrow(e) => write!(f, "Arrow error: {}", e),
            Self::UnsupportedFormat(s) => write!(f, "Unsupported format: {}", s),
            PersistError::Parse(e) => write!(f, "Parse error: {}", e),
        }
    }
}

impl std::error::Error for PersistError {}

impl From<std::io::Error> for PersistError {
    fn from(e: std::io::Error) -> Self {
        Self::Io(e)
    }
}

impl From<serde_json::Error> for PersistError {
    fn from(e: serde_json::Error) -> Self {
        Self::Json(e)
    }
}

#[cfg(feature = "parquet")]
impl From<parquet::errors::ParquetError> for PersistError {
    fn from(e: parquet::errors::ParquetError) -> Self {
        Self::Parquet(e)
    }
}

#[cfg(feature = "parquet")]
impl From<arrow::error::ArrowError> for PersistError {
    fn from(e: arrow::error::ArrowError) -> Self {
        Self::Arrow(e)
    }
}

#[derive(Error, Debug)]
pub enum LoaderError {
    /// No timestamps were provided
    #[error("Empty data: no timestamps to process")]
    EmptyData,

    /// File system or parquet I/O error
    #[error("I/O error: {0}")]
    IoError(String),
}

#[derive(Error, Debug)]
pub enum TemporalError {
    /// No timestamps were provided
    #[error("Empty data: no timestamps to process")]
    EmptyData,

    /// Timestamps are not strictly monotonically increasing
    #[error("Non-monotonic timestamps at index {index}: prev={prev}, curr={curr}")]
    NonMonotonic { index: usize, prev: u64, curr: u64 },

    /// Gap in timestamps exceeding the configured threshold
    #[error("Gap detected at index {index}: gap={gap_ns}ns > threshold={threshold_ns}ns")]
    GapDetected {
        index: usize,
        gap_ns: u64,
        threshold_ns: u64,
    },

    /// Insufficient data for the requested computation
    #[error("Insufficient data: {0}")]
    InsufficientData(String),
}