Skip to main content

provenant/models/
match_score.rs

1// SPDX-FileCopyrightText: Provenant contributors
2// SPDX-License-Identifier: Apache-2.0
3
4use 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}