use crate::csaf::types::csaf_datetime::{CsafDateTime, ValidCsafDateTime};
use crate::csaf_traits::{CsafTrait, DocumentTrait, TrackingTrait};
use crate::validation::ValidationError;
use std::collections::HashMap;
fn create_same_timestamp_error(
index: usize,
date: &ValidCsafDateTime,
conflicting_indices: &[usize],
) -> ValidationError {
let conflicting_indices_not_current = conflicting_indices
.iter()
.filter(|idx| *idx != &index)
.map(|idx| idx.to_string())
.collect::<Vec<String>>()
.join(", ");
ValidationError {
message: format!(
"The timestamp '{date}' of this revision history item is also used by item at the position(s) {conflicting_indices_not_current}."
),
instance_path: format!("/document/tracking/revision_history/{index}/date"),
}
}
pub fn test_6_2_21_same_timestamps_in_revision_history(doc: &impl CsafTrait) -> Result<(), Vec<ValidationError>> {
let revision_history = doc.get_document().get_tracking().aggregate_revision_history();
let mut datetime_path_lookup: HashMap<&ValidCsafDateTime, Vec<(usize, &ValidCsafDateTime)>> = HashMap::new();
let mut errors: Option<Vec<ValidationError>> = None;
for item in &revision_history {
match &item.date {
CsafDateTime::Valid(valid_date) => {
match datetime_path_lookup.get_mut(valid_date) {
Some(entries) => {
entries.push((item.path_index, valid_date));
},
None => {
datetime_path_lookup.insert(valid_date, vec![(item.path_index, valid_date)]);
},
}
},
CsafDateTime::Invalid(_) => {
return Ok(()); },
}
}
for (_, entries) in datetime_path_lookup.iter().filter(|(_, entries)| entries.len() > 1) {
let indices: Vec<usize> = entries.iter().map(|(idx, _)| *idx).collect();
for (index, original_datetime) in entries {
errors
.get_or_insert_default()
.push(create_same_timestamp_error(*index, original_datetime, &indices));
}
}
errors.map_or(Ok(()), Err)
}
crate::test_validation::impl_validator!(
csaf2_1,
ValidatorForTest6_2_21,
test_6_2_21_same_timestamps_in_revision_history
);
#[cfg(test)]
mod tests {
use super::*;
use crate::csaf2_1::testcases::TESTS_2_1;
use std::str::FromStr;
#[test]
fn test_test_6_2_21() {
let date_01 = ValidCsafDateTime::from_str("2024-01-21T10:00:00.000Z").unwrap();
let conflicting_indices_01: &[usize] = &[0, 1];
let case_01_two_items_with_same_date = Err(vec![
create_same_timestamp_error(0, &date_01, conflicting_indices_01),
create_same_timestamp_error(1, &date_01, conflicting_indices_01),
]);
let date_02a = ValidCsafDateTime::from_str("2024-01-21T10:00:00.000Z").unwrap();
let date_02b = ValidCsafDateTime::from_str("2024-01-22T11:00:00.000Z").unwrap();
let conflicting_indices_02a: &[usize] = &[0, 4];
let conflicting_indices_02b: &[usize] = &[1, 3, 5];
let case_02_two_groups_with_same_date = Err(vec![
create_same_timestamp_error(0, &date_02a, conflicting_indices_02a),
create_same_timestamp_error(4, &date_02a, conflicting_indices_02a),
create_same_timestamp_error(1, &date_02b, conflicting_indices_02b),
create_same_timestamp_error(3, &date_02b, conflicting_indices_02b),
create_same_timestamp_error(5, &date_02b, conflicting_indices_02b),
]);
let conflicting_indices_03: &[usize] = &[0, 1, 2, 3, 4, 5, 6, 7];
let case_03 = Err(vec![
create_same_timestamp_error(
0,
&ValidCsafDateTime::from_str("2024-01-21T10:00:00.000Z").unwrap(),
conflicting_indices_03,
),
create_same_timestamp_error(
1,
&ValidCsafDateTime::from_str("2024-01-21T11:00:00.000+01:00").unwrap(),
conflicting_indices_03,
),
create_same_timestamp_error(
2,
&ValidCsafDateTime::from_str("2024-01-21T20:00:00.000+10:00").unwrap(),
conflicting_indices_03,
),
create_same_timestamp_error(
3,
&ValidCsafDateTime::from_str("2024-01-21T05:00:00.000-05:00").unwrap(),
conflicting_indices_03,
),
create_same_timestamp_error(
4,
&ValidCsafDateTime::from_str("2024-01-21T13:00:00.000+03:00").unwrap(),
conflicting_indices_03,
),
create_same_timestamp_error(
5,
&ValidCsafDateTime::from_str("2024-01-21T07:00:00.000-03:00").unwrap(),
conflicting_indices_03,
),
create_same_timestamp_error(
6,
&ValidCsafDateTime::from_str("2024-01-21T00:00:00.000-10:00").unwrap(),
conflicting_indices_03,
),
create_same_timestamp_error(
7,
&ValidCsafDateTime::from_str("2024-01-22T00:00:00.000+14:00").unwrap(),
conflicting_indices_03,
),
]);
let conflicting_indices_04: &[usize] = &[0, 1];
let case_04_subsecond_precision = Err(vec![
create_same_timestamp_error(
0,
&ValidCsafDateTime::from_str("2024-01-21T10:00:00.000000Z").unwrap(),
conflicting_indices_04,
),
create_same_timestamp_error(
1,
&ValidCsafDateTime::from_str("2024-01-21T10:00:00.000Z").unwrap(),
conflicting_indices_04,
),
]);
let conflicting_indices_05: &[usize] = &[0, 1];
let case_05_empty_timezone_expr = Err(vec![
create_same_timestamp_error(
0,
&ValidCsafDateTime::from_str("2024-01-21T10:00:00.000+00:00").unwrap(),
conflicting_indices_05,
),
create_same_timestamp_error(
1,
&ValidCsafDateTime::from_str("2024-01-21T10:00:00.000Z").unwrap(),
conflicting_indices_05,
),
]);
TESTS_2_1.test_6_2_21.expect(
case_01_two_items_with_same_date,
case_02_two_groups_with_same_date,
case_03,
case_04_subsecond_precision,
case_05_empty_timezone_expr,
Ok(()),
Ok(()),
Ok(()),
);
}
}