sdivi_core/input/threshold_input.rs
1//! Threshold input structs for the pure-compute API.
2
3use std::collections::BTreeMap;
4
5use serde::{Deserialize, Serialize};
6
7// ── Threshold inputs ──────────────────────────────────────────────────────────
8/// Per-category threshold override for [`ThresholdsInput`].
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
10pub struct ThresholdOverrideInput {
11 /// Overridden pattern entropy rate (uses default if absent).
12 #[serde(skip_serializing_if = "Option::is_none")]
13 pub pattern_entropy_rate: Option<f64>,
14 /// Overridden convention drift rate.
15 #[serde(skip_serializing_if = "Option::is_none")]
16 pub convention_drift_rate: Option<f64>,
17 /// Overridden coupling delta rate.
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub coupling_delta_rate: Option<f64>,
20 /// Overridden boundary violation rate.
21 #[serde(skip_serializing_if = "Option::is_none")]
22 pub boundary_violation_rate: Option<f64>,
23 /// ISO-8601 expiry date (`"YYYY-MM-DD"`).
24 pub expires: String,
25}
26
27/// Threshold configuration for [`crate::compute_thresholds_check`].
28///
29/// The caller supplies `today` explicitly — no clock access in sdivi-core.
30///
31/// **IMPORTANT:** `ThresholdsInput::default()` sets `today` to a far-future
32/// sentinel (`9999-12-31`) so that all per-category overrides are treated as
33/// expired (i.e., the global rates apply). Callers that use per-category
34/// overrides MUST supply the real current date:
35///
36/// ```rust
37/// # use chrono::NaiveDate;
38/// use sdivi_core::input::ThresholdsInput;
39///
40/// let today = NaiveDate::from_ymd_opt(2026, 4, 30).unwrap();
41/// let t = ThresholdsInput { today, ..ThresholdsInput::default() };
42/// assert_eq!(t.pattern_entropy_rate, 2.0);
43/// ```
44#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
45pub struct ThresholdsInput {
46 /// Maximum allowed pattern entropy rate.
47 pub pattern_entropy_rate: f64,
48 /// Maximum allowed convention drift rate.
49 pub convention_drift_rate: f64,
50 /// Maximum allowed coupling delta rate.
51 pub coupling_delta_rate: f64,
52 /// Maximum allowed boundary violation rate.
53 pub boundary_violation_rate: f64,
54 /// Per-category overrides (may include expired entries — `today` determines which apply).
55 #[serde(default)]
56 pub overrides: BTreeMap<String, ThresholdOverrideInput>,
57 /// Today's date for expiry evaluation. Caller supplies this (no clock in sdivi-core).
58 /// `Default` uses `9999-12-31`; override with the real date to enable per-category filtering.
59 pub today: chrono::NaiveDate,
60}
61
62impl Default for ThresholdsInput {
63 fn default() -> Self {
64 ThresholdsInput {
65 pattern_entropy_rate: 2.0,
66 convention_drift_rate: 3.0,
67 coupling_delta_rate: 0.15,
68 boundary_violation_rate: 2.0,
69 overrides: BTreeMap::new(),
70 // Far-future sentinel — callers must supply the real `today` to enable override filtering.
71 today: chrono::NaiveDate::from_ymd_opt(9999, 12, 31).unwrap(),
72 }
73 }
74}