eml_nl/utils/
name_short_code.rs1use std::sync::LazyLock;
2
3use regex::Regex;
4use thiserror::Error;
5
6use crate::{EMLError, EMLValueResultExt as _, utils::StringValueData};
7
8static NAME_SHORT_CODE_RE: LazyLock<Regex> = LazyLock::new(|| {
10 Regex::new(r"^(\p{L}*\d{0,7})$").expect("Failed to compile Name Short Code regex")
11});
12
13#[derive(Debug, Clone, PartialEq, Eq, Hash)]
17#[repr(transparent)]
18pub struct NameShortCode(String);
19
20impl NameShortCode {
21 pub fn new(s: impl AsRef<str>) -> Result<Self, EMLError> {
23 StringValueData::parse_from_str(s.as_ref()).wrap_value_error()
24 }
25
26 pub fn value(&self) -> &str {
28 &self.0
29 }
30}
31
32#[derive(Debug, Clone, Error)]
34#[error("Invalid name short code: {0}")]
35pub struct InvalidNameShortCodeError(String);
36
37impl StringValueData for NameShortCode {
38 type Error = InvalidNameShortCodeError;
39 fn parse_from_str(s: &str) -> Result<Self, Self::Error>
40 where
41 Self: Sized,
42 {
43 #[expect(clippy::len_zero)]
45 if s.len() >= 1 && s.len() <= 15 && NAME_SHORT_CODE_RE.is_match(s) {
46 Ok(NameShortCode(s.to_string()))
47 } else {
48 Err(InvalidNameShortCodeError(s.to_string()))
49 }
50 }
51
52 fn to_raw_value(&self) -> String {
53 self.0.clone()
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 #[test]
62 fn test_name_short_code_regex_compiles() {
63 LazyLock::force(&NAME_SHORT_CODE_RE);
64 }
65
66 #[test]
67 fn test_valid_name_short_codes() {
68 let valid_codes = ["1", "12345", "ABC1234567"];
69 for code in valid_codes {
70 assert!(
71 NameShortCode::new(code).is_ok(),
72 "NameShortCode should accept valid code: {}",
73 code
74 );
75 }
76 }
77
78 #[test]
79 fn test_invalid_name_short_codes() {
80 let invalid_codes = ["", "verylongstringthatistoolong", "012345678", "123abc"];
81 for code in invalid_codes {
82 assert!(
83 NameShortCode::new(code).is_err(),
84 "NameShortCode should reject invalid code: {}",
85 code
86 );
87 }
88 }
89}