use std::{collections::HashSet, hash::Hash};

pub trait ToSnakeCase {
    fn to_snake_case(&self) -> String;
}

impl ToSnakeCase for String {
    fn to_snake_case(&self) -> String {
        let mut result: String = "".to_string();
        let mut uppercase_before = true;
        let mut underscore_before = true;
        for char in self.chars() {
            let is_alpha_numeric = char.is_ascii_alphanumeric();
            let is_uppercase = char.is_uppercase();
            if (is_uppercase && !uppercase_before || !is_alpha_numeric) && !underscore_before {
                result.push('_');
                underscore_before = true;
            }
            if is_alpha_numeric {
                result.push(char.to_ascii_lowercase());
                underscore_before = false;
            }
            uppercase_before = is_uppercase
        }
        if result.ends_with('_') {
            result.pop();
        }
        result
    }
}

pub fn has_unique_elements<T>(iter: T) -> bool
where
    T: IntoIterator,
    T::Item: Eq + Hash,
{
    let mut uniq = HashSet::new();
    iter.into_iter().all(move |x| uniq.insert(x))
}
