dd-sensitive-data-scanner 0.0.0

Core Sensitive Data Scanner library for detecting and redacting sensitive information.
Documentation
use crate::secondary_validation::Validator;

pub struct UkNinoFormatCheck;

const INVALID_FIRST: &[char] = &['D', 'F', 'I', 'Q', 'U', 'V'];
const INVALID_SECOND: &[char] = &['D', 'F', 'I', 'O', 'Q', 'U', 'V'];
const INVALID_PREFIXES: &[&str] = &["BG", "GB", "NK", "KN", "NT", "TN", "ZZ"];

const PATTERN_LENGTH: usize = 9;
const PREFIX_LENGTH: usize = 2;
const SUFFIX_INDEX: usize = PATTERN_LENGTH - 1;

impl Validator for UkNinoFormatCheck {
    fn is_valid_match(&self, regex_match: &str) -> bool {
        let chars: Vec<char> = regex_match
            .chars()
            .filter(|c| c.is_ascii_alphanumeric())
            .map(|c| c.to_ascii_uppercase())
            .collect();

        if chars.len() != PATTERN_LENGTH {
            return false;
        }

        let c1 = chars[0];
        let c2 = chars[1];

        if !c1.is_ascii_alphabetic() || !c2.is_ascii_alphabetic() {
            return false;
        }

        let prefix: String = chars[..PREFIX_LENGTH].iter().collect();
        let suffix = chars[SUFFIX_INDEX];

        if INVALID_FIRST.contains(&c1)
            || INVALID_SECOND.contains(&c2)
            || INVALID_PREFIXES.contains(&prefix.as_str())
        {
            return false;
        }

        matches!(suffix, 'A'..='D')
    }
}

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

    #[test]
    fn valid_ni_numbers() {
        let validator = UkNinoFormatCheck;

        let valid_numbers = vec!["AB123456C", "AB 12 34 56 C", "ab123456c", "ZY999999D"];
        for number in valid_numbers {
            assert!(
                validator.is_valid_match(number),
                "Expected no error for number {}",
                number
            );
        }
    }

    #[test]
    fn invalid_format() {
        let validator = UkNinoFormatCheck;

        let invalid_numbers = vec!["AB12345C", "AB1234567C", "1B123456C"];
        for number in invalid_numbers {
            assert!(
                !validator.is_valid_match(number),
                "Expected error for number {}",
                number
            );
        }
    }

    #[test]
    fn invalid_prefix_first_letter() {
        let validator = UkNinoFormatCheck;

        for c in INVALID_FIRST {
            let ni = format!("{}B123456A", c);
            assert!(
                !validator.is_valid_match(&ni),
                "Expected error for first letter {}",
                c
            );
        }
    }

    #[test]
    fn invalid_prefix_second_letter() {
        let validator = UkNinoFormatCheck;

        for c in INVALID_SECOND {
            let ni = format!("A{}123456A", c);
            assert!(
                !validator.is_valid_match(&ni),
                "Expected error for second letter {}",
                c
            );
        }
    }

    #[test]
    fn invalid_prefix_combinations() {
        let validator = UkNinoFormatCheck;

        for prefix in INVALID_PREFIXES {
            let ni = format!("{}123456A", prefix);
            assert!(
                !validator.is_valid_match(&ni),
                "Expected error for prefix {}",
                prefix
            );
        }
    }

    #[test]
    fn invalid_suffix() {
        let validator = UkNinoFormatCheck;

        for c in ['E', 'F', 'Z', '1'] {
            let ni = format!("AB123456{}", c);
            assert!(
                !validator.is_valid_match(&ni),
                "Expected error for suffix {}",
                c
            );
        }
    }
}