provenant/models/
match_score.rs1use std::fmt;
5
6use serde::{Deserialize, Serialize};
7
8#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
9#[serde(transparent)]
10pub struct MatchScore(f64);
11
12impl MatchScore {
13 pub const MAX: MatchScore = MatchScore(100.0);
14
15 pub const GOOD_THRESHOLD: f64 = 80.0;
16
17 pub fn from_percentage(value: f64) -> Self {
18 MatchScore((value * 100.0).round() / 100.0)
19 }
20
21 pub fn max(self, other: Self) -> Self {
22 if self >= other { self } else { other }
23 }
24
25 pub fn value(self) -> f64 {
26 self.0
27 }
28
29 pub fn is_good(self) -> bool {
30 self.0 >= Self::GOOD_THRESHOLD
31 }
32}
33
34impl fmt::Display for MatchScore {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 write!(f, "{:.2}", self.0)
37 }
38}
39
40impl PartialOrd for MatchScore {
41 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
42 self.0.partial_cmp(&other.0)
43 }
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49
50 #[test]
51 fn test_from_percentage() {
52 let score = MatchScore::from_percentage(95.5);
53 assert!(score.is_good());
54 }
55
56 #[test]
57 fn test_constants() {
58 assert!(MatchScore::MAX.is_good());
59 }
60
61 #[test]
62 fn test_is_good() {
63 assert!(!MatchScore::from_percentage(79.9).is_good());
64 assert!(MatchScore::from_percentage(80.0).is_good());
65 assert!(MatchScore::MAX.is_good());
66 }
67
68 #[test]
69 fn test_serde_roundtrip() {
70 let score = MatchScore::from_percentage(95.5);
71 let json = serde_json::to_string(&score).unwrap();
72 assert_eq!(json, "95.5");
73 let deserialized: MatchScore = serde_json::from_str(&json).unwrap();
74 assert_eq!(score, deserialized);
75 }
76
77 #[test]
78 fn test_display() {
79 assert_eq!(format!("{}", MatchScore::from_percentage(95.5)), "95.50");
80 assert_eq!(format!("{}", MatchScore::MAX), "100.00");
81 }
82
83 #[test]
84 fn test_value() {
85 let score = MatchScore::from_percentage(95.5);
86 assert!((score.value() - 95.5).abs() < 0.01);
87 }
88}