naru-config 0.7.0

A security-first configuration manager with encryption and audit logging
Documentation
use crate::core::schema_model::ValidationRules;

pub fn validate_string_length(value: &str, rules: &ValidationRules) -> Result<(), String> {
    if let Some(min) = rules.min_length {
        if value.len() < min {
            return Err(format!("Value is too short. Minimum length is {}", min));
        }
    }

    if let Some(max) = rules.max_length {
        if value.len() > max {
            return Err(format!("Value is too long. Maximum length is {}", max));
        }
    }

    Ok(())
}

pub fn validate_string_pattern(value: &str, pattern: &str) -> Result<(), String> {
    let re = regex::Regex::new(pattern).map_err(|e| format!("Invalid regex pattern: {}", e))?;

    if re.is_match(value) {
        Ok(())
    } else {
        Err(format!(
            "Value does not match required pattern: {}",
            pattern
        ))
    }
}

pub fn validate_string_with_rules(value: &str, rules: &ValidationRules) -> Result<(), String> {
    validate_string_length(value, rules)?;

    if let Some(pattern) = &rules.pattern {
        validate_string_pattern(value, pattern)?;
    }

    Ok(())
}

pub fn is_empty_string(value: &str) -> bool {
    value.is_empty()
}

pub fn is_whitespace_only(value: &str) -> bool {
    value.chars().all(|c| c.is_whitespace())
}

pub fn is_valid_string_length(value: &str, min: usize, max: usize) -> bool {
    let len = value.len();
    len >= min && len <= max
}

pub fn contains_control_characters(value: &str) -> bool {
    value.chars().any(|c| c.is_control())
}

pub fn contains_null_byte(value: &str) -> bool {
    value.contains('\0')
}

pub fn count_characters(value: &str) -> usize {
    value.chars().count()
}

pub fn count_bytes(value: &str) -> usize {
    value.as_bytes().len()
}

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

    #[test]
    fn test_validate_string_length_min() {
        let rules = ValidationRules::new().with_min_length(3);

        assert!(validate_string_length("abc", &rules).is_ok());
        assert!(validate_string_length("ab", &rules).is_err());
        assert!(validate_string_length("", &rules).is_err());
    }

    #[test]
    fn test_validate_string_length_max() {
        let rules = ValidationRules::new().with_max_length(5);

        assert!(validate_string_length("abc", &rules).is_ok());
        assert!(validate_string_length("abcdef", &rules).is_err());
    }

    #[test]
    fn test_validate_string_length_range() {
        let rules = ValidationRules::new().with_length_range(3, 5);

        assert!(validate_string_length("ab", &rules).is_err());
        assert!(validate_string_length("abc", &rules).is_ok());
        assert!(validate_string_length("abcdef", &rules).is_err());
    }

    #[test]
    fn test_validate_string_pattern() {
        assert!(validate_string_pattern("test@example.com", r"^[\w\.-]+@[\w\.-]+\.\w+$").is_ok());
        assert!(validate_string_pattern("invalid-email", r"^[\w\.-]+@[\w\.-]+\.\w+$").is_err());
    }

    #[test]
    fn test_validate_string_pattern_invalid_regex() {
        assert!(validate_string_pattern("test", "[").is_err());
    }

    #[test]
    fn test_validate_string_with_rules() {
        let rules = ValidationRules::new()
            .with_length_range(5, 10)
            .with_pattern(r"^[a-z]+$");

        assert!(validate_string_with_rules("hello", &rules).is_ok());
        assert!(validate_string_with_rules("hi", &rules).is_err());
        assert!(validate_string_with_rules("hello123", &rules).is_err());
    }

    #[test]
    fn test_empty_and_whitespace() {
        assert!(is_empty_string(""));
        assert!(!is_empty_string("a"));

        assert!(is_whitespace_only("   "));
        assert!(is_whitespace_only("\t\n\r"));
        assert!(!is_whitespace_only(" abc "));
    }

    #[test]
    fn test_valid_string_length() {
        assert!(is_valid_string_length("hello", 3, 10));
        assert!(is_valid_string_length("hi", 1, 5));
        assert!(!is_valid_string_length("hello", 10, 20));
    }

    #[test]
    fn test_control_characters() {
        assert!(!contains_control_characters("hello"));
        assert!(contains_control_characters("hello\x00world"));
        assert!(contains_control_characters("hello\nworld"));
    }

    #[test]
    fn test_null_byte() {
        assert!(!contains_null_byte("hello"));
        assert!(contains_null_byte("hello\0world"));
    }

    #[test]
    fn test_character_counting() {
        assert_eq!(count_characters("hello"), 5);
        assert_eq!(count_characters("héllo"), 5);
        assert_eq!(count_bytes("héllo"), 6);
    }

    #[test]
    fn test_string_length_edge_cases() {
        let rules = ValidationRules::new().with_min_length(1);
        assert!(validate_string_length("", &rules).is_err());

        let rules2 = ValidationRules::new().with_max_length(0);
        assert!(validate_string_length("", &rules2).is_ok());
        assert!(validate_string_length("a", &rules2).is_err());
    }

    #[test]
    fn test_pattern_with_unicode() {
        assert!(validate_string_pattern("café", r"^[\w\s]+$").is_ok());
        assert!(validate_string_pattern("Здравствуйте", r"^[\w\s]+$").is_ok());
    }

    #[test]
    fn test_complex_patterns() {
        let rules = ValidationRules::new().with_pattern(r"^[a-zA-Z0-9_]+$");

        assert!(validate_string_with_rules("valid_123", &rules).is_ok());
        assert!(validate_string_with_rules("valid-123", &rules).is_err());
    }
}