brep_kernel/props/
diagnostics.rs1use serde::{Deserialize, Serialize};
2use std::collections::BTreeMap;
3
4#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
5#[serde(rename_all = "lowercase")]
6pub enum DiagnosticSeverity {
7 Info,
8 Warning,
9 Degraded,
10 Error,
11}
12
13#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
14#[serde(rename_all = "snake_case")]
15pub enum KernelStage {
16 Collect,
17 Classify,
18 Intersect,
19 Refine,
20 Fragment,
21 Select,
22 Sew,
23 Validate,
24 Export,
25}
26
27#[derive(Clone, Debug, Deserialize, Serialize)]
28pub struct DiagnosticEvent {
29 pub severity: DiagnosticSeverity,
30 pub stage: KernelStage,
31 pub code: String,
33 pub message: String,
34}
35
36#[derive(Clone, Debug, Default, Deserialize, Serialize)]
37pub struct KernelDiagnostics {
38 pub events: Vec<DiagnosticEvent>,
39 pub counters: BTreeMap<String, u64>,
40 pub measurements: BTreeMap<String, f64>,
41}
42
43impl KernelDiagnostics {
44 pub fn event(
45 &mut self,
46 severity: DiagnosticSeverity,
47 stage: KernelStage,
48 code: impl Into<String>,
49 message: impl Into<String>,
50 ) {
51 self.events.push(DiagnosticEvent {
52 severity,
53 stage,
54 code: code.into(),
55 message: message.into(),
56 });
57 }
58
59 pub fn count(&mut self, code: impl Into<String>) {
60 *self.counters.entry(code.into()).or_default() += 1;
61 }
62
63 pub fn count_n(&mut self, code: impl Into<String>, amount: u64) {
64 *self.counters.entry(code.into()).or_default() += amount;
65 }
66
67 pub fn measure_max(&mut self, code: impl Into<String>, value: f64) {
68 let entry = self.measurements.entry(code.into()).or_insert(value);
69 *entry = entry.max(value);
70 }
71
72 pub fn worst(&self) -> Option<DiagnosticSeverity> {
73 self.events.iter().map(|event| event.severity).max()
74 }
75
76 pub fn shippable(&self) -> bool {
77 self.events
78 .iter()
79 .all(|event| event.severity < DiagnosticSeverity::Error)
80 }
81}
82
83#[derive(Clone, Debug, Deserialize, Serialize)]
84pub struct KernelOutcome<T> {
85 pub value: T,
86 pub diagnostics: KernelDiagnostics,
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn diagnostics_are_stable_and_queryable() {
95 let mut diagnostics = KernelDiagnostics::default();
96 diagnostics.count("intersect.pairs");
97 diagnostics.count_n("intersect.pairs", 2);
98 diagnostics.measure_max("validate.edge_error", 0.1);
99 diagnostics.measure_max("validate.edge_error", 0.05);
100 diagnostics.event(
101 DiagnosticSeverity::Warning,
102 KernelStage::Classify,
103 "pair.near_tangent",
104 "dedicated path selected",
105 );
106 assert_eq!(diagnostics.counters["intersect.pairs"], 3);
107 assert_eq!(diagnostics.measurements["validate.edge_error"], 0.1);
108 assert!(diagnostics.shippable());
109 }
110}