use std::fmt::{Debug, Display};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ARSLevelError {
#[error("Invalid ars level: {got}, expected {max} to {min}")]
InvalidLevel { min: u8, max: u8, got: u8 },
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub struct ARSLevel(u8);
impl ARSLevel {
pub const COUNTRY: Self = Self(0);
pub const STATE: Self = Self(1);
pub const DISTRICT: Self = Self(2);
pub const COUNTY: Self = Self(3);
pub const ADMIN_ASSOCIATION: Self = Self(4);
pub const MUNICIPALITY: Self = Self(5);
pub const MAX: Self = Self::COUNTRY;
pub const MIN: Self = Self::MUNICIPALITY;
pub const INVALID: Self = Self(100);
pub unsafe fn new_unchecked(level: u8) -> Self {
Self(level)
}
pub fn new(level: u8) -> Result<Self, ARSLevelError> {
if (Self::MAX.0..=Self::MIN.0).contains(&level) {
Ok(Self(level))
} else {
Err(ARSLevelError::InvalidLevel {
min: Self::MIN.0,
max: Self::MAX.0,
got: level,
})
}
}
pub fn parent(&self) -> Option<Self> {
match *self {
Self::COUNTRY => None,
Self::STATE => Some(Self::COUNTRY),
Self::DISTRICT => Some(Self::STATE),
Self::COUNTY => Some(Self::DISTRICT),
Self::ADMIN_ASSOCIATION => Some(Self::COUNTY),
Self::MUNICIPALITY => Some(Self::ADMIN_ASSOCIATION),
_ => unreachable!(),
}
}
pub fn child(&self) -> Option<Self> {
match *self {
Self::COUNTRY => Some(Self::STATE),
Self::STATE => Some(Self::DISTRICT),
Self::DISTRICT => Some(Self::COUNTY),
Self::COUNTY => Some(Self::ADMIN_ASSOCIATION),
Self::ADMIN_ASSOCIATION => Some(Self::MUNICIPALITY),
Self::MUNICIPALITY => None,
_ => unreachable!(),
}
}
pub const fn inner(&self) -> u8 {
self.0
}
}
impl Display for ARSLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
0 => write!(f, "Country (0)"),
1 => write!(f, "State (1)"),
2 => write!(f, "District (2)"),
3 => write!(f, "County (3)"),
4 => write!(f, "Admin Assiciation (4)"),
5 => write!(f, "Municipality (5)"),
_ => unreachable!(),
}
}
}
impl Debug for ARSLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ARSLevel").field(&self.to_string()).finish()
}
}
impl From<ARSLevel> for u8 {
fn from(value: ARSLevel) -> Self {
value.0
}
}
impl TryFrom<u8> for ARSLevel {
type Error = ARSLevelError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Self::new(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_constants() {
assert_eq!(ARSLevel::COUNTRY.inner(), 0);
assert_eq!(ARSLevel::STATE.inner(), 1);
assert_eq!(ARSLevel::DISTRICT.inner(), 2);
assert_eq!(ARSLevel::COUNTY.inner(), 3);
assert_eq!(ARSLevel::ADMIN_ASSOCIATION.inner(), 4);
assert_eq!(ARSLevel::MUNICIPALITY.inner(), 5);
}
#[test]
fn test_max_min_constants() {
assert_eq!(ARSLevel::MAX, ARSLevel::COUNTRY);
assert_eq!(ARSLevel::MIN, ARSLevel::MUNICIPALITY);
}
#[test]
fn test_new_valid_levels() {
for i in 0..=5 {
let level = ARSLevel::new(i).unwrap();
assert_eq!(level.inner(), i);
}
}
#[test]
fn test_new_invalid_levels() {
let invalid_levels = vec![6, 7, 10, 100, 255];
for i in invalid_levels {
let result = ARSLevel::new(i);
assert!(result.is_err());
match result {
Err(ARSLevelError::InvalidLevel { min, max, got }) => {
assert_eq!(min, 5);
assert_eq!(max, 0);
assert_eq!(got, i);
}
_ => panic!("Expected InvalidLevel error"),
}
}
}
#[test]
fn test_new_unchecked() {
let level = unsafe { ARSLevel::new_unchecked(3) };
assert_eq!(level, ARSLevel::COUNTY);
assert_eq!(level.inner(), 3);
}
#[test]
fn test_parent_chain() {
let mut current = ARSLevel::MUNICIPALITY;
assert_eq!(current.parent(), Some(ARSLevel::ADMIN_ASSOCIATION));
current = current.parent().unwrap();
assert_eq!(current.parent(), Some(ARSLevel::COUNTY));
current = current.parent().unwrap();
assert_eq!(current.parent(), Some(ARSLevel::DISTRICT));
current = current.parent().unwrap();
assert_eq!(current.parent(), Some(ARSLevel::STATE));
current = current.parent().unwrap();
assert_eq!(current.parent(), Some(ARSLevel::COUNTRY));
current = current.parent().unwrap();
assert_eq!(current.parent(), None);
}
#[test]
fn test_child_chain() {
let mut current = ARSLevel::COUNTRY;
assert_eq!(current.child(), Some(ARSLevel::STATE));
current = current.child().unwrap();
assert_eq!(current.child(), Some(ARSLevel::DISTRICT));
current = current.child().unwrap();
assert_eq!(current.child(), Some(ARSLevel::COUNTY));
current = current.child().unwrap();
assert_eq!(current.child(), Some(ARSLevel::ADMIN_ASSOCIATION));
current = current.child().unwrap();
assert_eq!(current.child(), Some(ARSLevel::MUNICIPALITY));
current = current.child().unwrap();
assert_eq!(current.child(), None);
}
#[test]
fn test_individual_parent_relationships() {
assert_eq!(ARSLevel::COUNTRY.parent(), None);
assert_eq!(ARSLevel::STATE.parent(), Some(ARSLevel::COUNTRY));
assert_eq!(ARSLevel::DISTRICT.parent(), Some(ARSLevel::STATE));
assert_eq!(ARSLevel::COUNTY.parent(), Some(ARSLevel::DISTRICT));
assert_eq!(ARSLevel::ADMIN_ASSOCIATION.parent(), Some(ARSLevel::COUNTY));
assert_eq!(
ARSLevel::MUNICIPALITY.parent(),
Some(ARSLevel::ADMIN_ASSOCIATION)
);
}
#[test]
fn test_individual_child_relationships() {
assert_eq!(ARSLevel::COUNTRY.child(), Some(ARSLevel::STATE));
assert_eq!(ARSLevel::STATE.child(), Some(ARSLevel::DISTRICT));
assert_eq!(ARSLevel::DISTRICT.child(), Some(ARSLevel::COUNTY));
assert_eq!(ARSLevel::COUNTY.child(), Some(ARSLevel::ADMIN_ASSOCIATION));
assert_eq!(
ARSLevel::ADMIN_ASSOCIATION.child(),
Some(ARSLevel::MUNICIPALITY)
);
assert_eq!(ARSLevel::MUNICIPALITY.child(), None);
}
#[test]
fn test_equality() {
assert_eq!(ARSLevel::COUNTRY, ARSLevel::new(0).unwrap());
assert_eq!(ARSLevel::STATE, ARSLevel::new(1).unwrap());
assert_ne!(ARSLevel::COUNTRY, ARSLevel::STATE);
}
#[test]
fn test_ordering() {
assert!(ARSLevel::COUNTRY < ARSLevel::STATE);
assert!(ARSLevel::STATE < ARSLevel::DISTRICT);
assert!(ARSLevel::DISTRICT < ARSLevel::COUNTY);
assert!(ARSLevel::COUNTY < ARSLevel::ADMIN_ASSOCIATION);
assert!(ARSLevel::ADMIN_ASSOCIATION < ARSLevel::MUNICIPALITY);
assert!(ARSLevel::MUNICIPALITY > ARSLevel::COUNTRY);
assert!(ARSLevel::ADMIN_ASSOCIATION > ARSLevel::STATE);
}
#[test]
fn test_display() {
assert_eq!(ARSLevel::COUNTRY.to_string(), "Country (0)");
assert_eq!(ARSLevel::STATE.to_string(), "State (1)");
assert_eq!(ARSLevel::DISTRICT.to_string(), "District (2)");
assert_eq!(ARSLevel::COUNTY.to_string(), "County (3)");
assert_eq!(
ARSLevel::ADMIN_ASSOCIATION.to_string(),
"Admin Assiciation (4)"
);
assert_eq!(ARSLevel::MUNICIPALITY.to_string(), "Municipality (5)");
}
#[test]
fn test_debug() {
let level = ARSLevel::COUNTY;
let debug_str = format!("{:?}", level);
assert!(debug_str.contains("ARSLevel"));
assert!(debug_str.contains("County (3)"));
}
#[test]
fn test_inner() {
assert_eq!(ARSLevel::COUNTRY.inner(), 0);
assert_eq!(ARSLevel::STATE.inner(), 1);
assert_eq!(ARSLevel::DISTRICT.inner(), 2);
assert_eq!(ARSLevel::COUNTY.inner(), 3);
assert_eq!(ARSLevel::ADMIN_ASSOCIATION.inner(), 4);
assert_eq!(ARSLevel::MUNICIPALITY.inner(), 5);
}
#[test]
fn test_from_u8_to_arslevel() {
let level: ARSLevel = 2u8.try_into().unwrap();
assert_eq!(level, ARSLevel::DISTRICT);
}
#[test]
fn test_from_u8_to_arslevel_invalid() {
let result: Result<ARSLevel, _> = 10u8.try_into();
assert!(result.is_err());
}
#[test]
fn test_from_arslevel_to_u8() {
let level = ARSLevel::COUNTY;
let value: u8 = level.into();
assert_eq!(value, 3);
}
#[test]
fn test_clone_copy() {
let level = ARSLevel::STATE;
let cloned = level.clone();
let copied = level;
assert_eq!(level, cloned);
assert_eq!(level, copied);
}
#[test]
fn test_error_message() {
let error = ARSLevel::new(10).unwrap_err();
let message = error.to_string();
assert!(message.contains("Invalid ars level"));
assert!(message.contains("10"));
}
}