1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6pub enum FitVerdict {
7 RunsWell,
9 TightFit,
11 TooLarge,
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub struct FitAssessment {
18 pub verdict: FitVerdict,
20 pub required_bytes: i64,
22}
23
24impl FitVerdict {
25 pub fn as_str(&self) -> &'static str {
27 match self {
28 FitVerdict::RunsWell => "runs_well",
29 FitVerdict::TightFit => "tight_fit",
30 FitVerdict::TooLarge => "too_large",
31 }
32 }
33
34 const MEMORY_OVERHEAD_FACTOR: f64 = 1.25;
36 const RUNS_WELL_FRACTION: f64 = 0.75;
38 const TIGHT_FIT_FRACTION: f64 = 0.95;
40
41 pub fn assess(footprint_bytes: Option<i64>, total_memory_bytes: u64) -> Option<FitAssessment> {
45 let footprint_bytes = footprint_bytes.filter(|&bytes| bytes > 0)?;
46 if total_memory_bytes == 0 {
47 return None;
48 }
49 let required_bytes = (footprint_bytes as f64 * Self::MEMORY_OVERHEAD_FACTOR) as i64;
50 let share = required_bytes as f64 / total_memory_bytes as f64;
51 let verdict = if share < Self::RUNS_WELL_FRACTION {
52 FitVerdict::RunsWell
53 } else if share < Self::TIGHT_FIT_FRACTION {
54 FitVerdict::TightFit
55 } else {
56 FitVerdict::TooLarge
57 };
58 Some(FitAssessment {
59 verdict,
60 required_bytes,
61 })
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 const GIB: i64 = 1 << 30;
70 const MEMORY: u64 = 16 * (1 << 30);
71
72 #[test]
73 fn an_unknown_or_empty_footprint_is_unassessable() {
74 assert!(FitVerdict::assess(None, MEMORY).is_none());
75 assert!(FitVerdict::assess(Some(0), MEMORY).is_none());
76 assert!(FitVerdict::assess(Some(-5), MEMORY).is_none());
77 assert!(FitVerdict::assess(Some(GIB), 0).is_none());
78 }
79
80 #[test]
81 fn the_verdict_tracks_the_memory_share() {
82 let assessment = FitVerdict::assess(Some(GIB), MEMORY).unwrap();
84 assert_eq!(assessment.verdict, FitVerdict::RunsWell);
85
86 let tight = FitVerdict::assess(Some(12 * GIB), MEMORY).unwrap();
88 assert_eq!(tight.verdict, FitVerdict::TightFit);
89
90 let too_large = FitVerdict::assess(Some(16 * GIB), MEMORY).unwrap();
92 assert_eq!(too_large.verdict, FitVerdict::TooLarge);
93 }
94
95 #[test]
96 fn each_verdict_has_a_stable_slug() {
97 assert_eq!(FitVerdict::RunsWell.as_str(), "runs_well");
98 assert_eq!(FitVerdict::TightFit.as_str(), "tight_fit");
99 assert_eq!(FitVerdict::TooLarge.as_str(), "too_large");
100 }
101
102 #[test]
103 fn required_bytes_includes_the_overhead_factor() {
104 let assessment = FitVerdict::assess(Some(GIB), 64 * (1 << 30)).unwrap();
105 assert_eq!(assessment.required_bytes, 1280 * (1 << 20));
107 }
108}