pub struct ComparisonPolicy {
pub default_percent: Option<f64>,
pub per_metric_percent: BTreeMap<String, f64>,
}Expand description
Re-export of the comparison-policy types so downstream crates
using ktstr::cli as their public surface don’t need to reach
into the internal ktstr::stats module (which is pub(crate) —
see lib.rs — and therefore not a stable public path). The
policy is the only item in stats that a CLI or external
consumer constructs directly; every other item is internal
plumbing reached via cli::compare_partitions.
Per-metric threshold policy driving compare_rows /
compare_partitions.
Resolution priority for a given metric’s relative significance threshold, highest first:
per_metric_percent[metric_name]— explicit override for this metric.default_percent— uniform override across every metric not listed in the map (equivalent to the old--threshold NCLI flag).- The metric’s built-in
default_relfrom theMETRICSregistry — the “no policy” fallback.
Values in the struct are stored as PERCENT (e.g. 10.0 meaning
10%), NOT fractions. Self::rel_threshold does the /100.0
conversion so every caller inside compare_rows reads a
fraction without re-deriving the division.
Note on the registry-fallback branch: the default_rel field
on MetricDef is already a FRACTION (e.g. 0.25 for 25%),
not a percent. rel_threshold returns it verbatim — it
does NOT divide by 100. Only the override branches
(per-metric map, default_percent) do the percent-to-fraction
conversion because their inputs are percents. This asymmetry
is deliberate so callers supplying CLI/file-based overrides
work in human-intuitive percent units while the registry
defaults (which already ship in fraction form) pass through
unchanged.
The struct is serde::Serialize / serde::Deserialize so
cargo ktstr perf-delta --policy <path> can load a
JSON-persisted policy file. Default construction produces an
empty policy that uses every registry default; Self::uniform
reproduces the old --threshold N behaviour without any
per-metric override plumbing at the call site.
Fields§
§default_percent: Option<f64>Uniform override: when Some(p), every metric whose name is
NOT in Self::per_metric_percent uses p / 100.0 as its
relative threshold. None falls through to the registry
default_rel. Stored as percent (e.g. 10.0 for 10%).
per_metric_percent: BTreeMap<String, f64>Per-metric overrides keyed by metric name. Each value is a
percent (e.g. 15.0 → 15%). An entry here takes precedence
over both Self::default_percent and the registry
default_rel.
Implementations§
Source§impl ComparisonPolicy
impl ComparisonPolicy
Sourcepub fn new() -> Self
pub fn new() -> Self
Empty policy — every metric uses its METRICS registry
default. Equivalent to the old --threshold None CLI path.
Sourcepub fn uniform(percent: f64) -> Self
pub fn uniform(percent: f64) -> Self
Uniform override: every metric uses percent / 100.0.
Mirrors the old --threshold N CLI behaviour; the CLI
dispatch at cargo ktstr perf-delta --threshold N
constructs a policy via this constructor.
Sourcepub fn load_json(path: &Path) -> Result<Self>
pub fn load_json(path: &Path) -> Result<Self>
Load a JSON-persisted policy from a file. Errors propagate
the read / parse reason as an anyhow::Error with the file
path in the context chain so a malformed --policy path.json
surfaces an actionable message rather than a generic
“invalid JSON.”
Validates after parsing via Self::validate: rejects
negative thresholds (a misconfigured 10 vs -10 would
invert the dual-gate logic at the .abs() >= rel_thresh
check and silently classify every metric as significant)
and rejects per-metric keys not registered in METRICS
(a typo like "wrost_spread" would otherwise be silently
ignored — the key simply never matches during resolution
and the metric falls through to default_percent).
Sourcepub fn validate(&self) -> Result<()>
pub fn validate(&self) -> Result<()>
Structural validation separate from parsing so both the
load_json path and programmatic constructors (after
Self::uniform with a user-supplied percent) can share
one set of invariants without re-implementing checks at
each call site. Called automatically by Self::load_json;
CLI dispatch should call it after constructing via
Self::uniform to catch --threshold -10 at the
entry point rather than deep inside compare_rows where
the dual-gate math silently misbehaves.
Rejects:
- Negative
default_percent(nonsensical — thresholds are absolute-value comparisons). - Negative entries in
per_metric_percent. - Per-metric keys not in the
METRICSregistry (silent typos would otherwise fall through todefault_percentunnoticed).
Sourcepub fn from_cli_flags(
threshold: Option<f64>,
policy: Option<&Path>,
) -> Result<Self>
pub fn from_cli_flags( threshold: Option<f64>, policy: Option<&Path>, ) -> Result<Self>
Resolve the mutually-exclusive --threshold / --policy CLI
pair into a policy: --threshold N is sugar for a uniform N%
default (validated for sign); --policy PATH loads a
per-metric JSON policy; neither falls through to the registry
defaults. Shared by every subcommand that accepts the pair
(perf-delta) so the resolution rules — and
the “exactly one of the two” contract — live in one place.
Both flags set is rejected with an error. At the CLI call
sites clap conflicts_with makes that unreachable, but this is
a library entry point and must not panic on its inputs; the
error is the defence-in-depth backstop.
Sourcepub fn rel_threshold(&self, metric_name: &str, default_rel: f64) -> f64
pub fn rel_threshold(&self, metric_name: &str, default_rel: f64) -> f64
Resolve the relative threshold (as a fraction, e.g. 0.10
for 10%) for metric_name with default_rel as the
registry-level fallback. Handles the percent→fraction
conversion so compare_rows_by does not need to re-derive
p / 100.0 at every call site.
Trait Implementations§
Source§impl Clone for ComparisonPolicy
impl Clone for ComparisonPolicy
Source§fn clone(&self) -> ComparisonPolicy
fn clone(&self) -> ComparisonPolicy
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for ComparisonPolicy
impl Debug for ComparisonPolicy
Source§impl Default for ComparisonPolicy
impl Default for ComparisonPolicy
Source§fn default() -> ComparisonPolicy
fn default() -> ComparisonPolicy
Source§impl<'de> Deserialize<'de> for ComparisonPolicywhere
ComparisonPolicy: Default,
impl<'de> Deserialize<'de> for ComparisonPolicywhere
ComparisonPolicy: Default,
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Auto Trait Implementations§
impl Freeze for ComparisonPolicy
impl RefUnwindSafe for ComparisonPolicy
impl Send for ComparisonPolicy
impl Sync for ComparisonPolicy
impl Unpin for ComparisonPolicy
impl UnwindSafe for ComparisonPolicy
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more