#![allow(dead_code)]
#![allow(clippy::cast_precision_loss)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TapeFormat {
Lto7,
Lto8,
Lto9,
Lto10,
}
impl TapeFormat {
#[must_use]
pub fn capacity_gb(&self) -> u64 {
match self {
Self::Lto7 => 6_000,
Self::Lto8 => 12_000,
Self::Lto9 => 18_000,
Self::Lto10 => 36_000,
}
}
#[must_use]
pub fn native_speed_mbps(&self) -> f64 {
match self {
Self::Lto7 => 300.0,
Self::Lto8 => 360.0,
Self::Lto9 => 400.0,
Self::Lto10 => 900.0,
}
}
#[must_use]
pub fn compressed_capacity_gb(&self) -> u64 {
match self {
Self::Lto7 => 15_000,
Self::Lto8 => 30_000,
Self::Lto9 => 45_000,
Self::Lto10 => 90_000,
}
}
#[must_use]
pub fn generation(&self) -> u8 {
match self {
Self::Lto7 => 7,
Self::Lto8 => 8,
Self::Lto9 => 9,
Self::Lto10 => 10,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TapeStatus {
Blank,
Active,
Full,
Scratch,
Expired,
}
impl TapeStatus {
#[must_use]
pub fn is_writable(&self) -> bool {
matches!(self, Self::Blank | Self::Active | Self::Scratch)
}
}
#[derive(Debug, Clone)]
pub struct TapeCartridge {
pub barcode: String,
pub format: TapeFormat,
pub used_gb: f64,
pub status: TapeStatus,
}
impl TapeCartridge {
#[must_use]
pub fn new(barcode: &str, format: TapeFormat) -> Self {
Self {
barcode: barcode.to_string(),
format,
used_gb: 0.0,
status: TapeStatus::Blank,
}
}
#[must_use]
pub fn available_gb(&self) -> f64 {
let cap = self.format.capacity_gb() as f64;
(cap - self.used_gb).max(0.0)
}
#[must_use]
pub fn utilization_pct(&self) -> f64 {
let cap = self.format.capacity_gb() as f64;
if cap <= 0.0 {
return 100.0;
}
(self.used_gb / cap * 100.0).clamp(0.0, 100.0)
}
#[must_use]
pub fn write_time_s(&self, data_gb: f64) -> f64 {
let speed_gbps = self.format.native_speed_mbps() / 1024.0;
if speed_gbps <= 0.0 {
return f64::MAX;
}
data_gb / speed_gbps
}
pub fn record_write(&mut self, data_gb: f64) {
self.used_gb = (self.used_gb + data_gb).min(self.format.capacity_gb() as f64);
if self.available_gb() <= 0.0 {
self.status = TapeStatus::Full;
} else {
self.status = TapeStatus::Active;
}
}
}
pub struct TapeLibrary {
pub cartridges: Vec<TapeCartridge>,
pub drives: usize,
}
impl TapeLibrary {
#[must_use]
pub fn new(drives: usize) -> Self {
Self {
cartridges: Vec::new(),
drives,
}
}
pub fn add_cartridge(&mut self, c: TapeCartridge) {
self.cartridges.push(c);
}
#[must_use]
pub fn find_cartridge(&self, barcode: &str) -> Option<&TapeCartridge> {
self.cartridges.iter().find(|c| c.barcode == barcode)
}
#[must_use]
pub fn find_cartridge_mut(&mut self, barcode: &str) -> Option<&mut TapeCartridge> {
self.cartridges.iter_mut().find(|c| c.barcode == barcode)
}
#[must_use]
pub fn available_capacity_gb(&self) -> f64 {
self.cartridges
.iter()
.filter(|c| c.status.is_writable())
.map(TapeCartridge::available_gb)
.sum()
}
#[must_use]
pub fn cartridge_count(&self) -> usize {
self.cartridges.len()
}
#[must_use]
pub fn cartridges_by_status(&self, status: TapeStatus) -> Vec<&TapeCartridge> {
self.cartridges
.iter()
.filter(|c| c.status == status)
.collect()
}
#[must_use]
pub fn find_writable(&self, required_gb: f64) -> Option<&TapeCartridge> {
self.cartridges
.iter()
.filter(|c| c.status.is_writable() && c.available_gb() >= required_gb)
.min_by(|a, b| {
a.available_gb()
.partial_cmp(&b.available_gb())
.unwrap_or(std::cmp::Ordering::Equal)
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tape_format_capacity() {
assert_eq!(TapeFormat::Lto7.capacity_gb(), 6_000);
assert_eq!(TapeFormat::Lto8.capacity_gb(), 12_000);
assert_eq!(TapeFormat::Lto9.capacity_gb(), 18_000);
assert_eq!(TapeFormat::Lto10.capacity_gb(), 36_000);
}
#[test]
fn test_tape_format_compressed_capacity() {
assert_eq!(TapeFormat::Lto9.compressed_capacity_gb(), 45_000);
assert_eq!(TapeFormat::Lto10.compressed_capacity_gb(), 90_000);
}
#[test]
fn test_tape_format_speed() {
assert!((TapeFormat::Lto7.native_speed_mbps() - 300.0).abs() < 1e-9);
assert!((TapeFormat::Lto10.native_speed_mbps() - 900.0).abs() < 1e-9);
}
#[test]
fn test_tape_format_generation() {
assert_eq!(TapeFormat::Lto7.generation(), 7);
assert_eq!(TapeFormat::Lto10.generation(), 10);
}
#[test]
fn test_tape_status_writable() {
assert!(TapeStatus::Blank.is_writable());
assert!(TapeStatus::Active.is_writable());
assert!(TapeStatus::Scratch.is_writable());
assert!(!TapeStatus::Full.is_writable());
assert!(!TapeStatus::Expired.is_writable());
}
#[test]
fn test_cartridge_new_is_blank() {
let c = TapeCartridge::new("TAPE001L9", TapeFormat::Lto9);
assert_eq!(c.status, TapeStatus::Blank);
assert_eq!(c.used_gb, 0.0);
}
#[test]
fn test_cartridge_available_gb() {
let mut c = TapeCartridge::new("T001", TapeFormat::Lto8);
c.used_gb = 5_000.0;
assert!((c.available_gb() - 7_000.0).abs() < 0.01);
}
#[test]
fn test_cartridge_utilization_pct() {
let mut c = TapeCartridge::new("T001", TapeFormat::Lto8);
c.used_gb = 6_000.0;
assert!((c.utilization_pct() - 50.0).abs() < 0.01);
}
#[test]
fn test_cartridge_write_time() {
let c = TapeCartridge::new("T001", TapeFormat::Lto9);
let t = c.write_time_s(18_000.0);
assert!(t > 40_000.0 && t < 50_000.0);
}
#[test]
fn test_cartridge_record_write_transitions_to_active() {
let mut c = TapeCartridge::new("T001", TapeFormat::Lto9);
c.record_write(1_000.0);
assert_eq!(c.status, TapeStatus::Active);
assert!((c.used_gb - 1_000.0).abs() < 0.01);
}
#[test]
fn test_library_add_and_count() {
let mut lib = TapeLibrary::new(2);
lib.add_cartridge(TapeCartridge::new("T001", TapeFormat::Lto9));
lib.add_cartridge(TapeCartridge::new("T002", TapeFormat::Lto9));
assert_eq!(lib.cartridge_count(), 2);
}
#[test]
fn test_library_find_cartridge() {
let mut lib = TapeLibrary::new(1);
lib.add_cartridge(TapeCartridge::new("FINDME", TapeFormat::Lto8));
assert!(lib.find_cartridge("FINDME").is_some());
assert!(lib.find_cartridge("NOTHERE").is_none());
}
#[test]
fn test_library_available_capacity() {
let mut lib = TapeLibrary::new(1);
lib.add_cartridge(TapeCartridge::new("T001", TapeFormat::Lto9));
lib.add_cartridge(TapeCartridge::new("T002", TapeFormat::Lto9));
assert_eq!(lib.available_capacity_gb(), 36_000.0);
}
#[test]
fn test_library_find_writable() {
let mut lib = TapeLibrary::new(1);
let mut c = TapeCartridge::new("T001", TapeFormat::Lto9);
c.status = TapeStatus::Full;
lib.add_cartridge(c);
lib.add_cartridge(TapeCartridge::new("T002", TapeFormat::Lto9));
let w = lib.find_writable(100.0);
assert!(w.is_some());
assert_eq!(w.expect("test expectation failed").barcode, "T002");
}
}