use serde::{Deserialize, Serialize};
use std::net::Ipv4Addr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Capacity {
pub cores: u32,
pub ram_bytes: u64,
pub data_bytes: u64,
pub disk_mib_s: u32,
}
impl Capacity {
fn key(&self) -> (u32, u64, u64, u32) {
(self.cores, self.ram_bytes, self.data_bytes, self.disk_mib_s)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Verdict {
pub twin: String,
pub primary: String,
pub why: String,
}
pub type Report<'a> = (&'a str, Ipv4Addr, &'a Capacity);
pub fn weakest(a: Report<'_>, b: Report<'_>) -> Verdict {
let (an, ao, ac) = a;
let (bn, bo, bc) = b;
let (twin, primary, why) = match ac.key().cmp(&bc.key()) {
std::cmp::Ordering::Less => (an, bn, format!("`{an}` is the weaker box ({}) against `{bn}` ({})", say(ac), say(bc))),
std::cmp::Ordering::Greater => (bn, an, format!("`{bn}` is the weaker box ({}) against `{an}` ({})", say(bc), say(ac))),
std::cmp::Ordering::Equal => {
if ao <= bo {
(an, bn, format!("`{an}` and `{bn}` measure the same ({}); the lower address {ao} is the twin", say(ac)))
} else {
(bn, an, format!("`{an}` and `{bn}` measure the same ({}); the lower address {bo} is the twin", say(bc)))
}
}
};
Verdict { twin: twin.to_string(), primary: primary.to_string(), why }
}
fn say(c: &Capacity) -> String {
format!("{} cores, {} GiB RAM, {} GiB data, {} MiB/s disk", c.cores, c.ram_bytes >> 30, c.data_bytes >> 30, c.disk_mib_s)
}
#[cfg(test)]
mod tests {
use super::*;
const BIG: Capacity = Capacity { cores: 4, ram_bytes: 8 << 30, data_bytes: 40 << 30, disk_mib_s: 900 };
const SMALL: Capacity = Capacity { cores: 2, ram_bytes: 8 << 30, data_bytes: 40 << 30, disk_mib_s: 900 };
#[test]
fn the_weaker_box_is_the_twin_whichever_side_it_is_on() {
let v = weakest(("main", crate::OVERLAY_BOX_A, &BIG), ("twin", crate::OVERLAY_BOX_B, &SMALL));
assert_eq!((v.twin.as_str(), v.primary.as_str()), ("twin", "main"));
let v = weakest(("main", crate::OVERLAY_BOX_A, &SMALL), ("twin", crate::OVERLAY_BOX_B, &BIG));
assert_eq!((v.twin.as_str(), v.primary.as_str()), ("main", "twin"));
assert!(v.why.contains("weaker box"), "{}", v.why);
}
#[test]
fn a_tie_goes_to_the_lower_address_and_a_slower_disk_only_breaks_ties() {
let v = weakest(("b", crate::OVERLAY_BOX_B, &BIG), ("a", crate::OVERLAY_BOX_A, &BIG));
assert_eq!(v.twin, "a", "{}", v.why);
let slow = Capacity { disk_mib_s: 100, ..BIG };
let v = weakest(("a", crate::OVERLAY_BOX_A, &slow), ("b", crate::OVERLAY_BOX_B, &BIG));
assert_eq!(v.twin, "a");
let few_fast = Capacity { cores: 2, disk_mib_s: 5000, ..BIG };
let v = weakest(("a", crate::OVERLAY_BOX_A, &few_fast), ("b", crate::OVERLAY_BOX_B, &BIG));
assert_eq!(v.twin, "a");
}
}