use rustc_hash::FxHashMap;
pub(super) struct DiagnosticCollectors {
pub(super) csg_failures: std::sync::Mutex<FxHashMap<u32, Vec<ifc_lite_geometry::BoolFailure>>>,
pub(super) classification: std::sync::Mutex<ifc_lite_geometry::ClassificationStats>,
pub(super) host_diags:
std::sync::Mutex<FxHashMap<u32, ifc_lite_geometry::HostOpeningDiagnostic>>,
pub(super) rect_fast: std::sync::Mutex<ifc_lite_geometry::RectFastStats>,
pub(super) unsupported_items: std::sync::Mutex<FxHashMap<String, u64>>,
pub(super) backstop: std::sync::atomic::AtomicU64,
pub(super) oversized_ref_drops: std::sync::atomic::AtomicU64,
}
impl DiagnosticCollectors {
pub(super) fn new() -> Self {
Self {
csg_failures: std::sync::Mutex::new(FxHashMap::default()),
classification: std::sync::Mutex::new(ifc_lite_geometry::ClassificationStats::default()),
host_diags: std::sync::Mutex::new(FxHashMap::default()),
rect_fast: std::sync::Mutex::new(ifc_lite_geometry::RectFastStats::default()),
unsupported_items: std::sync::Mutex::new(FxHashMap::default()),
backstop: std::sync::atomic::AtomicU64::new(0),
oversized_ref_drops: std::sync::atomic::AtomicU64::new(0),
}
}
}
pub(super) fn collate(
classification_collector: std::sync::Mutex<ifc_lite_geometry::ClassificationStats>,
host_diag_collector: std::sync::Mutex<FxHashMap<u32, ifc_lite_geometry::HostOpeningDiagnostic>>,
rect_fast_collector: std::sync::Mutex<ifc_lite_geometry::RectFastStats>,
unsupported_item_collector: std::sync::Mutex<FxHashMap<String, u64>>,
csg_failures: &FxHashMap<u32, Vec<ifc_lite_geometry::BoolFailure>>,
oversized_ref_drops: u64,
) -> Option<ifc_lite_geometry::GeometryDiagnostics> {
const WORST_HOSTS_LIMIT: usize = 16;
let classification = classification_collector
.into_inner()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let host_diags = host_diag_collector
.into_inner()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let rect_fast = rect_fast_collector
.into_inner()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let unsupported_items = unsupported_item_collector
.into_inner()
.unwrap_or_else(|poisoned| poisoned.into_inner());
warn_if_dropped(&unsupported_items);
let diag = ifc_lite_geometry::aggregate_diagnostics(
classification,
csg_failures,
&host_diags,
rect_fast,
WORST_HOSTS_LIMIT,
oversized_ref_drops,
&unsupported_items,
);
(!diag.is_empty()).then_some(diag)
}
pub(super) fn drain_unsupported_items(
router: &ifc_lite_geometry::GeometryRouter,
collector: &std::sync::Mutex<FxHashMap<String, u64>>,
) {
let unsupported = router.take_unsupported_items();
if unsupported.is_empty() {
return;
}
let mut acc = collector
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
for (ifc_type, count) in unsupported {
*acc.entry(ifc_type).or_insert(0) += count;
}
}
pub(super) fn warn_if_dropped(unsupported_items: &FxHashMap<String, u64>) {
if unsupported_items.is_empty() {
return;
}
let total: u64 = unsupported_items.values().sum();
let breakdown = ifc_lite_geometry::format_unsupported_breakdown(unsupported_items);
tracing::warn!(
total_unsupported_items = total,
%breakdown,
"Representation items dropped (no processor, or the processor failed) — \
these elements are missing or incomplete in the output"
);
}