use crate::model::{Component, NormalizedSbom};
pub(crate) fn manufacturer_scope_components(sbom: &NormalizedSbom) -> Vec<&Component> {
let mut incoming = std::collections::HashSet::new();
for edge in &sbom.edges {
incoming.insert(&edge.to);
}
let primary = sbom.primary_component_id.as_ref();
let scope: Vec<&Component> = sbom
.components
.iter()
.filter(|(id, _)| primary.is_some_and(|p| p == *id) || !incoming.contains(id))
.map(|(_, c)| c)
.collect();
if scope.is_empty() {
return sbom.components.values().collect();
}
scope
}
pub(crate) fn known_component_name(comp: &Component) -> bool {
let name = comp.name.trim();
if name.is_empty() || name.eq_ignore_ascii_case("NOASSERTION") {
return false;
}
if !(name.eq_ignore_ascii_case("NONE") || name.eq_ignore_ascii_case("UNKNOWN")) {
return true;
}
comp.identifiers.purl.as_deref().is_some_and(|purl| {
purl.rsplit('/')
.next()
.and_then(|seg| seg.split('@').next())
.is_some_and(|purl_name| purl_name.eq_ignore_ascii_case(name))
})
}
pub(crate) fn truncate_list(items: &[String], max: usize) -> String {
if items.len() <= max {
items.join(", ")
} else {
let head = items[..max].join(", ");
let rest = items.len() - max;
format!("{head}, …and {rest} more")
}
}
pub(crate) fn known_value(value: Option<&str>) -> Option<&str> {
let v = value?.trim();
if v.is_empty()
|| v.eq_ignore_ascii_case("NOASSERTION")
|| v.eq_ignore_ascii_case("NONE")
|| v.eq_ignore_ascii_case("UNKNOWN")
{
return None;
}
Some(v)
}
pub(crate) fn has_known_value(value: &Option<String>) -> bool {
known_value(value.as_deref()).is_some()
}
pub(crate) fn has_known_supplier(
supplier: &Option<crate::model::Organization>,
author: &Option<String>,
) -> bool {
supplier
.as_ref()
.is_some_and(|s| known_value(Some(s.name.as_str())).is_some())
|| has_known_value(author)
}
pub(crate) fn is_valid_email_format(email: &str) -> bool {
if email.contains(' ') || email.is_empty() {
return false;
}
let parts: Vec<&str> = email.split('@').collect();
if parts.len() != 2 {
return false;
}
let local = parts[0];
let domain = parts[1];
if local.is_empty() {
return false;
}
if domain.is_empty()
|| !domain.contains('.')
|| domain.starts_with('.')
|| domain.ends_with('.')
{
return false;
}
true
}