chrony-confile 0.1.0

A full-featured Rust library for parsing, editing, validating, and serializing chrony configuration files
Documentation
//! Semantic validation across multiple directives.
//!
//! Validates cross-directive constraints such as ensuring the number of `ntsservercert`
//! and `ntsserverkey` directives match.

use crate::ast::*;
use crate::error::DirectiveError;
use crate::span::Span;

/// Validates cross-directive semantic constraints.
///
/// Checks:
/// - The number of `ntsservercert` and `ntsserverkey` directives must match
pub fn validate_config(config: &ChronyConfig) -> Vec<DirectiveError> {
    let mut errors = Vec::new();
    validate_nts_cert_key_count(config, &mut errors);
    errors
}

fn validate_nts_cert_key_count(config: &ChronyConfig, errors: &mut Vec<DirectiveError>) {
    let directives: Vec<&DirectiveKind> = config
        .nodes
        .iter()
        .filter_map(|n| match n {
            ConfigNode::Directive(d) => Some(&d.kind),
            _ => None,
        })
        .collect();

    let cert_count = directives
        .iter()
        .filter(|k| matches!(k, DirectiveKind::NtsServerCert(_)))
        .count();
    let key_count = directives
        .iter()
        .filter(|k| matches!(k, DirectiveKind::NtsServerKey(_)))
        .count();

    if cert_count != key_count {
        errors.push(DirectiveError::InvalidValue {
            directive: "ntsservercert/ntsserverkey".into(),
            expected: "equal number of cert and key directives",
            got: format!("{cert_count} certs, {key_count} keys"),
            span: Span::new(None, 0, 0, 0),
        });
    }
}