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 serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::countries::{country_to_iso, iso_to_flag_emoji};
|
||||||
|
|
||||||
/// A single country's count for geographic statistics.
|
/// A single country's count for geographic statistics.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct CountryStat {
|
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 bags;
|
||||||
pub mod brews;
|
pub mod brews;
|
||||||
pub mod cafes;
|
pub mod cafes;
|
||||||
|
pub mod countries;
|
||||||
pub mod country_stats;
|
pub mod country_stats;
|
||||||
pub mod cups;
|
pub mod cups;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue