// License: see LICENSE file at root directory of `master` branch

//! # Case-insensitive string

use alloc::{
    borrow::Cow,
    string::String,
};

use core::{
    cmp::Ordering,
    convert::TryFrom,
    fmt,
    hash::{Hash, Hasher},
    str::FromStr,
};

use crate::Error;

/// # Case-insensitive string
///
/// ## Notes
///
/// - This struct is intended for sorting in lists, or to be used in sets, so it _generates_ a lower-case form of source string and stores it
///   inside. That might look like a waste of memory, but it helps with performance. For example when you call [`Vec::sort()`][r://Vec/sort()]
///   or when you insert it into a [`HashSet`][r://HashSet], it doesn't have to generate any string again and again...
/// - Implementations of `From<&'a str>`, `From<&'a String>` and `From<String>` either borrow or take the source string and store it inside.
/// - Implementation of `FromStr` will _clone_ the source strings.
///
/// ## Examples
///
/// ```
/// use std::collections::HashSet;
/// use sub_strs::CainStr;
///
/// let wild_data: HashSet<_> = vec!["swift", "C++", "rust", "SWIFT"].into_iter().collect();
/// assert_eq!(wild_data.len(), 4);
///
/// let data: HashSet<_> = wild_data.into_iter().map(|s| CainStr::from(s)).collect();
/// assert_eq!(data.len(), 3);
///
/// let mut data: Vec<_> = data.into_iter().collect();
/// data.sort();
/// let data: Vec<_> = data.iter().map(|cs| cs.as_ref()).collect();
/// assert_eq!(&data[..2], &["C++", "rust"]);
/// assert!(data[2].eq_ignore_ascii_case("swift"));
/// ```
///
/// [r://HashSet]: https://doc.rust-lang.org/std/collections/struct.HashSet.html
/// [r://Vec/sort()]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.sort
#[derive(Debug, Eq)]
pub struct CainStr<'a> {

    /// # Source string
    src: Cow<'a, str>,

    /// # Lower-case form of source string
    lowercase: String,

}

impl CainStr<'_> {

    /// # Gets the lowercase form of source string
    pub fn lowercase(&self) -> &str {
        &self.lowercase
    }

    /// # Converts self into the lowercase form of source string
    ///
    /// This function simply takes the inner field out. There is no allocation.
    pub fn into_lowercase(self) -> String {
        self.lowercase
    }

}

impl PartialEq for CainStr<'_> {

    fn eq(&self, other: &Self) -> bool {
        self.lowercase.eq(&other.lowercase)
    }

}

impl Ord for CainStr<'_> {

    fn cmp(&self, other: &Self) -> Ordering {
        self.lowercase.cmp(&other.lowercase)
    }

}

impl PartialOrd for CainStr<'_> {

    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }

}

impl Hash for CainStr<'_> {

    fn hash<H>(&self, h: &mut H) where H: Hasher {
        self.lowercase.hash(h);
    }

}

impl fmt::Display for CainStr<'_> {

    fn fmt(&self, f: &mut fmt::Formatter) -> core::result::Result<(), fmt::Error> {
        f.write_str(&self.src)
    }

}

impl<'a> From<&'a str> for CainStr<'a> {

    fn from(src: &'a str) -> Self {
        Self {
            src: src.into(),
            lowercase: src.to_lowercase(),
        }
    }

}

impl<'a> From<&'a String> for CainStr<'a> {

    fn from(src: &'a String) -> Self {
        Self {
            src: src.into(),
            lowercase: src.to_lowercase(),
        }
    }

}

impl From<String> for CainStr<'_> {

    fn from(src: String) -> Self {
        let lowercase = src.to_lowercase();
        Self {
            src: src.into(),
            lowercase,
        }
    }

}

impl<'a> TryFrom<CainStr<'a>> for &'a str {

    type Error = Error;

    fn try_from(cain_str: CainStr<'a>) -> core::result::Result<Self, Self::Error> {
        match cain_str.src {
            Cow::Borrowed(s) => Ok(s),
            Cow::Owned(_) => Err(err!("CainStr is borrowed, not owned")),
        }
    }

}

impl<'a> TryFrom<CainStr<'a>> for String {

    type Error = Error;

    fn try_from(cain_str: CainStr<'a>) -> core::result::Result<Self, Self::Error> {
        match cain_str.src {
            Cow::Owned(s) => Ok(s),
            Cow::Borrowed(_) => Err(err!("CainStr is owned, not borrowed")),
        }
    }

}

impl FromStr for CainStr<'_> {

    type Err = Error;

    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
        Ok(Self::from(String::from(s)))
    }

}

impl AsRef<str> for CainStr<'_> {

    fn as_ref(&self) -> &str {
        &self.src
    }

}

#[test]
fn test_cain_str() {
    let s = "UPPER-CASE";
    assert_eq!(s.to_lowercase(), CainStr::from(s).lowercase);
}
