naru-config 0.7.0

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

pub mod type_validator {
    pub use crate::core::type_validator::*;
}

pub mod string_validator {
    pub use crate::core::string_validator::*;
}

pub fn validate_value(value: &str, field: &FieldDefinition) -> Result<(), String> {
    match field.r#type.as_str() {
        "integer" => {
            type_validator::validate_integer_with_rules(
                value,
                field
                    .validation
                    .as_ref()
                    .unwrap_or(&crate::core::schema_model::ValidationRules::new()),
            )?;
        }
        "boolean" => {
            type_validator::validate_boolean(value).map(|_| ())?;
        }
        "string" | _ => {
            if let Some(rules) = &field.validation {
                string_validator::validate_string_with_rules(value, rules)?;
            }
        }
    }

    Ok(())
}

pub fn is_valid_value(value: &str, field: &FieldDefinition) -> bool {
    validate_value(value, field).is_ok()
}

pub fn get_validation_error(value: &str, field: &FieldDefinition) -> Option<String> {
    match validate_value(value, field) {
        Ok(()) => None,
        Err(e) => Some(e),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::core::schema_model::ValidationRules;

    #[test]
    fn test_validate_integer() {
        let field = FieldDefinition {
            key: "age".into(),
            r#type: "integer".into(),
            description: None,
            validation: Some(ValidationRules {
                min_value: Some(0),
                max_value: Some(120),
                min_length: None,
                max_length: None,
                pattern: None,
            }),
            is_secret: false,
        };

        assert!(validate_value("25", &field).is_ok());
        assert!(validate_value("-1", &field).is_err());
        assert!(validate_value("121", &field).is_err());
        assert!(validate_value("abc", &field).is_err());
    }

    #[test]
    fn test_validate_string() {
        let field = FieldDefinition {
            key: "username".into(),
            r#type: "string".into(),
            description: None,
            validation: Some(ValidationRules {
                min_length: Some(3),
                max_length: Some(10),
                min_value: None,
                max_value: None,
                pattern: None,
            }),
            is_secret: false,
        };

        assert!(validate_value("admin", &field).is_ok());
        assert!(validate_value("ab", &field).is_err());
        assert!(validate_value("verylongusername", &field).is_err());
    }

    #[test]
    fn test_validate_pattern() {
        let field = FieldDefinition {
            key: "email".into(),
            r#type: "string".into(),
            description: None,
            validation: Some(ValidationRules {
                min_length: None,
                max_length: None,
                min_value: None,
                max_value: None,
                pattern: Some(r"^[\w\.-]+@[\w\.-]+\.\w+$".to_string()),
            }),
            is_secret: false,
        };

        assert!(validate_value("test@example.com", &field).is_ok());
        assert!(validate_value("invalid-email", &field).is_err());
    }

    #[test]
    fn test_validate_boolean_strict() {
        let field = FieldDefinition {
            key: "active".into(),
            r#type: "boolean".into(),
            description: None,
            validation: None,
            is_secret: false,
        };

        assert!(validate_value("true", &field).is_ok());
        assert!(validate_value("false", &field).is_ok());
        assert!(validate_value("1", &field).is_err());
        assert!(validate_value("yes", &field).is_err());
    }

    #[test]
    fn test_is_valid_value() {
        let field = FieldDefinition::new("test", "integer");

        assert!(is_valid_value("123", &field));
        assert!(!is_valid_value("abc", &field));
    }

    #[test]
    fn test_get_validation_error() {
        let field = FieldDefinition {
            key: "age".into(),
            r#type: "integer".into(),
            description: None,
            validation: Some(ValidationRules {
                min_value: Some(0),
                max_value: Some(100),
                min_length: None,
                max_length: None,
                pattern: None,
            }),
            is_secret: false,
        };

        assert!(get_validation_error("50", &field).is_none());
        assert!(get_validation_error("-1", &field).is_some());
    }

    #[test]
    fn test_combined_rules() {
        let field = FieldDefinition {
            key: "username".into(),
            r#type: "string".into(),
            description: None,
            validation: Some(ValidationRules {
                min_length: Some(5),
                max_length: Some(10),
                min_value: None,
                max_value: None,
                pattern: Some(r"^[a-z]+$".to_string()),
            }),
            is_secret: false,
        };

        assert!(validate_value("admin", &field).is_ok());
        assert!(validate_value("adm", &field).is_err());
        assert!(validate_value("ADMIN", &field).is_err());
        assert!(validate_value("administrator", &field).is_err());
    }
}