use super::error::MetadataError;
use super::types::MetadataValue;
pub const MAX_KEYS_PER_VECTOR: usize = 64;
pub const MAX_KEY_LENGTH: usize = 256;
pub const MAX_STRING_VALUE_LENGTH: usize = 65_536;
pub const MAX_STRING_ARRAY_LENGTH: usize = 1_024;
pub fn validate_key(key: &str) -> Result<(), MetadataError> {
if key.is_empty() {
return Err(MetadataError::EmptyKey);
}
if key.len() > MAX_KEY_LENGTH {
return Err(MetadataError::KeyTooLong {
length: key.len(),
max: MAX_KEY_LENGTH,
});
}
if key.contains('\0') {
return Err(MetadataError::InvalidKeyFormat {
key: key.replace('\0', "\\0"),
});
}
if !key.is_ascii() {
return Err(MetadataError::InvalidKeyFormat {
key: key.to_string(),
});
}
if !key.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_') {
return Err(MetadataError::InvalidKeyFormat {
key: key.to_string(),
});
}
Ok(())
}
pub fn validate_value(value: &MetadataValue) -> Result<(), MetadataError> {
match value {
MetadataValue::String(s) => {
if s.len() > MAX_STRING_VALUE_LENGTH {
return Err(MetadataError::StringValueTooLong {
length: s.len(),
max: MAX_STRING_VALUE_LENGTH,
});
}
}
MetadataValue::StringArray(arr) => {
if arr.len() > MAX_STRING_ARRAY_LENGTH {
return Err(MetadataError::ArrayTooLong {
length: arr.len(),
max: MAX_STRING_ARRAY_LENGTH,
});
}
for s in arr {
if s.len() > MAX_STRING_VALUE_LENGTH {
return Err(MetadataError::StringValueTooLong {
length: s.len(),
max: MAX_STRING_VALUE_LENGTH,
});
}
}
}
MetadataValue::Float(f) => {
if f.is_nan() {
return Err(MetadataError::InvalidFloat {
reason: "NaN not allowed",
});
}
if f.is_infinite() {
return Err(MetadataError::InvalidFloat {
reason: "Infinity not allowed",
});
}
}
MetadataValue::Integer(_) | MetadataValue::Boolean(_) => {}
}
Ok(())
}
pub fn validate_key_value(key: &str, value: &MetadataValue) -> Result<(), MetadataError> {
validate_key(key)?;
validate_value(value)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validate_key_empty() {
assert!(validate_key("").is_err());
assert!(matches!(validate_key(""), Err(MetadataError::EmptyKey)));
}
#[test]
fn test_validate_key_too_long() {
let long_key = "a".repeat(MAX_KEY_LENGTH + 1);
assert!(validate_key(&long_key).is_err());
assert!(matches!(
validate_key(&long_key),
Err(MetadataError::KeyTooLong { .. })
));
}
#[test]
fn test_validate_key_valid() {
assert!(validate_key("my_key_123").is_ok());
assert!(validate_key("a").is_ok());
assert!(validate_key("_").is_ok());
assert!(validate_key("CamelCase").is_ok());
assert!(validate_key("snake_case").is_ok());
assert!(validate_key("UPPER_CASE").is_ok());
}
#[test]
fn test_validate_key_max_length() {
let max_key = "a".repeat(MAX_KEY_LENGTH);
assert!(validate_key(&max_key).is_ok());
}
#[test]
fn test_validate_key_invalid_chars() {
assert!(validate_key("bad-key").is_err());
assert!(validate_key("with spaces").is_err());
assert!(validate_key("with.dot").is_err());
assert!(validate_key("key@symbol").is_err());
assert!(validate_key("key/slash").is_err());
}
#[test]
fn test_validate_key_unicode_rejected() {
assert!(validate_key("日本語").is_err()); assert!(validate_key("émoji").is_err()); assert!(validate_key("café").is_err()); assert!(validate_key("usеr").is_err()); }
#[test]
fn test_validate_key_null_byte_rejected() {
assert!(validate_key("valid\0key").is_err());
assert!(validate_key("\0").is_err());
assert!(validate_key("key\0").is_err());
}
#[test]
fn test_validate_key_ascii_only() {
assert!(validate_key("valid_key_123").is_ok());
assert!(validate_key("UPPERCASE").is_ok());
assert!(validate_key("lowercase").is_ok());
assert!(validate_key("_leading_underscore").is_ok());
assert!(validate_key("trailing_underscore_").is_ok());
assert!(validate_key("__double__underscore__").is_ok());
}
#[test]
fn test_validate_value_nan() {
let value = MetadataValue::Float(f64::NAN);
assert!(validate_value(&value).is_err());
assert!(matches!(
validate_value(&value),
Err(MetadataError::InvalidFloat {
reason: "NaN not allowed"
})
));
}
#[test]
fn test_validate_value_infinity() {
let value = MetadataValue::Float(f64::INFINITY);
assert!(validate_value(&value).is_err());
assert!(matches!(
validate_value(&value),
Err(MetadataError::InvalidFloat {
reason: "Infinity not allowed"
})
));
}
#[test]
fn test_validate_value_neg_infinity() {
let value = MetadataValue::Float(f64::NEG_INFINITY);
assert!(validate_value(&value).is_err());
}
#[test]
fn test_validate_value_valid_float() {
assert!(validate_value(&MetadataValue::Float(0.0)).is_ok());
assert!(validate_value(&MetadataValue::Float(-1.5)).is_ok());
assert!(validate_value(&MetadataValue::Float(f64::MAX)).is_ok());
assert!(validate_value(&MetadataValue::Float(f64::MIN)).is_ok());
assert!(validate_value(&MetadataValue::Float(f64::MIN_POSITIVE)).is_ok());
}
#[test]
fn test_validate_value_string_too_long() {
let long_string = "a".repeat(MAX_STRING_VALUE_LENGTH + 1);
let value = MetadataValue::String(long_string);
assert!(validate_value(&value).is_err());
}
#[test]
fn test_validate_value_string_max_length() {
let max_string = "a".repeat(MAX_STRING_VALUE_LENGTH);
let value = MetadataValue::String(max_string);
assert!(validate_value(&value).is_ok());
}
#[test]
fn test_validate_value_array_too_long() {
let long_array = vec!["a".to_string(); MAX_STRING_ARRAY_LENGTH + 1];
let value = MetadataValue::StringArray(long_array);
assert!(validate_value(&value).is_err());
}
#[test]
fn test_validate_value_array_max_length() {
let max_array = vec!["a".to_string(); MAX_STRING_ARRAY_LENGTH];
let value = MetadataValue::StringArray(max_array);
assert!(validate_value(&value).is_ok());
}
#[test]
fn test_validate_value_array_element_too_long() {
let long_element = "a".repeat(MAX_STRING_VALUE_LENGTH + 1);
let value = MetadataValue::StringArray(vec![long_element]);
assert!(validate_value(&value).is_err());
}
#[test]
fn test_validate_value_integer_always_valid() {
assert!(validate_value(&MetadataValue::Integer(0)).is_ok());
assert!(validate_value(&MetadataValue::Integer(i64::MAX)).is_ok());
assert!(validate_value(&MetadataValue::Integer(i64::MIN)).is_ok());
assert!(validate_value(&MetadataValue::Integer(-1)).is_ok());
}
#[test]
fn test_validate_value_boolean_always_valid() {
assert!(validate_value(&MetadataValue::Boolean(true)).is_ok());
assert!(validate_value(&MetadataValue::Boolean(false)).is_ok());
}
#[test]
fn test_validate_key_value_both_valid() {
assert!(validate_key_value("title", &MetadataValue::String("Hello".into())).is_ok());
assert!(validate_key_value("count", &MetadataValue::Integer(42)).is_ok());
}
#[test]
fn test_validate_key_value_invalid_key() {
assert!(validate_key_value("", &MetadataValue::String("Hello".into())).is_err());
}
#[test]
fn test_validate_key_value_invalid_value() {
assert!(validate_key_value("score", &MetadataValue::Float(f64::NAN)).is_err());
}
#[test]
fn test_constants_have_reasonable_values() {
assert_eq!(MAX_KEYS_PER_VECTOR, 64);
assert_eq!(MAX_KEY_LENGTH, 256);
assert_eq!(MAX_STRING_VALUE_LENGTH, 65_536);
assert_eq!(MAX_STRING_ARRAY_LENGTH, 1_024);
}
}