diff --git a/src/domain/countries.rs b/src/domain/countries.rs new file mode 100644 index 0000000..e59ec80 --- /dev/null +++ b/src/domain/countries.rs @@ -0,0 +1,146 @@ +use std::collections::HashMap; +use std::sync::LazyLock; + +static COUNTRY_MAP: LazyLock> = LazyLock::new(|| { + HashMap::from([ + // Coffee-producing countries + ("ethiopia", "ET"), + ("colombia", "CO"), + ("kenya", "KE"), + ("brazil", "BR"), + ("guatemala", "GT"), + ("costa rica", "CR"), + ("rwanda", "RW"), + ("honduras", "HN"), + ("panama", "PA"), + ("mexico", "MX"), + ("el salvador", "SV"), + ("bolivia", "BO"), + ("peru", "PE"), + ("indonesia", "ID"), + ("india", "IN"), + ("vietnam", "VN"), + ("myanmar", "MM"), + ("china", "CN"), + ("papua new guinea", "PG"), + ("tanzania", "TZ"), + ("uganda", "UG"), + ("burundi", "BI"), + ("democratic republic of the congo", "CD"), + ("drc", "CD"), + ("congo", "CD"), + ("republic of the congo", "CG"), + ("yemen", "YE"), + ("nicaragua", "NI"), + ("ecuador", "EC"), + ("dominican republic", "DO"), + ("haiti", "HT"), + ("jamaica", "JM"), + ("thailand", "TH"), + ("laos", "LA"), + ("philippines", "PH"), + ("taiwan", "TW"), + // European roaster/cafe countries + ("united kingdom", "GB"), + ("uk", "GB"), + ("england", "GB"), + ("scotland", "GB"), + ("wales", "GB"), + ("northern ireland", "GB"), + ("germany", "DE"), + ("france", "FR"), + ("spain", "ES"), + ("italy", "IT"), + ("netherlands", "NL"), + ("belgium", "BE"), + ("denmark", "DK"), + ("sweden", "SE"), + ("norway", "NO"), + ("finland", "FI"), + ("switzerland", "CH"), + ("austria", "AT"), + ("portugal", "PT"), + ("ireland", "IE"), + ("poland", "PL"), + ("czech republic", "CZ"), + ("czechia", "CZ"), + ("greece", "GR"), + ("slovenia", "SI"), + ("croatia", "HR"), + ("romania", "RO"), + ("hungary", "HU"), + // North America + ("united states", "US"), + ("united states of america", "US"), + ("usa", "US"), + ("us", "US"), + ("canada", "CA"), + // Asia-Pacific + ("japan", "JP"), + ("south korea", "KR"), + ("korea", "KR"), + ("australia", "AU"), + ("new zealand", "NZ"), + ("singapore", "SG"), + // Other + ("south africa", "ZA"), + ("turkey", "TR"), + ("israel", "IL"), + ("united arab emirates", "AE"), + ("uae", "AE"), + ]) +}); + +/// Maps a free-text country name to its ISO-3166-1 alpha-2 code. +pub fn country_to_iso(name: &str) -> Option<&'static str> { + COUNTRY_MAP + .get(name.trim().to_lowercase().as_str()) + .copied() +} + +/// Converts an ISO-3166-1 alpha-2 code to a flag emoji using regional indicator symbols. +pub fn iso_to_flag_emoji(code: &str) -> String { + code.chars() + .filter_map(|c| { + let upper = c.to_ascii_uppercase(); + if upper.is_ascii_uppercase() { + char::from_u32(0x1F1E6 + (upper as u32 - 'A' as u32)) + } else { + None + } + }) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn country_to_iso_normalises_case() { + assert_eq!(country_to_iso("Ethiopia"), Some("ET")); + assert_eq!(country_to_iso("ETHIOPIA"), Some("ET")); + assert_eq!(country_to_iso(" ethiopia "), Some("ET")); + } + + #[test] + fn country_to_iso_handles_aliases() { + assert_eq!(country_to_iso("United Kingdom"), Some("GB")); + assert_eq!(country_to_iso("UK"), Some("GB")); + assert_eq!(country_to_iso("England"), Some("GB")); + } + + #[test] + fn country_to_iso_returns_none_for_unknown() { + assert_eq!(country_to_iso("Blend"), None); + assert_eq!(country_to_iso("Multiple Origins"), None); + assert_eq!(country_to_iso(""), None); + } + + #[test] + fn iso_to_flag_emoji_produces_correct_flags() { + assert_eq!(iso_to_flag_emoji("GB"), "πŸ‡¬πŸ‡§"); + assert_eq!(iso_to_flag_emoji("US"), "πŸ‡ΊπŸ‡Έ"); + assert_eq!(iso_to_flag_emoji("ET"), "πŸ‡ͺπŸ‡Ή"); + } +} diff --git a/src/domain/country_stats.rs b/src/domain/country_stats.rs index a672557..7a41abb 100644 --- a/src/domain/country_stats.rs +++ b/src/domain/country_stats.rs @@ -1,8 +1,7 @@ -use std::collections::HashMap; -use std::sync::LazyLock; - use serde::{Deserialize, Serialize}; +use super::countries::{country_to_iso, iso_to_flag_emoji}; + /// A single country's count for geographic statistics. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CountryStat { @@ -51,147 +50,3 @@ impl GeoStats { } } } - -static COUNTRY_MAP: LazyLock> = LazyLock::new(|| { - HashMap::from([ - // Coffee-producing countries - ("ethiopia", "ET"), - ("colombia", "CO"), - ("kenya", "KE"), - ("brazil", "BR"), - ("guatemala", "GT"), - ("costa rica", "CR"), - ("rwanda", "RW"), - ("honduras", "HN"), - ("panama", "PA"), - ("mexico", "MX"), - ("el salvador", "SV"), - ("bolivia", "BO"), - ("peru", "PE"), - ("indonesia", "ID"), - ("india", "IN"), - ("vietnam", "VN"), - ("myanmar", "MM"), - ("china", "CN"), - ("papua new guinea", "PG"), - ("tanzania", "TZ"), - ("uganda", "UG"), - ("burundi", "BI"), - ("democratic republic of the congo", "CD"), - ("drc", "CD"), - ("congo", "CD"), - ("republic of the congo", "CG"), - ("yemen", "YE"), - ("nicaragua", "NI"), - ("ecuador", "EC"), - ("dominican republic", "DO"), - ("haiti", "HT"), - ("jamaica", "JM"), - ("thailand", "TH"), - ("laos", "LA"), - ("philippines", "PH"), - ("taiwan", "TW"), - // European roaster/cafe countries - ("united kingdom", "GB"), - ("uk", "GB"), - ("england", "GB"), - ("scotland", "GB"), - ("wales", "GB"), - ("northern ireland", "GB"), - ("germany", "DE"), - ("france", "FR"), - ("spain", "ES"), - ("italy", "IT"), - ("netherlands", "NL"), - ("belgium", "BE"), - ("denmark", "DK"), - ("sweden", "SE"), - ("norway", "NO"), - ("finland", "FI"), - ("switzerland", "CH"), - ("austria", "AT"), - ("portugal", "PT"), - ("ireland", "IE"), - ("poland", "PL"), - ("czech republic", "CZ"), - ("czechia", "CZ"), - ("greece", "GR"), - ("slovenia", "SI"), - ("croatia", "HR"), - ("romania", "RO"), - ("hungary", "HU"), - // North America - ("united states", "US"), - ("united states of america", "US"), - ("usa", "US"), - ("us", "US"), - ("canada", "CA"), - // Asia-Pacific - ("japan", "JP"), - ("south korea", "KR"), - ("korea", "KR"), - ("australia", "AU"), - ("new zealand", "NZ"), - ("singapore", "SG"), - // Other - ("south africa", "ZA"), - ("turkey", "TR"), - ("israel", "IL"), - ("united arab emirates", "AE"), - ("uae", "AE"), - ]) -}); - -/// Maps a free-text country name to its ISO-3166-1 alpha-2 code. -pub fn country_to_iso(name: &str) -> Option<&'static str> { - COUNTRY_MAP - .get(name.trim().to_lowercase().as_str()) - .copied() -} - -/// Converts an ISO-3166-1 alpha-2 code to a flag emoji using regional indicator symbols. -pub fn iso_to_flag_emoji(code: &str) -> String { - code.chars() - .filter_map(|c| { - let upper = c.to_ascii_uppercase(); - if upper.is_ascii_uppercase() { - char::from_u32(0x1F1E6 + (upper as u32 - 'A' as u32)) - } else { - None - } - }) - .collect() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn country_to_iso_normalises_case() { - assert_eq!(country_to_iso("Ethiopia"), Some("ET")); - assert_eq!(country_to_iso("ETHIOPIA"), Some("ET")); - assert_eq!(country_to_iso(" ethiopia "), Some("ET")); - } - - #[test] - fn country_to_iso_handles_aliases() { - assert_eq!(country_to_iso("United Kingdom"), Some("GB")); - assert_eq!(country_to_iso("UK"), Some("GB")); - assert_eq!(country_to_iso("England"), Some("GB")); - } - - #[test] - fn country_to_iso_returns_none_for_unknown() { - assert_eq!(country_to_iso("Blend"), None); - assert_eq!(country_to_iso("Multiple Origins"), None); - assert_eq!(country_to_iso(""), None); - } - - #[test] - fn iso_to_flag_emoji_produces_correct_flags() { - assert_eq!(iso_to_flag_emoji("GB"), "πŸ‡¬πŸ‡§"); - assert_eq!(iso_to_flag_emoji("US"), "πŸ‡ΊπŸ‡Έ"); - assert_eq!(iso_to_flag_emoji("ET"), "πŸ‡ͺπŸ‡Ή"); - } -} diff --git a/src/domain/mod.rs b/src/domain/mod.rs index bd16395..7884bbc 100644 --- a/src/domain/mod.rs +++ b/src/domain/mod.rs @@ -2,6 +2,7 @@ pub mod ai_usage; pub mod bags; pub mod brews; pub mod cafes; +pub mod countries; pub mod country_stats; pub mod cups; pub mod errors;