Skip to main content

eml_nl/utils/
election_domain_id.rs

1use std::sync::LazyLock;
2
3use regex::Regex;
4use thiserror::Error;
5
6use crate::{EMLError, EMLValueResultExt, utils::StringValueData};
7
8/// Regular expression for validating ElectionDomainId values.
9static ELECTION_DOMAIN_ID_RE: LazyLock<Regex> = LazyLock::new(|| {
10    Regex::new(r"^(\d{4}|([12]?[0-9]))$").expect("Failed to compile Election Domain ID regex")
11});
12
13/// A string of type ElectionDomainId as defined in the EML_NL specification
14#[derive(Debug, Clone, PartialEq, Eq, Hash)]
15#[repr(transparent)]
16pub struct ElectionDomainId(String);
17
18impl ElectionDomainId {
19    /// Create a new ElectionDomainId from a string, validating its format
20    pub fn new(s: impl AsRef<str>) -> Result<Self, EMLError> {
21        StringValueData::parse_from_str(s.as_ref()).wrap_value_error()
22    }
23
24    /// Get the raw string value of the ElectionDomainId.
25    pub fn value(&self) -> &str {
26        &self.0
27    }
28}
29
30/// Error returned when a string could not be parsed as a [`ElectionDomainId`]
31#[derive(Debug, Clone, Error)]
32#[error("Invalid election domain id: {0}")]
33pub struct InvalidElectionDomainIdError(String);
34
35impl StringValueData for ElectionDomainId {
36    type Error = InvalidElectionDomainIdError;
37
38    fn parse_from_str(s: &str) -> Result<Self, Self::Error>
39    where
40        Self: Sized,
41    {
42        if ELECTION_DOMAIN_ID_RE.is_match(s) {
43            Ok(ElectionDomainId(s.to_string()))
44        } else {
45            Err(InvalidElectionDomainIdError(s.to_string()))
46        }
47    }
48
49    fn to_raw_value(&self) -> String {
50        self.0.clone()
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn test_election_domain_id_regex_compiles() {
60        LazyLock::force(&ELECTION_DOMAIN_ID_RE);
61    }
62
63    #[test]
64    fn test_valid_election_domain_ids() {
65        let valid_ids = ["1", "12", "1234"];
66        for id in valid_ids {
67            assert!(
68                ElectionDomainId::new(id).is_ok(),
69                "ElectionDomainId should accept valid id: {}",
70                id
71            );
72        }
73    }
74
75    #[test]
76    fn test_invalid_election_domain_ids() {
77        let invalid_ids = ["", "34", "123", "12345", "abc"];
78        for id in invalid_ids {
79            assert!(
80                ElectionDomainId::new(id).is_err(),
81                "ElectionDomainId should reject invalid id: {}",
82                id
83            );
84        }
85    }
86}