use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Domain {
Mathematics,
Physics,
Chemistry,
Biology,
EarthScience,
Astronomy,
Statistics,
Computing,
Psychology,
Sociology,
Medicine,
Survival,
Agriculture,
Construction,
Repair,
Communication,
Encyclopedia,
Language,
Literature,
Geography,
}
impl Domain {
#[must_use]
pub const fn display_name(&self) -> &'static str {
match self {
Self::Mathematics => "Mathematics",
Self::Physics => "Physics",
Self::Chemistry => "Chemistry",
Self::Biology => "Biology",
Self::EarthScience => "Earth Science",
Self::Astronomy => "Astronomy",
Self::Statistics => "Statistics",
Self::Computing => "Computing",
Self::Psychology => "Psychology",
Self::Sociology => "Sociology",
Self::Medicine => "Medicine",
Self::Survival => "Survival",
Self::Agriculture => "Agriculture",
Self::Construction => "Construction",
Self::Repair => "Repair",
Self::Communication => "Communication",
Self::Encyclopedia => "Encyclopedia",
Self::Language => "Language",
Self::Literature => "Literature",
Self::Geography => "Geography",
}
}
#[must_use]
pub const fn description(&self) -> &'static str {
match self {
Self::Mathematics => "Algebra, calculus, geometry, number theory",
Self::Physics => "Mechanics, optics, electromagnetism, thermodynamics",
Self::Chemistry => "Elements, reactions, kinetics, materials",
Self::Biology => "Microbiology, ethology, botany, physiology",
Self::EarthScience => "Geology, mineralogy, weather, atmospheric science",
Self::Astronomy => "Orbital mechanics, stellar physics, celestial computation",
Self::Statistics => "Probability, distributions, inference, data analysis",
Self::Computing => "Programming, algorithms, systems, best practices",
Self::Psychology => "Cognition, perception, learning, decision-making",
Self::Sociology => "Social networks, game theory, group dynamics",
Self::Medicine => "Diagnosis, treatment, first aid, field medicine",
Self::Survival => "Shelter, water, food, navigation, emergency response",
Self::Agriculture => "Farming, gardening, permaculture, aquaponics",
Self::Construction => "Building, plumbing, electrical, solar, earthworks",
Self::Repair => "Electronics, appliances, vehicles, maintenance",
Self::Communication => "Radio, signals, emergency communications",
Self::Encyclopedia => "General knowledge and reference",
Self::Language => "Dictionaries, grammar, translation",
Self::Literature => "Books, philosophy, history",
Self::Geography => "Maps, travel, cultures, world facts",
}
}
#[must_use]
pub const fn agnos_crates(&self) -> &'static [&'static str] {
match self {
Self::Mathematics => &["hisab"],
Self::Physics => &["impetus", "prakash", "bijli", "ushma"],
Self::Chemistry => &["kimiya"],
Self::Biology => &["jivanu", "jantu", "vanaspati", "sharira"],
Self::EarthScience => &["khanij", "badal"],
Self::Astronomy => &["falak", "tara", "jyotish"],
Self::Statistics => &["pramana"],
Self::Computing => &["vidya"],
Self::Psychology => &["bodh"],
Self::Sociology => &["sangha"],
_ => &[],
}
}
#[must_use]
pub const fn all() -> &'static [Domain] {
&[
Self::Mathematics,
Self::Physics,
Self::Chemistry,
Self::Biology,
Self::EarthScience,
Self::Astronomy,
Self::Statistics,
Self::Computing,
Self::Psychology,
Self::Sociology,
Self::Medicine,
Self::Survival,
Self::Agriculture,
Self::Construction,
Self::Repair,
Self::Communication,
Self::Encyclopedia,
Self::Language,
Self::Literature,
Self::Geography,
]
}
}
impl std::fmt::Display for Domain {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.display_name())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_domains() {
assert_eq!(Domain::all().len(), 20);
}
#[test]
fn display() {
assert_eq!(Domain::Mathematics.to_string(), "Mathematics");
assert_eq!(Domain::EarthScience.to_string(), "Earth Science");
}
#[test]
fn agnos_crates() {
assert_eq!(Domain::Mathematics.agnos_crates(), &["hisab"]);
assert_eq!(Domain::Chemistry.agnos_crates(), &["kimiya"]);
assert_eq!(
Domain::Physics.agnos_crates(),
&["impetus", "prakash", "bijli", "ushma"]
);
assert!(Domain::Medicine.agnos_crates().is_empty());
}
#[test]
fn serde_roundtrip() {
let d = Domain::Physics;
let json = serde_json::to_string(&d).unwrap();
let decoded: Domain = serde_json::from_str(&json).unwrap();
assert_eq!(d, decoded);
}
#[test]
fn serde_roundtrip_all() {
for &domain in Domain::all() {
let json = serde_json::to_string(&domain).unwrap();
let decoded: Domain = serde_json::from_str(&json).unwrap();
assert_eq!(domain, decoded, "roundtrip failed for {domain}");
}
}
#[test]
fn display_name_matches_display() {
for &domain in Domain::all() {
assert_eq!(domain.to_string(), domain.display_name());
}
}
#[test]
fn description_not_empty() {
for &domain in Domain::all() {
assert!(
!domain.description().is_empty(),
"{domain} has empty description"
);
}
}
#[test]
fn science_domains_have_crates() {
let science = [
Domain::Mathematics,
Domain::Physics,
Domain::Chemistry,
Domain::Biology,
Domain::EarthScience,
Domain::Astronomy,
Domain::Statistics,
Domain::Computing,
Domain::Psychology,
Domain::Sociology,
];
for domain in science {
assert!(
!domain.agnos_crates().is_empty(),
"{domain} should have AGNOS crates"
);
}
}
#[test]
fn applied_domains_have_no_crates() {
let applied = [
Domain::Medicine,
Domain::Survival,
Domain::Agriculture,
Domain::Construction,
Domain::Repair,
Domain::Communication,
Domain::Encyclopedia,
Domain::Language,
Domain::Literature,
Domain::Geography,
];
for domain in applied {
assert!(
domain.agnos_crates().is_empty(),
"{domain} should not have AGNOS crates"
);
}
}
#[test]
fn domain_eq_and_hash() {
use std::collections::HashSet;
let mut set = HashSet::new();
for &d in Domain::all() {
assert!(set.insert(d), "duplicate domain: {d}");
}
assert_eq!(set.len(), 20);
}
}