zescrow_core/condition/threshold.rs
1use bincode::{Decode, Encode};
2#[cfg(feature = "json")]
3use serde::{Deserialize, Serialize};
4
5use super::Condition;
6
7/// N-of-M threshold condition.
8///
9/// Satisfied when at least `threshold` of the `subconditions` verify
10/// successfully. Subconditions can be any [`Condition`] variant,
11/// including nested thresholds.
12#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
13#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
14pub struct Threshold {
15 /// Minimum number of valid subconditions required.
16 pub threshold: usize,
17
18 /// Subconditions to evaluate.
19 pub subconditions: Vec<Condition>,
20}
21
22impl Threshold {
23 /// Verifies that at least `threshold` subconditions are satisfied.
24 ///
25 /// Returns `Ok(())` if the threshold is met, or `Err` with details
26 /// about how many conditions passed versus required.
27 ///
28 /// A threshold of zero is always satisfied, regardless of subconditions.
29 pub fn verify(&self) -> Result<(), Error> {
30 (self.threshold == 0)
31 .then_some(())
32 .map(Ok)
33 .unwrap_or_else(|| self.verify_threshold())
34 }
35
36 /// Counts satisfied subconditions and checks against threshold.
37 fn verify_threshold(&self) -> Result<(), Error> {
38 let satisfied = self.count_satisfied();
39
40 (satisfied >= self.threshold)
41 .then_some(())
42 .ok_or(Error::ThresholdNotMet {
43 required: self.threshold,
44 satisfied,
45 })
46 }
47
48 /// Counts the number of subconditions that verify successfully.
49 fn count_satisfied(&self) -> usize {
50 self.subconditions
51 .iter()
52 .filter_map(|c| c.verify().ok())
53 .count()
54 }
55}
56
57/// Threshold conditions verification errors.
58#[derive(Debug, thiserror::Error)]
59pub enum Error {
60 /// Fewer than the required number of subconditions were satisfied.
61 #[error("needed at least {required} passes, but only {satisfied} succeeded")]
62 ThresholdNotMet {
63 /// Minimum number of valid subconditions required.
64 required: usize,
65 /// Number of verified subconditions.
66 satisfied: usize,
67 },
68}