use std::collections::{HashMap, VecDeque};
use nord_usb::ObjectClass;
#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)]
pub struct Progress {
pub done: u32,
pub total: Option<u32>,
pub running: bool,
}
#[derive(Default)]
pub struct Scan {
queue: VecDeque<ObjectClass>,
progress: HashMap<u32, Progress>,
}
impl Scan {
pub fn start(&mut self, class: ObjectClass) {
if !self.queue.contains(&class) {
self.queue.push_back(class);
}
self.progress.insert(
class.to_raw(),
Progress {
done: 0,
total: None,
running: true,
},
);
}
pub fn take(&mut self) -> Option<ObjectClass> {
self.queue.pop_front()
}
pub fn expect(&mut self, class: ObjectClass, total: Option<u32>) {
self.progress.entry(class.to_raw()).or_default().total = total;
}
pub fn bank(&mut self, class: ObjectClass, bank: u32) {
let progress = self.progress.entry(class.to_raw()).or_default();
progress.done = progress.done.max(bank);
}
pub fn finished(&mut self, class: ObjectClass) {
let again = self.queue.contains(&class);
self.progress.entry(class.to_raw()).or_default().running = again;
}
pub fn progress(&self, class: ObjectClass) -> Option<Progress> {
self.progress.get(&class.to_raw()).copied()
}
pub fn clear(&mut self) {
self.queue.clear();
self.progress.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_class_reports_its_banks_as_they_land() {
let mut scan = Scan::default();
scan.start(ObjectClass::Program);
scan.start(ObjectClass::Program);
assert_eq!(scan.take(), Some(ObjectClass::Program));
assert_eq!(scan.take(), None, "one class, one walk");
scan.expect(ObjectClass::Program, Some(8));
for bank in 1..=3 {
scan.bank(ObjectClass::Program, bank);
}
let progress = scan.progress(ObjectClass::Program).unwrap();
assert_eq!((progress.done, progress.total), (3, Some(8)));
assert!(progress.running);
}
#[test]
fn a_finished_walk_stops_running() {
let mut scan = Scan::default();
scan.start(ObjectClass::Sample);
scan.take();
scan.bank(ObjectClass::Sample, 1);
scan.finished(ObjectClass::Sample);
let progress = scan.progress(ObjectClass::Sample).unwrap();
assert!(!progress.running);
assert_eq!(progress.done, 1);
}
#[test]
fn reading_a_class_again_starts_its_count_over() {
let mut scan = Scan::default();
scan.start(ObjectClass::Program);
scan.take();
scan.bank(ObjectClass::Program, 4);
scan.finished(ObjectClass::Program);
scan.start(ObjectClass::Program);
assert_eq!(scan.progress(ObjectClass::Program).unwrap().done, 0);
assert_eq!(scan.take(), Some(ObjectClass::Program));
}
#[test]
fn finishing_one_class_leaves_the_others_queued() {
let mut scan = Scan::default();
scan.start(ObjectClass::Program);
scan.start(ObjectClass::SetList);
assert_eq!(scan.take(), Some(ObjectClass::Program));
scan.finished(ObjectClass::Program);
assert_eq!(scan.take(), Some(ObjectClass::SetList));
}
#[test]
fn a_class_asked_for_during_its_own_walk_is_read_again() {
let mut scan = Scan::default();
scan.start(ObjectClass::Program);
assert_eq!(scan.take(), Some(ObjectClass::Program));
scan.bank(ObjectClass::Program, 2);
scan.start(ObjectClass::Program);
scan.finished(ObjectClass::Program);
assert!(
scan.progress(ObjectClass::Program).unwrap().running,
"the second walk is still owed"
);
assert_eq!(scan.take(), Some(ObjectClass::Program));
scan.finished(ObjectClass::Program);
assert!(!scan.progress(ObjectClass::Program).unwrap().running);
assert_eq!(scan.take(), None);
}
}