Skip to main content

conjure_oxide/
rule_trace_aggregates.rs

1//! Counts rule applications in memory and writes the totals once, when the CLI exits.
2use 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/// Owns the CLI output path and shared rule application counter.
10#[derive(Clone)]
11pub struct RuleTraceAggregatesHandle {
12    path: PathBuf,
13    counter: RuleApplicationCounter,
14}
15
16impl RuleTraceAggregatesHandle {
17    /// Create the output file before rewriting, so an unusable path fails early.
18    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    /// A layer sharing the counts written by this handle.
28    pub fn layer(&self) -> RuleApplicationCounter {
29        self.counter.clone()
30    }
31
32    /// Write the counts collected so far.
33    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}