use crate::csaf_traits::{BranchTrait, CategoryOfTheBranch, CsafTrait, ProductTreeTrait, build_leaf_instance_path};
use crate::validation::ValidationError;
fn format_category_path(categories: &[CategoryOfTheBranch]) -> String {
categories
.iter()
.map(|c| c.to_string())
.collect::<Vec<_>>()
.join(" -> ")
}
fn create_branch_categories_error(
full_path: &[CategoryOfTheBranch],
relevant_categories: &[CategoryOfTheBranch],
instance_path: String,
) -> ValidationError {
let full_display = format_category_path(full_path);
let found_display = if relevant_categories.is_empty() {
"(none)".to_string()
} else {
format_category_path(relevant_categories)
};
ValidationError {
message: format!(
"Branch path to product does not follow the recommended sequence of the categories 'vendor' -> 'product_name' -> 'product_version'. Along the categories of this path '{full_display}', the following sequence was found: '{found_display}'"
),
instance_path,
}
}
const REQUIRED_CATEGORIES_ORDER: [CategoryOfTheBranch; 3] = [
CategoryOfTheBranch::Vendor,
CategoryOfTheBranch::ProductName,
CategoryOfTheBranch::ProductVersion,
];
pub fn test_6_3_9_branch_categories(doc: &impl CsafTrait) -> Result<(), Vec<ValidationError>> {
let mut errors: Option<Vec<ValidationError>> = None;
let Some(product_tree) = doc.get_product_tree() else {
return Ok(()); };
let leaf_paths = product_tree.collect_leaf_paths();
for (path, indices) in leaf_paths {
let mut relevant: Vec<CategoryOfTheBranch> = path
.iter()
.map(|b| b.get_category())
.filter(|c| REQUIRED_CATEGORIES_ORDER.contains(c))
.collect();
relevant.dedup();
if !relevant.iter().eq(REQUIRED_CATEGORIES_ORDER.iter()) {
let all_categories: Vec<CategoryOfTheBranch> = path.iter().map(|b| b.get_category()).collect();
errors.get_or_insert_default().push(create_branch_categories_error(
&all_categories,
&relevant,
build_leaf_instance_path(&indices),
));
}
}
errors.map_or(Ok(()), Err)
}
crate::test_validation::impl_validator!(ValidatorForTest6_3_9, test_6_3_9_branch_categories);
#[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_3_9() {
let case_01_missing_product_version = Err(vec![create_branch_categories_error(
&[
CategoryOfTheBranch::Vendor,
CategoryOfTheBranch::ProductName,
CategoryOfTheBranch::PatchLevel,
],
&[CategoryOfTheBranch::Vendor, CategoryOfTheBranch::ProductName],
"/product_tree/branches/0/branches/0/branches/0/product".to_string(),
)]);
let case_02_missing_vendor = Err(vec![
create_branch_categories_error(
&[
CategoryOfTheBranch::ProductFamily,
CategoryOfTheBranch::ProductName,
CategoryOfTheBranch::ProductVersion,
],
&[CategoryOfTheBranch::ProductName, CategoryOfTheBranch::ProductVersion],
"/product_tree/branches/0/branches/0/branches/0/product".to_string(),
),
create_branch_categories_error(
&[
CategoryOfTheBranch::ProductFamily,
CategoryOfTheBranch::ProductName,
CategoryOfTheBranch::ProductVersion,
],
&[CategoryOfTheBranch::ProductName, CategoryOfTheBranch::ProductVersion],
"/product_tree/branches/0/branches/0/branches/1/product".to_string(),
),
]);
let case_03_missing_vendor_wrong_order = Err(vec![
create_branch_categories_error(
&[
CategoryOfTheBranch::ProductFamily,
CategoryOfTheBranch::ProductVersion,
CategoryOfTheBranch::ProductName,
],
&[CategoryOfTheBranch::ProductVersion, CategoryOfTheBranch::ProductName],
"/product_tree/branches/0/branches/0/branches/0/product".to_string(),
),
create_branch_categories_error(
&[
CategoryOfTheBranch::ProductFamily,
CategoryOfTheBranch::ProductVersion,
CategoryOfTheBranch::ProductName,
],
&[CategoryOfTheBranch::ProductVersion, CategoryOfTheBranch::ProductName],
"/product_tree/branches/0/branches/0/branches/1/product".to_string(),
),
]);
let case_04_categories: &[CategoryOfTheBranch] = &[
CategoryOfTheBranch::Vendor,
CategoryOfTheBranch::ProductVersion,
CategoryOfTheBranch::ProductName,
];
let case_04_wrong_order = Err(vec![
create_branch_categories_error(
case_04_categories,
case_04_categories,
"/product_tree/branches/0/branches/0/branches/0/product".to_string(),
),
create_branch_categories_error(
case_04_categories,
case_04_categories,
"/product_tree/branches/0/branches/0/branches/1/product".to_string(),
),
]);
let case_05_full: &[CategoryOfTheBranch] = &[
CategoryOfTheBranch::HostName,
CategoryOfTheBranch::Vendor,
CategoryOfTheBranch::ProductVersion,
CategoryOfTheBranch::Language,
CategoryOfTheBranch::ProductName,
CategoryOfTheBranch::Architecture,
CategoryOfTheBranch::ServicePack,
CategoryOfTheBranch::PatchLevel,
];
let case_05_relevant = &[
CategoryOfTheBranch::Vendor,
CategoryOfTheBranch::ProductVersion,
CategoryOfTheBranch::ProductName,
];
let case_05_wrong_order_deep_tree = Err(vec![
create_branch_categories_error(
case_05_full,
case_05_relevant,
"/product_tree/branches/0/branches/0/branches/0/branches/0/branches/0/branches/0/branches/0/branches/0/product".to_string(),
),
create_branch_categories_error(
case_05_full,
case_05_relevant,
"/product_tree/branches/0/branches/0/branches/0/branches/0/branches/0/branches/1/branches/0/branches/0/product".to_string(),
),
]);
let case_06_missing_vendor_name_version = Err(vec![
create_branch_categories_error(
&[
CategoryOfTheBranch::HostName,
CategoryOfTheBranch::Architecture,
CategoryOfTheBranch::Language,
],
&[],
"/product_tree/branches/0/branches/0/branches/0/product".to_string(),
),
create_branch_categories_error(
&[
CategoryOfTheBranch::HostName,
CategoryOfTheBranch::Architecture,
CategoryOfTheBranch::ServicePack,
CategoryOfTheBranch::PatchLevel,
],
&[],
"/product_tree/branches/0/branches/1/branches/0/branches/0/product".to_string(),
),
]);
let case_s01_stacked_wrong_order = Err(vec![create_branch_categories_error(
&[
CategoryOfTheBranch::Vendor,
CategoryOfTheBranch::ProductName,
CategoryOfTheBranch::Vendor,
CategoryOfTheBranch::ProductVersion,
],
&[
CategoryOfTheBranch::Vendor,
CategoryOfTheBranch::ProductName,
CategoryOfTheBranch::Vendor,
CategoryOfTheBranch::ProductVersion,
],
"/product_tree/branches/0/branches/0/branches/0/branches/0/product".to_string(),
)]);
TESTS_2_0.test_6_3_9.expect(
case_01_missing_product_version.clone(),
case_02_missing_vendor.clone(),
case_03_missing_vendor_wrong_order.clone(),
case_04_wrong_order.clone(),
case_05_wrong_order_deep_tree.clone(),
case_06_missing_vendor_name_version.clone(),
case_s01_stacked_wrong_order.clone(),
Ok(()),
Ok(()),
Ok(()),
Ok(()),
Ok(()),
Ok(()),
);
TESTS_2_1.test_6_3_9.expect(
case_01_missing_product_version,
case_02_missing_vendor,
case_03_missing_vendor_wrong_order,
case_04_wrong_order,
case_05_wrong_order_deep_tree,
case_06_missing_vendor_name_version,
case_s01_stacked_wrong_order,
Ok(()),
Ok(()),
Ok(()),
Ok(()),
Ok(()),
Ok(()),
);
}
}