use super::cause::*;

use std::{cmp::*, error::*};

//
// CauseComparison
//

/// Cause comparison attachment.
pub struct CauseComparison(pub Box<dyn Fn(&Cause, &Cause) -> Option<Ordering> + Send + Sync>);

impl CauseComparison {
    /// Constructor.
    ///
    /// The cause's error type must implement [PartialOrd].
    pub fn new<ErrorT>() -> Self
    where
        ErrorT: 'static + Error + PartialOrd,
    {
        Self(Box::new(Self::compare_::<ErrorT>))
    }

    /// Compare causes.
    pub fn compare(&self, left: &Cause, right: &Cause) -> Option<Ordering> {
        self.0(left, right)
    }

    fn compare_<ErrorT>(left: &Cause, right: &Cause) -> Option<Ordering>
    where
        ErrorT: 'static + Error + PartialOrd,
    {
        if let Some(left) = left.error.downcast_ref::<ErrorT>()
            && let Some(right) = right.error.downcast_ref::<ErrorT>()
        {
            left.partial_cmp(right)
        } else {
            None
        }
    }
}
