use super::{
registry,
subtag::{self, Ascii},
};
#[cfg(test)]
mod tests;
#[cfg_attr(
feature = "quickcheck",
derive(::quickcheck_richderive::Arbitrary),
quickcheck(arbitrary = "crate::quickcheck_helpers::composite::region")
)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Region(Ascii<AREA>);
impl Region {
pub fn new(text: &str) -> Result<Self, ParseRegionError> {
if text.is_empty() {
return Err(ParseRegionError::Empty);
}
let letters = text
.chars()
.all(|character| character.is_ascii_alphabetic());
let digits = text.chars().all(|character| character.is_ascii_digit());
let canonical = match (letters, digits) {
(true, _) if text.len() == COUNTRY => Ascii::upper(text),
(true, _) => return Err(ParseRegionError::WrongLetterWidth),
(_, true) if text.len() == AREA => Ascii::verbatim(text),
(_, true) => return Err(ParseRegionError::WrongDigitWidth),
_ => match subtag::non_alphanumeric(text) {
Some(outside) => return Err(ParseRegionError::NotAlphanumeric(outside)),
None => return Err(ParseRegionError::Mixed),
},
};
match registry::region_preferred(canonical.as_str()) {
Some(preferred) => Ok(Self(Ascii::verbatim(preferred))),
None => Ok(Self(canonical)),
}
}
pub const ZZ: Self = Self(Ascii::literal("ZZ"));
#[must_use]
pub fn is_zz(&self) -> bool {
*self == Self::ZZ
}
#[inline]
#[must_use]
pub fn as_str(&self) -> &str {
self.0.as_str()
}
#[must_use]
pub fn is_area(&self) -> bool {
self.as_str().len() == AREA
}
#[inline]
#[must_use]
pub fn name(&self) -> Option<&'static str> {
registry::region_name(self.as_str())
}
#[inline]
#[must_use]
pub fn is_registered(&self) -> bool {
self.name().is_some()
}
#[inline]
#[must_use]
pub fn is_deprecated(&self) -> bool {
registry::region_is_deprecated(self.as_str())
}
#[inline]
#[must_use]
pub fn is_private_use(&self) -> bool {
registry::region_is_private_use(self.as_str())
}
}
const COUNTRY: usize = 2;
pub(super) const AREA: usize = 3;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
#[non_exhaustive]
pub enum ParseRegionError {
#[error("a region subtag is two letters or three digits, and nothing was sent")]
Empty,
#[error("a region subtag written in letters is exactly two")]
WrongLetterWidth,
#[error("a region subtag written in digits is exactly three")]
WrongDigitWidth,
#[error("a region subtag is letters or digits, so `{0}` is not one of its characters")]
NotAlphanumeric(char),
#[error("a region subtag is two letters or three digits, and never a mixture")]
Mixed,
}
super::subtag_common!(Region, ParseRegionError);