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