use std::sync::LazyLock;
use regex::Regex;
use thiserror::Error;
use crate::{EMLError, EMLValueResultExt, utils::StringValueData};
static ELECTION_DOMAIN_ID_RE: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"^(\d{4}|([12]?[0-9]))$").expect("Failed to compile Election Domain ID regex")
});
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct ElectionDomainId(String);
impl ElectionDomainId {
pub fn new(s: impl AsRef<str>) -> Result<Self, EMLError> {
StringValueData::parse_from_str(s.as_ref()).wrap_value_error()
}
pub fn value(&self) -> &str {
&self.0
}
}
#[derive(Debug, Clone, Error)]
#[error("Invalid election domain id: {0}")]
pub struct InvalidElectionDomainIdError(String);
impl StringValueData for ElectionDomainId {
type Error = InvalidElectionDomainIdError;
fn parse_from_str(s: &str) -> Result<Self, Self::Error>
where
Self: Sized,
{
if ELECTION_DOMAIN_ID_RE.is_match(s) {
Ok(ElectionDomainId(s.to_string()))
} else {
Err(InvalidElectionDomainIdError(s.to_string()))
}
}
fn to_raw_value(&self) -> String {
self.0.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_election_domain_id_regex_compiles() {
LazyLock::force(&ELECTION_DOMAIN_ID_RE);
}
#[test]
fn test_valid_election_domain_ids() {
let valid_ids = ["1", "12", "1234"];
for id in valid_ids {
assert!(
ElectionDomainId::new(id).is_ok(),
"ElectionDomainId should accept valid id: {}",
id
);
}
}
#[test]
fn test_invalid_election_domain_ids() {
let invalid_ids = ["", "34", "123", "12345", "abc"];
for id in invalid_ids {
assert!(
ElectionDomainId::new(id).is_err(),
"ElectionDomainId should reject invalid id: {}",
id
);
}
}
}