sub-strs 0.29.2

For finding sub strings...
Documentation
/*
==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--

sub-strs

Copyright (C) 2019-2023  Anonymous

There are several releases over multiple years,
they are listed as ranges, such as: "2019-2023".

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.

::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
*/

//! # Case-insensitive string

use {
    alloc::{
        borrow::Cow,
        string::{String, ToString},
    },
    core::{
        cmp::Ordering,
        fmt,
        hash::{Hash, Hasher},
        str::FromStr,
    },
    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 Clone for CainStr<'_> {

    fn clone(&self) -> Self {
        Self {
            src: self.src.clone().into_owned().into(),
            lowercase: self.lowercase.to_string(),
        }
    }

}

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<Cow<'a, str>> for CainStr<'a> {

    fn from(src: Cow<'a, str>) -> Self {
        let lowercase = src.to_lowercase();
        Self {
            src,
            lowercase,
        }
    }

}

impl<'a> From<CainStr<'a>> for Cow<'a, str> {

    fn from(src: CainStr<'a>) -> Self {
        src.src
    }

}

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

    fn from(src: &'a str) -> 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 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);
}