Skip to main content

tokmd_analysis_types/
churn.rs

1//! Predictive churn receipt DTOs.
2//!
3//! These contract types remain re-exported from the crate root to preserve
4//! existing `tokmd_analysis_types::...` names.
5
6use std::collections::BTreeMap;
7
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct PredictiveChurnReport {
12    pub per_module: BTreeMap<String, ChurnTrend>,
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct ChurnTrend {
17    pub slope: f64,
18    pub r2: f64,
19    pub recent_change: i64,
20    pub classification: TrendClass,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
24#[serde(rename_all = "snake_case")]
25pub enum TrendClass {
26    Rising,
27    Flat,
28    Falling,
29}
30
31#[cfg(test)]
32mod tests {
33    use super::TrendClass;
34
35    #[test]
36    fn trend_class_serde_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
37        for variant in [TrendClass::Rising, TrendClass::Flat, TrendClass::Falling] {
38            let json = serde_json::to_string(&variant)?;
39            let back: TrendClass = serde_json::from_str(&json)?;
40            assert_eq!(back, variant);
41        }
42        Ok(())
43    }
44
45    #[test]
46    fn trend_class_uses_snake_case() -> Result<(), Box<dyn std::error::Error>> {
47        assert_eq!(serde_json::to_string(&TrendClass::Rising)?, "\"rising\"");
48        Ok(())
49    }
50}