use crate::schema_validation::{SchemaError, SchemaValidator};
use crate::schemas::get_schema_info;
use matchy_format::DatabaseBuilder;
pub trait DatabaseBuilderExt: Sized {
fn with_schema(self, schema_name: &str) -> Result<Self, SchemaError>;
}
impl DatabaseBuilderExt for DatabaseBuilder {
fn with_schema(self, schema_name: &str) -> Result<Self, SchemaError> {
let validator = SchemaValidator::new(schema_name)?;
let db_type = get_schema_info(schema_name)
.map(|info| info.database_type)
.unwrap_or(schema_name);
Ok(self
.with_database_type(db_type)
.with_validator(Box::new(validator)))
}
}
#[cfg(test)]
mod tests {
use super::*;
use matchy_data_format::DataValue;
use matchy_match_mode::MatchMode;
use std::collections::HashMap;
fn valid_threatdb_data() -> HashMap<String, DataValue> {
let mut data = HashMap::new();
data.insert(
"threat_level".to_string(),
DataValue::String("high".to_string()),
);
data.insert(
"category".to_string(),
DataValue::String("malware".to_string()),
);
data.insert(
"source".to_string(),
DataValue::String("abuse.ch".to_string()),
);
data
}
#[test]
fn test_with_schema_creates_validator() {
let builder = DatabaseBuilder::new(MatchMode::CaseSensitive)
.with_schema("threatdb")
.expect("should create builder with schema");
let mut builder = builder;
let data = valid_threatdb_data();
assert!(builder.add_entry("1.2.3.4", data).is_ok());
}
#[test]
fn test_with_schema_unknown() {
let result = DatabaseBuilder::new(MatchMode::CaseSensitive).with_schema("nonexistent");
assert!(result.is_err());
}
#[test]
fn test_with_schema_validates_entries() {
let mut builder = DatabaseBuilder::new(MatchMode::CaseSensitive)
.with_schema("threatdb")
.expect("should create builder");
let valid_data = valid_threatdb_data();
assert!(builder.add_entry("1.2.3.4", valid_data).is_ok());
let mut invalid_data = HashMap::new();
invalid_data.insert(
"threat_level".to_string(),
DataValue::String("super-critical".to_string()), );
let result = builder.add_entry("2.3.4.5", invalid_data);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.to_string().contains("Validation error"));
}
#[test]
fn test_with_schema_validates_all_add_methods() {
let mut builder = DatabaseBuilder::new(MatchMode::CaseSensitive)
.with_schema("threatdb")
.expect("should create builder");
let valid_data = valid_threatdb_data();
assert!(builder.add_ip("10.0.0.1", valid_data.clone()).is_ok());
assert!(builder
.add_literal("evil.example.com", valid_data.clone())
.is_ok());
assert!(builder.add_glob("*.evil.com", valid_data.clone()).is_ok());
let invalid_data = HashMap::new();
assert!(builder.add_ip("10.0.0.2", invalid_data.clone()).is_err());
assert!(builder
.add_literal("bad.example.com", invalid_data.clone())
.is_err());
assert!(builder.add_glob("*.bad.com", invalid_data.clone()).is_err());
}
#[test]
fn test_builder_without_schema_no_validation() {
let mut builder = DatabaseBuilder::new(MatchMode::CaseSensitive);
let empty_data = HashMap::new();
assert!(builder.add_entry("1.2.3.4", empty_data).is_ok());
let mut arbitrary_data = HashMap::new();
arbitrary_data.insert("foo".to_string(), DataValue::String("bar".to_string()));
assert!(builder.add_entry("2.3.4.5", arbitrary_data).is_ok());
}
}