/// See the method documentation.
pub trait StringPattern {
    /// Checks if a string matches the pattern.
    fn matches(&self, s: &str) -> bool;

    /// Checks if a string matches the pattern.
    fn matches_start(&self, s: &str) -> bool;

    /// Checks if a string matches the pattern.
    fn matches_end(&self, s: &str) -> bool;

    /// Displays the pattern.
    fn display(&self) -> String;
}

// FIXME: replace this with a const generic when it is available.
macro_rules! impl_array {
    ( $n:expr ) => {
        impl StringPattern for [char; $n] {
            fn matches(&self, s: &str) -> bool {
                self.iter().any(|c| s.contains(*c))
            }

            fn matches_start(&self, s: &str) -> bool {
                self.iter().any(|c| s.chars().next() == Some(*c))
            }

            fn matches_end(&self, s: &str) -> bool {
                self.iter().any(|c| s.chars().last() == Some(*c))
            }

            fn display(&self) -> String {
                format!("{:?}", self)
            }
        }
    };
}

impl_array!(2);
impl_array!(3);
impl_array!(4);
impl_array!(5);
impl_array!(6);
impl_array!(7);
impl_array!(8);
impl_array!(9);
impl_array!(10);
impl_array!(11);
impl_array!(12);
impl_array!(13);
impl_array!(14);
impl_array!(15);
impl_array!(16);

impl StringPattern for char {
    fn matches(&self, s: &str) -> bool {
        s.contains(*self)
    }

    fn matches_start(&self, s: &str) -> bool {
        s.chars().next() == Some(*self)
    }

    fn matches_end(&self, s: &str) -> bool {
        s.chars().last() == Some(*self)
    }

    fn display(&self) -> String {
        self.to_string()
    }
}

impl<'s> StringPattern for &'s str {
    fn matches(&self, s: &str) -> bool {
        s.contains(self)
    }

    fn matches_start(&self, s: &str) -> bool {
        s.starts_with(self)
    }

    fn matches_end(&self, s: &str) -> bool {
        s.ends_with(self)
    }

    fn display(&self) -> String {
        self.to_string()
    }
}

impl<'s> StringPattern for &'s [char] {
    fn matches(&self, s: &str) -> bool {
        self.iter().any(|c| s.contains(*c))
    }

    fn matches_start(&self, s: &str) -> bool {
        self.iter().any(|c| s.chars().next() == Some(*c))
    }

    fn matches_end(&self, s: &str) -> bool {
        self.iter().any(|c| s.chars().next() == Some(*c))
    }

    fn display(&self) -> String {
        format!("{:?}", self)
    }
}

impl<F> StringPattern for F
where
    F: Fn(char) -> bool,
{
    fn matches(&self, s: &str) -> bool {
        s.chars().any(self)
    }

    fn matches_start(&self, s: &str) -> bool {
        s.chars().next().map(self).unwrap_or(false)
    }

    fn matches_end(&self, s: &str) -> bool {
        s.chars().last().map(self).unwrap_or(false)
    }

    fn display(&self) -> String {
        "<Fn(char) -> bool>".into()
    }
}
