hodgepodge 0.3.0

Lightweight dataset crate of enums for prototyping, teaching, and experimentation
Documentation
use hodgepodge::*;
use std::{fmt::Debug, hash::Hash, str::FromStr};

macro_rules! all_datasets {
    ($check:ident) => {
        $check::<ROYGBIV>();
        $check::<CMYK>();
        $check::<RGB>();
        $check::<CSS>();
        $check::<Unit>();
        $check::<TaxonomicRank>();
        $check::<PrefixLarge>();
        $check::<PrefixSmall>();
        $check::<Planet>();
        $check::<Element>();
        $check::<Direction>();
        $check::<Continent>();
        $check::<EU>();
        $check::<States>();
        $check::<Ocean>();
        $check::<CanadianProvince>();
        $check::<Country>();
        $check::<Day>();
        $check::<Month>();
        $check::<Season>();
        $check::<Quarter>();
        $check::<Suit>();
        $check::<Rank>();
        $check::<DiceFace>();
        $check::<ChessPiece>();
        $check::<BetterThanRust>();
        $check::<Medal>();
        $check::<Ordinal>();
    };
}

#[test]
fn every_dataset_supports_the_default_api() {
    fn check<
        T: Copy + Clone + Debug + Eq + Hash + std::fmt::Display + FromStr<Err = ParseEnumError>,
    >() {
    }
    all_datasets!(check);
}

#[test]
fn names_are_available_without_optional_features() {
    const SEPTEMBER: &str = Month::September.as_str();
    assert_eq!(SEPTEMBER, "September");
    assert_eq!("sEpTeMbEr".parse::<Month>(), Ok(Month::September));
    assert_eq!(Month::September.to_string(), SEPTEMBER);
    let mut counts = std::collections::HashMap::new();
    counts.insert(Month::September, 3);
    assert_eq!(counts.get(&"september".parse::<Month>().unwrap()), Some(&3));
}

#[test]
fn invalid_names_return_a_typed_error() {
    for input in ["", " September", "September ", "Sep", "9", "Séptember"] {
        let error = input.parse::<Month>().unwrap_err();
        assert_eq!(error.enum_name(), "Month");
        assert_eq!(error.to_string(), "unknown Month variant");
        assert!(std::error::Error::source(&error).is_none());
    }
    assert!("North America".parse::<Continent>().is_err());
    assert_eq!(
        "northamerica".parse::<Continent>(),
        Ok(Continent::NorthAmerica)
    );
}

#[cfg(feature = "strum")]
#[test]
fn every_variant_round_trips_through_its_name() {
    fn check<T>()
    where
        T: Copy
            + Debug
            + Eq
            + Hash
            + std::fmt::Display
            + FromStr<Err = ParseEnumError>
            + IntoEnumIterator
            + EnumCount,
    {
        let mut seen = std::collections::HashSet::new();
        for value in T::iter() {
            let name = value.to_string();
            assert_eq!(name.parse::<T>(), Ok(value));
            assert_eq!(name.to_ascii_lowercase().parse::<T>(), Ok(value));
            assert_eq!(name.to_ascii_uppercase().parse::<T>(), Ok(value));
            assert!(seen.insert(value), "duplicate variant: {name}");
        }
        assert_eq!(seen.len(), T::COUNT);
    }
    all_datasets!(check);
}

#[cfg(all(feature = "serde", feature = "strum"))]
#[test]
fn every_variant_round_trips_through_json() {
    fn check<T>()
    where
        T: Debug
            + Eq
            + std::fmt::Display
            + IntoEnumIterator
            + serde::Serialize
            + serde::de::DeserializeOwned,
    {
        for value in T::iter() {
            let json = serde_json::to_string(&value).unwrap();
            assert_eq!(json, format!("\"{value}\""));
            assert_eq!(serde_json::from_str::<T>(&json).unwrap(), value);
        }
    }
    all_datasets!(check);
}