use core::{
cmp::Ordering,
fmt::{Display, Formatter, Result as FmtResult},
hash::{Hash, Hasher},
str::FromStr,
};
#[cfg(feature = "serde")]
use serde::{
Deserialize, Deserializer, Serialize, Serializer,
de::{Error as DeserializeError, Visitor},
};
use crate::Country;
#[derive(Copy, Clone, Debug, Eq, PartialEq, derive_more::Display, derive_more::Error)]
pub enum SubdivisionParseError {
#[display("invalid subdivision code")]
InvalidCode,
}
#[derive(Copy, Clone, derive_more::Debug)]
pub struct Subdivision {
pub code: &'static str,
pub name: &'static str,
#[debug(skip)]
country_index: usize,
}
const ALPHA2_CODE_COUNT: usize = 26 * 26;
const fn letter_index(letter: u8) -> usize {
match letter {
b'A' => 0,
b'B' => 1,
b'C' => 2,
b'D' => 3,
b'E' => 4,
b'F' => 5,
b'G' => 6,
b'H' => 7,
b'I' => 8,
b'J' => 9,
b'K' => 10,
b'L' => 11,
b'M' => 12,
b'N' => 13,
b'O' => 14,
b'P' => 15,
b'Q' => 16,
b'R' => 17,
b'S' => 18,
b'T' => 19,
b'U' => 20,
b'V' => 21,
b'W' => 22,
b'X' => 23,
b'Y' => 24,
b'Z' => 25,
_ => 26,
}
}
const fn alpha2_index(code: &str) -> usize {
let &[first, second, ..] = code.as_bytes() else {
return 0;
};
letter_index(first) * 26 + letter_index(second)
}
const fn country_indexes_by_alpha2() -> [usize; ALPHA2_CODE_COUNT] {
let mut country_indexes = [0; ALPHA2_CODE_COUNT];
let countries = Country::countries();
let mut index = 0;
while index < countries.len() {
country_indexes[alpha2_index(countries[index].alpha2)] = index;
index += 1;
}
country_indexes
}
const COUNTRY_INDEXES_BY_ALPHA2: [usize; ALPHA2_CODE_COUNT] = country_indexes_by_alpha2();
macro_rules! subdivision {
($code:literal, $name:literal) => {
Subdivision {
code: $code,
name: $name,
country_index: COUNTRY_INDEXES_BY_ALPHA2[alpha2_index($code)],
}
};
}
mod data {
use super::{COUNTRY_INDEXES_BY_ALPHA2, Subdivision, alpha2_index};
include!("subdivision_data.rs");
}
pub const SUBDIVISION_DATA_VERSION: &str = data::CLDR_VERSION;
impl Subdivision {
#[must_use]
pub const fn subdivisions() -> &'static [Self] {
&data::SUBDIVISIONS
}
#[must_use]
pub const fn country(&self) -> Country {
Country::countries()[self.country_index]
}
pub fn from_code<A: AsRef<str>>(code: A) -> Result<Self, SubdivisionParseError> {
let code = code.as_ref();
let mut normalized = [0_u8; 6];
let normalized = normalized
.get_mut(..code.len())
.ok_or(SubdivisionParseError::InvalidCode)?;
normalized.copy_from_slice(code.as_bytes());
normalized.make_ascii_uppercase();
let normalized =
core::str::from_utf8(normalized).map_err(|_| SubdivisionParseError::InvalidCode)?;
data::SUBDIVISIONS
.binary_search_by(|subdivision| subdivision.code.cmp(normalized))
.ok()
.and_then(|index| data::SUBDIVISIONS.get(index))
.copied()
.ok_or(SubdivisionParseError::InvalidCode)
}
}
impl Ord for Subdivision {
fn cmp(&self, other: &Self) -> Ordering {
self.code.cmp(other.code)
}
}
impl PartialOrd for Subdivision {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Eq for Subdivision {}
impl PartialEq for Subdivision {
fn eq(&self, other: &Self) -> bool {
self.code == other.code
}
}
impl Hash for Subdivision {
fn hash<H: Hasher>(&self, state: &mut H) {
self.code.hash(state);
}
}
impl Country {
#[must_use]
pub fn subdivisions(&self) -> &'static [Subdivision] {
let start = data::SUBDIVISIONS.partition_point(|subdivision| {
subdivision.code.get(..2).unwrap_or_default() < self.alpha2
});
let end = data::SUBDIVISIONS.partition_point(|subdivision| {
subdivision.code.get(..2).unwrap_or_default() <= self.alpha2
});
data::SUBDIVISIONS.get(start..end).unwrap_or_default()
}
}
impl Display for Subdivision {
fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
formatter.write_str(self.code)
}
}
impl FromStr for Subdivision {
type Err = SubdivisionParseError;
fn from_str(code: &str) -> Result<Self, Self::Err> {
Self::from_code(code)
}
}
#[cfg(feature = "serde")]
impl Serialize for Subdivision {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.code)
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Subdivision {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct SubdivisionVisitor;
impl Visitor<'_> for SubdivisionVisitor {
type Value = Subdivision;
fn expecting(&self, formatter: &mut Formatter<'_>) -> FmtResult {
formatter.write_str("an ISO 3166-2 subdivision code")
}
fn visit_str<E>(self, code: &str) -> Result<Self::Value, E>
where
E: DeserializeError,
{
Subdivision::from_code(code).map_err(|_| {
DeserializeError::invalid_value(serde::de::Unexpected::Str(code), &self)
})
}
}
deserializer.deserialize_str(SubdivisionVisitor)
}
}
#[cfg(test)]
mod tests {
use super::{Country, alpha2_index, country_indexes_by_alpha2, letter_index};
#[test]
fn compile_time_country_indexes_are_complete() {
for (expected, letter) in (b'A'..=b'Z').enumerate() {
assert_eq!(letter_index(letter), expected);
}
assert_eq!(letter_index(b'?'), 26);
assert_eq!(alpha2_index(""), 0);
assert_eq!(alpha2_index("AD-02"), 3);
let country_indexes = country_indexes_by_alpha2();
for (expected, country) in Country::countries().iter().enumerate() {
assert_eq!(country_indexes[alpha2_index(country.alpha2)], expected);
}
}
}