use std::collections::HashSet;
use crate::config::{Configuration, KeyringConfig};
#[derive(Debug, Clone)]
pub struct ValidationResult {
pub is_valid: bool,
pub errors: Vec<ValidationError>,
pub warnings: Vec<ValidationWarning>,
}
#[derive(Debug, Clone)]
pub struct ValidationError {
pub category: ValidationCategory,
pub message: String,
pub context: Option<String>,
}
#[derive(Debug, Clone)]
pub struct ValidationWarning {
pub category: ValidationCategory,
pub message: String,
pub context: Option<String>,
}
#[derive(Debug, Clone)]
pub enum ValidationCategory {
Addressing,
Security,
GroupAddress,
}
pub struct ConfigValidator;
impl ConfigValidator {
#[must_use]
pub fn new() -> Self {
Self
}
#[must_use]
pub fn validate(&self, config: &Configuration) -> ValidationResult {
let mut result = ValidationResult {
is_valid: true,
errors: Vec::new(),
warnings: Vec::new(),
};
if let Some(ref keyring) = config.keyring {
Self::validate_keyring(keyring, &mut result);
}
result.is_valid = result.errors.is_empty();
result
}
fn validate_keyring(keyring: &KeyringConfig, result: &mut ValidationResult) {
let mut individual_addresses = HashSet::new();
for interface in &keyring.interfaces {
if !individual_addresses.insert(interface.individual_address) {
result.errors.push(ValidationError {
category: ValidationCategory::Addressing,
message: format!(
"Duplicate individual address {} in interfaces",
interface.individual_address
),
context: Some(format!("Interface: {}", interface.host)),
});
}
if interface.user_id == 0 || interface.user_id > 127 {
result.errors.push(ValidationError {
category: ValidationCategory::Security,
message: format!("Invalid user ID {} (must be 1-127)", interface.user_id),
context: Some(format!("Interface: {}", interface.host)),
});
}
if interface.user_password.is_empty() {
result.errors.push(ValidationError {
category: ValidationCategory::Security,
message: "Empty user password".to_string(),
context: Some(format!("Interface: {}", interface.host)),
});
}
}
for device in &keyring.devices {
if !individual_addresses.insert(device.individual_address) {
result.errors.push(ValidationError {
category: ValidationCategory::Addressing,
message: format!(
"Duplicate individual address {} in devices",
device.individual_address
),
context: device.serial_number.clone(),
});
}
}
let mut group_addresses = HashSet::new();
for ga in &keyring.group_addresses {
if !group_addresses.insert(ga.address) {
result.warnings.push(ValidationWarning {
category: ValidationCategory::GroupAddress,
message: format!("Duplicate group address {}", ga.address),
context: ga.description.clone(),
});
}
if ga.key.is_empty() {
result.errors.push(ValidationError {
category: ValidationCategory::Security,
message: format!("Empty security key for group address {}", ga.address),
context: ga.description.clone(),
});
}
}
}
}
impl Default for ConfigValidator {
fn default() -> Self {
Self::new()
}
}
impl ValidationResult {
#[must_use]
pub fn is_ok(&self) -> bool {
self.is_valid
}
#[must_use]
pub fn error_count(&self) -> usize {
self.errors.len()
}
#[must_use]
pub fn warning_count(&self) -> usize {
self.warnings.len()
}
#[must_use]
pub fn error_messages(&self) -> Vec<String> {
self.errors.iter().map(|e| e.message.clone()).collect()
}
#[must_use]
pub fn warning_messages(&self) -> Vec<String> {
self.warnings.iter().map(|w| w.message.clone()).collect()
}
}