use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum ConvergenceCriteria {
PlatformDefault,
MaxRounds { rounds: u32 },
ConfidenceStableFor { cycles: u32 },
ConsensusAmongMembers,
Custom { signals: Vec<ConvergenceSignal> },
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ConvergenceSignal {
pub name: String,
pub description: String,
}
impl ConvergenceSignal {
pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
Self {
name: name.into(),
description: description.into(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn criteria_serializes_with_stable_kind_tags() {
let criteria = ConvergenceCriteria::ConfidenceStableFor { cycles: 3 };
let json = serde_json::to_string(&criteria).unwrap();
assert_eq!(json, r#"{"kind":"confidence_stable_for","cycles":3}"#);
let back: ConvergenceCriteria = serde_json::from_str(&json).unwrap();
assert_eq!(back, criteria);
}
#[test]
fn custom_criteria_carries_named_signals() {
let criteria = ConvergenceCriteria::Custom {
signals: vec![ConvergenceSignal::new(
"confidence_delta",
"stop when confidence delta is below threshold",
)],
};
let json = serde_json::to_value(&criteria).unwrap();
assert_eq!(json["kind"], "custom");
assert_eq!(json["signals"][0]["name"], "confidence_delta");
}
}