1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
use std::sync::OnceLock;

use regex::Regex;
use validator::ValidationError;

static REGEX: OnceLock<Regex> = OnceLock::new();

/// Helper function to validate identifierString
///
/// # identfierString
/// This is a case-insensitive dataType and can only contain characters from the following
/// character set: `a-z`, `A-Z`, `0-9`, `'*'`, `'-'`, `'_'`, `'='`, `':'`, `'+'`, `'|'`, `'@'`, `'.'`
pub fn validate_identifier_string(s: &str) -> Result<(), ValidationError> {
    // regex for identifierString as defined by the specification
    let res = REGEX
        .get_or_init(|| Regex::new(r"^[a-zA-Z0-9*+=:|@._-]*$").unwrap())
        .is_match(s);

    match res {
        true => Ok(()),
        false => Err(ValidationError::new("Not a valid identifierString")),
    }
}

#[cfg(test)]
mod test {
    use super::validate_identifier_string;

    #[test]
    fn good_case() {
        let good_cases = ["abc123", "A*C_|..", "||||", "ABCabc123:==@"];

        for case in good_cases.iter() {
            dbg!(case);
            validate_identifier_string(case).unwrap();
        }
    }

    #[test]
    fn bad_case() {
        let bad_cases = [
            "abc123/",
            "https://",
            "ABC#123",
            ",,,,",
            "Test test",
            "123 Prøve",
            "123 Test?",
        ];

        for case in bad_cases.iter() {
            dbg!(case);
            validate_identifier_string(case).unwrap_err();
        }
    }
}