x-pipe-rs 0.1.0

Composable recommendation/feed pipeline framework built on comp-cat-rs
Documentation
//! Pipeline error types.
//!
//! Each pipeline stage variant has its own error struct, collected
//! under the top-level [`PipelineError`] enum.  All error types
//! implement [`Display`] and [`std::error::Error`] by hand.

use std::fmt;

/// Name identifying a candidate source.
#[derive(Debug, Clone)]
pub struct SourceName(String);

impl SourceName {
    /// Create a new source name.
    #[must_use]
    pub fn new(name: impl Into<String>) -> Self {
        Self(name.into())
    }

    /// The name as a string slice.
    #[must_use]
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for SourceName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// Error from a candidate source.
#[derive(Debug)]
pub struct SourceError {
    source_name: SourceName,
    detail: String,
}

impl SourceError {
    /// Create a new source error.
    #[must_use]
    pub fn new(source_name: SourceName, detail: impl Into<String>) -> Self {
        Self {
            source_name,
            detail: detail.into(),
        }
    }

    /// Which source failed.
    #[must_use]
    pub fn source_name(&self) -> &SourceName {
        &self.source_name
    }

    /// Detail message.
    #[must_use]
    pub fn detail(&self) -> &str {
        &self.detail
    }
}

impl fmt::Display for SourceError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "source '{}': {}", self.source_name, self.detail)
    }
}

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

/// Error from a hydration stage.
#[derive(Debug)]
pub struct HydrationError {
    detail: String,
}

impl HydrationError {
    /// Create a new hydration error.
    #[must_use]
    pub fn new(detail: impl Into<String>) -> Self {
        Self {
            detail: detail.into(),
        }
    }

    /// Detail message.
    #[must_use]
    pub fn detail(&self) -> &str {
        &self.detail
    }
}

impl fmt::Display for HydrationError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "hydration: {}", self.detail)
    }
}

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

/// Error from a filter stage.
#[derive(Debug)]
pub struct FilterError {
    detail: String,
}

impl FilterError {
    /// Create a new filter error.
    #[must_use]
    pub fn new(detail: impl Into<String>) -> Self {
        Self {
            detail: detail.into(),
        }
    }

    /// Detail message.
    #[must_use]
    pub fn detail(&self) -> &str {
        &self.detail
    }
}

impl fmt::Display for FilterError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "filter: {}", self.detail)
    }
}

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

/// Error from a scoring stage.
#[derive(Debug)]
pub struct ScoringError {
    detail: String,
}

impl ScoringError {
    /// Create a new scoring error.
    #[must_use]
    pub fn new(detail: impl Into<String>) -> Self {
        Self {
            detail: detail.into(),
        }
    }

    /// Detail message.
    #[must_use]
    pub fn detail(&self) -> &str {
        &self.detail
    }
}

impl fmt::Display for ScoringError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "scoring: {}", self.detail)
    }
}

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

/// Error from a selection stage.
#[derive(Debug)]
pub struct SelectionError {
    detail: String,
}

impl SelectionError {
    /// Create a new selection error.
    #[must_use]
    pub fn new(detail: impl Into<String>) -> Self {
        Self {
            detail: detail.into(),
        }
    }

    /// Detail message.
    #[must_use]
    pub fn detail(&self) -> &str {
        &self.detail
    }
}

impl fmt::Display for SelectionError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "selection: {}", self.detail)
    }
}

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

/// Error from a side-effect stage.
#[derive(Debug)]
pub struct SideEffectError {
    detail: String,
}

impl SideEffectError {
    /// Create a new side-effect error.
    #[must_use]
    pub fn new(detail: impl Into<String>) -> Self {
        Self {
            detail: detail.into(),
        }
    }

    /// Detail message.
    #[must_use]
    pub fn detail(&self) -> &str {
        &self.detail
    }
}

impl fmt::Display for SideEffectError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "side effect: {}", self.detail)
    }
}

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

/// Top-level pipeline error.
///
/// Each variant wraps the error type from a specific stage.
#[derive(Debug)]
pub enum PipelineError {
    /// A candidate source failed.
    Source(SourceError),
    /// Hydration failed.
    Hydration(HydrationError),
    /// A filter failed.
    Filter(FilterError),
    /// Scoring failed.
    Scoring(ScoringError),
    /// Selection failed.
    Selection(SelectionError),
    /// A side effect failed.
    SideEffect(SideEffectError),
}

impl fmt::Display for PipelineError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Source(e) => write!(f, "pipeline error: {e}"),
            Self::Hydration(e) => write!(f, "pipeline error: {e}"),
            Self::Filter(e) => write!(f, "pipeline error: {e}"),
            Self::Scoring(e) => write!(f, "pipeline error: {e}"),
            Self::Selection(e) => write!(f, "pipeline error: {e}"),
            Self::SideEffect(e) => write!(f, "pipeline error: {e}"),
        }
    }
}

impl std::error::Error for PipelineError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Self::Source(e) => Some(e),
            Self::Hydration(e) => Some(e),
            Self::Filter(e) => Some(e),
            Self::Scoring(e) => Some(e),
            Self::Selection(e) => Some(e),
            Self::SideEffect(e) => Some(e),
        }
    }
}

impl From<SourceError> for PipelineError {
    fn from(e: SourceError) -> Self {
        Self::Source(e)
    }
}

impl From<HydrationError> for PipelineError {
    fn from(e: HydrationError) -> Self {
        Self::Hydration(e)
    }
}

impl From<FilterError> for PipelineError {
    fn from(e: FilterError) -> Self {
        Self::Filter(e)
    }
}

impl From<ScoringError> for PipelineError {
    fn from(e: ScoringError) -> Self {
        Self::Scoring(e)
    }
}

impl From<SelectionError> for PipelineError {
    fn from(e: SelectionError) -> Self {
        Self::Selection(e)
    }
}

impl From<SideEffectError> for PipelineError {
    fn from(e: SideEffectError) -> Self {
        Self::SideEffect(e)
    }
}