refactor(domain): extract country utilities into dedicated module
Move COUNTRY_MAP, country_to_iso(), iso_to_flag_emoji() and their tests from country_stats.rs into a new countries.rs module for reuse by the detail page view models.
This commit is contained in:
parent
bfe65d23ff
commit
9237a386e0
3 changed files with 149 additions and 147 deletions
146
src/domain/countries.rs
Normal file
146
src/domain/countries.rs
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
use std::collections::HashMap;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
static COUNTRY_MAP: LazyLock<HashMap<&'static str, &'static str>> = 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"), "🇪🇹");
|
||||
}
|
||||
}
|
||||
|
|
@ -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<HashMap<&'static str, &'static str>> = 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"), "🇪🇹");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue