#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DataClass {
Usb2,
Gen1,
Gen2,
Gen2x2,
Usb4,
}
impl DataClass {
pub fn mbps(self) -> u32 {
match self {
DataClass::Usb2 => 480,
DataClass::Gen1 => 5_000,
DataClass::Gen2 => 10_000,
DataClass::Gen2x2 => 20_000,
DataClass::Usb4 => 40_000,
}
}
pub fn carries_video(self) -> bool {
!matches!(self, DataClass::Usb2)
}
}
#[derive(Debug, Clone, Copy)]
pub struct Cable {
pub data: DataClass,
pub emarker: bool,
pub declared_amps: f64,
pub length_m: f64,
}
impl Cable {
pub fn allowed_amps(&self) -> f64 {
if self.emarker { self.declared_amps.min(5.0) } else { 3.0 }
}
pub fn max_watts_at(&self, volts: f64) -> f64 {
volts * self.allowed_amps()
}
pub fn can_deliver(&self, target_watts: f64, volts: f64) -> bool {
self.max_watts_at(volts) >= target_watts
}
pub fn passive_length_is_plausible(&self) -> bool {
let limit_m = match self.data {
DataClass::Usb2 => 4.0,
DataClass::Gen1 => 3.0,
DataClass::Gen2 | DataClass::Gen2x2 => 1.0,
DataClass::Usb4 => 0.8,
};
self.length_m <= limit_m
}
}
pub fn typical_bundled() -> Cable {
Cable { data: DataClass::Usb2, emarker: false, declared_amps: 0.0, length_m: 1.0 }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_bundled_cable_caps_a_laptop_charger() {
let c = typical_bundled();
assert!(!c.can_deliver(100.0, 20.0));
assert_eq!(c.max_watts_at(20.0), 60.0);
}
#[test]
fn the_emarker_not_the_copper_lifts_the_ceiling() {
let thick_but_dumb = Cable { emarker: false, declared_amps: 5.0, ..typical_bundled() };
let marked = Cable { emarker: true, declared_amps: 5.0, ..typical_bundled() };
assert_eq!(thick_but_dumb.allowed_amps(), 3.0, "declared amps are ignored without a chip");
assert_eq!(marked.allowed_amps(), 5.0);
}
#[test]
fn charging_well_and_carrying_video_are_unrelated() {
let charger = Cable { data: DataClass::Usb2, emarker: true, declared_amps: 5.0, length_m: 2.0 };
assert!(charger.can_deliver(100.0, 20.0), "it charges a laptop fine");
assert!(!charger.data.carries_video(), "and still shows nothing on a monitor");
}
#[test]
fn speed_shortens_the_plausible_passive_cable() {
let long_slow = Cable { data: DataClass::Usb2, length_m: 3.0, ..typical_bundled() };
let long_fast = Cable { data: DataClass::Usb4, length_m: 3.0, ..typical_bundled() };
assert!(long_slow.passive_length_is_plausible());
assert!(!long_fast.passive_length_is_plausible(),
"a passive 3 m 40 Gbps cable should be treated as implausible");
}
}