use std::sync::LazyLock;
use regex::Regex;
use thiserror::Error;
use crate::{EMLError, EMLValueResultExt, utils::StringValueData};
static REPORTING_UNIT_IDENTIFIER_ID_RE: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"^((HSB\d+)|((HSB\d+::)?\d{4})|(((HSB\d+::)?\d{4}::)?SB\d+)|(HSB\d+::SB\d+))$")
.expect("Failed to compile ReportingUnitIdentifier id regex")
});
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct ReportingUnitIdentifierId(String);
impl ReportingUnitIdentifierId {
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 reporting unit identifier id: {0}")]
pub struct InvalidReportingUnitIdentifierIdError(String);
impl StringValueData for ReportingUnitIdentifierId {
type Error = InvalidReportingUnitIdentifierIdError;
fn parse_from_str(s: &str) -> Result<Self, Self::Error>
where
Self: Sized,
{
if REPORTING_UNIT_IDENTIFIER_ID_RE.is_match(s) {
Ok(ReportingUnitIdentifierId(s.to_string()))
} else {
Err(InvalidReportingUnitIdentifierIdError(s.to_string()))
}
}
fn to_raw_value(&self) -> String {
self.0.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_reporting_unit_identifier_id_regex_compiles() {
LazyLock::force(&REPORTING_UNIT_IDENTIFIER_ID_RE);
}
#[test]
fn test_valid_reporting_unit_identifier_ids() {
let valid_ids = [
"HSB123",
"HSB123::5678",
"5678",
"SB456",
"HSB123::SB456",
"HSB123::5678::SB456",
];
for id in valid_ids {
assert!(
ReportingUnitIdentifierId::new(id).is_ok(),
"ReportingUnitIdentifierId should accept valid id: {}",
id
);
}
}
#[test]
fn test_invalid_reporting_unit_identifier_ids() {
let invalid_ids = [
"",
"HSB",
"HSB::123",
"HSB123::",
"::5678",
"HSB123::5678::",
"HSB123::5678::SB",
];
for id in invalid_ids {
assert!(
ReportingUnitIdentifierId::new(id).is_err(),
"ReportingUnitIdentifierId should reject invalid id: {}",
id
);
}
}
}