rjango 0.1.1

A full-stack Rust backend framework inspired by Django
Documentation
use std::collections::{HashMap, hash_map::DefaultHasher};
use std::fmt::Display;
use std::hash::{Hash, Hasher};

/// Convert a value to a hashable representation.
/// Handles converting unhashable types (like Vec, HashMap) to deterministic hash keys.
#[must_use]
pub fn make_hashable<T: Hash>(value: &T) -> u64 {
    let mut hasher = DefaultHasher::new();
    value.hash(&mut hasher);
    hasher.finish()
}

/// Make a slice of items hashable by hashing each element.
#[must_use]
pub fn make_hashable_slice<T: Hash>(items: &[T]) -> u64 {
    let mut hasher = DefaultHasher::new();
    for item in items {
        item.hash(&mut hasher);
    }
    hasher.finish()
}

/// Make a HashMap hashable by sorting keys and hashing pairs.
#[must_use]
pub fn make_hashable_map(map: &HashMap<String, String>) -> u64 {
    let mut pairs = map.iter().collect::<Vec<_>>();
    pairs.sort_unstable_by(|(left_key, _), (right_key, _)| left_key.cmp(right_key));

    let mut hasher = DefaultHasher::new();
    for (key, value) in pairs {
        key.hash(&mut hasher);
        value.hash(&mut hasher);
    }
    hasher.finish()
}

/// Wrapper that makes any Display type hashable via its string representation.
#[derive(Debug, Clone)]
pub struct HashableDisplay<T: Display>(pub T);

impl<T: Display> Hash for HashableDisplay<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.0.to_string().hash(state);
    }
}

impl<T: Display> PartialEq for HashableDisplay<T> {
    fn eq(&self, other: &Self) -> bool {
        self.0.to_string() == other.0.to_string()
    }
}

impl<T: Display> Eq for HashableDisplay<T> {}

#[cfg(test)]
mod tests {
    use super::{HashableDisplay, make_hashable, make_hashable_map, make_hashable_slice};
    use std::collections::HashMap;

    #[test]
    fn test_make_hashable_deterministic() {
        let value = vec!["alpha", "beta", "gamma"];

        assert_eq!(make_hashable(&value), make_hashable(&value));
    }

    #[test]
    fn test_make_hashable_slice() {
        let items = [1_u32, 2, 3, 4];
        let reordered = [4_u32, 3, 2, 1];

        assert_eq!(make_hashable_slice(&items), make_hashable_slice(&items));
        assert_ne!(make_hashable_slice(&items), make_hashable_slice(&reordered));
    }

    #[test]
    fn test_make_hashable_map() {
        let mut first = HashMap::new();
        first.insert("beta".to_string(), "2".to_string());
        first.insert("alpha".to_string(), "1".to_string());

        let mut second = HashMap::new();
        second.insert("alpha".to_string(), "1".to_string());
        second.insert("beta".to_string(), "2".to_string());

        assert_eq!(make_hashable_map(&first), make_hashable_map(&second));
    }

    #[test]
    fn test_hashable_display_eq() {
        assert_eq!(HashableDisplay(42), HashableDisplay(42));
        assert_ne!(HashableDisplay("42"), HashableDisplay("0042"));
    }
}