conjure_oxide/
rule_trace_aggregates.rs1use std::fmt::Write as _;
3use std::fs::{self, File};
4use std::path::PathBuf;
5
6use anyhow::Context as _;
7use conjure_cp_cli::utils::rule_trace::RuleApplicationCounter;
8
9#[derive(Clone)]
11pub struct RuleTraceAggregatesHandle {
12 path: PathBuf,
13 counter: RuleApplicationCounter,
14}
15
16impl RuleTraceAggregatesHandle {
17 pub fn new(path: PathBuf) -> anyhow::Result<Self> {
19 File::create(&path)
20 .with_context(|| format!("Unable to create aggregate trace file {}", path.display()))?;
21 Ok(Self {
22 path,
23 counter: RuleApplicationCounter::default(),
24 })
25 }
26
27 pub fn layer(&self) -> RuleApplicationCounter {
29 self.counter.clone()
30 }
31
32 pub fn flush(&self) {
34 self.write().expect("failed to write rule trace aggregates")
35 }
36}
37
38impl RuleTraceAggregatesHandle {
39 fn write(&self) -> anyhow::Result<()> {
40 let counts = self.counter.snapshot();
41 let mut rows: Vec<_> = counts.iter().collect();
42 rows.sort_by(|(rule_name_a, count_a), (rule_name_b, count_b)| {
43 count_b
44 .cmp(count_a)
45 .then_with(|| rule_name_a.cmp(rule_name_b))
46 });
47
48 let mut contents = format!(
49 "total_rule_applications: {}\n",
50 counts.values().sum::<u64>()
51 );
52 for (rule_name, count) in rows {
53 writeln!(contents, "{count:6} {rule_name}")?;
54 }
55
56 fs::write(&self.path, contents).with_context(|| {
57 format!(
58 "Unable to write aggregate trace file {}",
59 self.path.display()
60 )
61 })
62 }
63}