Skip to main content

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}