mod collection_pattern;

pub use collection_pattern::CollectionPattern;

use crate::assertions::collection::*;
use crate::core::assert::ChainableAssert;
use std::collections::{LinkedList, VecDeque};
use std::fmt::Debug;

/// Adds the assertions to the collections.
pub trait ShouldCollection<C: Debug>
where
    C: Collection,
{
    /// Checks that a collection contains a pattern:
    ///
    /// # Example
    ///
    /// ```rust
    /// # use fluid::prelude::*;
    /// let v = vec![1, 2, 3];
    ///
    /// v.should().contain(3).and_should().not().contain(0);
    /// ```
    fn contain<P>(self, right: P) -> ChainableAssert<ContainCollection<C, P>>
    where
        P: CollectionPattern<C>;

    /// Checks that a collection is empty:
    ///
    /// # Example
    ///
    /// ```rust
    /// # use fluid::prelude::*;
    /// let v = vec![1, 2, 3];
    ///
    /// v.should().not().be_empty();
    /// ```
    fn be_empty(self) -> ChainableAssert<BeEmptyCollection<C>>;
}

/// Abstracts a collection, like `Vec`, `VecDeque`, `LinkedList`, etc.
pub trait Collection {
    /// The type of the items yielded by the collection.
    type Item: PartialEq;

    /// Returns whether the collection is empty or not.
    fn is_empty(&self) -> bool;

    /// Verifies if the collection contains an item.
    fn contains(&self, item: &Self::Item) -> bool;
}

impl<T> Collection for Vec<T>
where
    T: PartialEq<T>,
{
    type Item = T;

    fn is_empty(&self) -> bool {
        self.is_empty()
    }

    fn contains(&self, item: &Self::Item) -> bool {
        (self as &[_]).contains(item)
    }
}

impl<T> Collection for VecDeque<T>
where
    T: PartialEq<T>,
{
    type Item = T;

    fn is_empty(&self) -> bool {
        self.is_empty()
    }

    fn contains(&self, item: &Self::Item) -> bool {
        VecDeque::contains(self, item)
    }
}

impl<T> Collection for LinkedList<T>
where
    T: PartialEq<T>,
{
    type Item = T;

    fn is_empty(&self) -> bool {
        self.is_empty()
    }

    fn contains(&self, item: &Self::Item) -> bool {
        LinkedList::contains(self, item)
    }
}
