csaf-rs 0.5.0

A parser for the CSAF standard written in Rust
use crate::csaf::types::csaf_datetime::CsafDateTime::Valid;
use crate::csaf_traits::{CsafTrait, DocumentTrait, TrackingTrait};
use crate::validation::ValidationError;

fn create_older_current_release_date_error(
    current_release_date: impl std::fmt::Display,
    newest_rev_history_release_date: impl std::fmt::Display,
) -> ValidationError {
    ValidationError {
        message: format!(
            "Current release date '{current_release_date}' is older than the newest revision history date '{newest_rev_history_release_date}'"
        ),
        instance_path: "/document/tracking/current_release_date".to_string(),
    }
}

/// 6.2.6 Older Current Release Date than Revision History
///
pub fn test_6_2_06_older_current_release_than_rev_history(doc: &impl CsafTrait) -> Result<(), Vec<ValidationError>> {
    let current_release_date = doc.get_document().get_tracking().get_current_release_date();
    // TODO: Check for invalid dates here, will be done after revision history refactor, which will introduce
    // generic parsing error handling

    let mut rev_history = doc.get_document().get_tracking().aggregate_revision_history();
    rev_history.inplace_sort_by_date_then_number();
    // We can safely unwrap here because empty revision histories would not parse schema validation
    let newest_rev_history_item_date = match rev_history.last() {
        None => return Ok(()), // TODO #409 return a precondition failed here,
        Some(x) => x,
    };
    let Valid(current_release_date) = current_release_date else {
        return Ok(()); // TODO #409 return a precondition failed here
    };
    let Valid(newest_rev_history_item_date) = &newest_rev_history_item_date.date else {
        return Ok(()); // TODO #409 return a precondition failed here
    };
    if current_release_date.get_as_utc() < newest_rev_history_item_date.get_as_utc() {
        return Err(vec![create_older_current_release_date_error(
            current_release_date.get_raw_string(),
            newest_rev_history_item_date.get_raw_string(),
        )]);
    }
    Ok(())
}

crate::test_validation::impl_validator!(
    ValidatorForTest6_2_6,
    test_6_2_06_older_current_release_than_rev_history
);

#[cfg(test)]
mod tests {
    use super::*;
    use crate::csaf2_0::testcases::TESTS_2_0;
    use crate::csaf2_1::testcases::TESTS_2_1;

    #[test]
    fn test_test_6_2_06() {
        // Both CSAF 2.0 and 2.1 have test cases
        TESTS_2_0
            .test_6_2_6
            .expect(Err(vec![create_older_current_release_date_error(
                "2021-05-06T10:00:00.000Z",
                "2021-07-21T11:00:00.000Z",
            )]));
        TESTS_2_1.test_6_2_6.expect(
            Err(vec![create_older_current_release_date_error(
                "2023-09-06T10:00:00.000Z",
                "2024-01-21T11:00:00.000Z",
            )]),
            Err(vec![create_older_current_release_date_error(
                "2024-01-21T11:00:00.000+11:00",
                "2024-01-21T11:00:00.000+10:00",
            )]),
            Err(vec![create_older_current_release_date_error(
                "2024-01-22T11:00:00.000+11:30",
                "2024-01-21T13:00:00.000-11:30",
            )]),
            Ok(()),
            Ok(()),
            Ok(()),
        );
    }
}