Implement Debug for TimerHeap<T>
Use a separate type (DebugEntry) for formatting TimerEntrys during debug formatting of TimerHeap so that we can still see the other fields when debug formatting TimerEntrys outside of formatting a TimerHeap. Fixes #5master
parent
1a00a785eb
commit
5e45b94a6a
53
src/lib.rs
53
src/lib.rs
|
@ -30,6 +30,8 @@ use std::collections::{BinaryHeap, HashMap};
|
||||||
use std::cmp::{Ordering, Ord, PartialOrd, PartialEq};
|
use std::cmp::{Ordering, Ord, PartialOrd, PartialEq};
|
||||||
use std::time::{Instant, Duration};
|
use std::time::{Instant, Duration};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
use std::fmt::{self, Debug};
|
||||||
|
use std::convert::From;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -64,7 +66,7 @@ impl<'a, T> Iterator for Expired<'a, T> where T: Eq + Clone + Hash {
|
||||||
}
|
}
|
||||||
if popped.recurring {
|
if popped.recurring {
|
||||||
let key = popped.key.clone();
|
let key = popped.key.clone();
|
||||||
// We use the expired_at time so we don't keep skewing later and later
|
// We use the expires_at time so we don't keep skewing later and later
|
||||||
// by adding the duration to the current time.
|
// by adding the duration to the current time.
|
||||||
popped.expires_at += popped.duration;
|
popped.expires_at += popped.duration;
|
||||||
self.heap.timers.push(popped);
|
self.heap.timers.push(popped);
|
||||||
|
@ -98,6 +100,17 @@ pub struct TimerHeap<T> {
|
||||||
counter: u64
|
counter: u64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T:Debug + Eq + Clone + Hash + Ord> Debug for TimerHeap<T> {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt.debug_map()
|
||||||
|
.entries(self.timers
|
||||||
|
.iter()
|
||||||
|
.filter(|e| self.is_active(e))
|
||||||
|
.map(|e| (&e.key, DebugEntry::from(e))))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Eq + Clone + Hash> TimerHeap<T> {
|
impl<T: Eq + Clone + Hash> TimerHeap<T> {
|
||||||
/// Create a new TimerHeap
|
/// Create a new TimerHeap
|
||||||
pub fn new() -> TimerHeap<T> {
|
pub fn new() -> TimerHeap<T> {
|
||||||
|
@ -160,7 +173,7 @@ impl<T: Eq + Clone + Hash> TimerHeap<T> {
|
||||||
fn _time_remaining(&self, now: Instant) -> Option<Duration> {
|
fn _time_remaining(&self, now: Instant) -> Option<Duration> {
|
||||||
self.timers
|
self.timers
|
||||||
.iter()
|
.iter()
|
||||||
.find(|e| self.active.get(&e.key) == Some(&e.counter))
|
.find(|e| self.is_active(e))
|
||||||
.map(|e| {
|
.map(|e| {
|
||||||
if now > e.expires_at {
|
if now > e.expires_at {
|
||||||
return Duration::new(0, 0);
|
return Duration::new(0, 0);
|
||||||
|
@ -197,6 +210,11 @@ impl<T: Eq + Clone + Hash> TimerHeap<T> {
|
||||||
heap: self
|
heap: self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is a given entry still active ?
|
||||||
|
fn is_active(&self, entry: &TimerEntry<T>) -> bool {
|
||||||
|
self.active.get(&entry.key) == Some(&entry.counter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Eq, Debug)]
|
#[derive(Eq, Debug)]
|
||||||
|
@ -208,6 +226,33 @@ struct TimerEntry<T> {
|
||||||
counter: u64
|
counter: u64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A timer entry used only when debug formatting a TimerHeap
|
||||||
|
struct DebugEntry {
|
||||||
|
recurring: bool,
|
||||||
|
expires_at: Instant,
|
||||||
|
duration: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for DebugEntry {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt.debug_struct("Timer")
|
||||||
|
.field("recurring", &self.recurring)
|
||||||
|
.field("expires_at", &self.expires_at)
|
||||||
|
.field("duration", &self.duration)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> From<&'a TimerEntry<T>> for DebugEntry {
|
||||||
|
fn from(e: &TimerEntry<T>) -> Self {
|
||||||
|
DebugEntry {
|
||||||
|
recurring: e.recurring,
|
||||||
|
expires_at: e.expires_at,
|
||||||
|
duration: e.duration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> TimerEntry<T> {
|
impl<T> TimerEntry<T> {
|
||||||
pub fn new(key: T,
|
pub fn new(key: T,
|
||||||
duration: Duration,
|
duration: Duration,
|
||||||
|
@ -258,6 +303,7 @@ mod tests {
|
||||||
use super::{TimerHeap, TimerType, Error};
|
use super::{TimerHeap, TimerType, Error};
|
||||||
use std::time::{Instant, Duration};
|
use std::time::{Instant, Duration};
|
||||||
|
|
||||||
|
// Run this test with `cargo test -- --nocapture` to see the debug output
|
||||||
#[test]
|
#[test]
|
||||||
fn time_remaining() {
|
fn time_remaining() {
|
||||||
let mut heap = TimerHeap::new();
|
let mut heap = TimerHeap::new();
|
||||||
|
@ -265,17 +311,20 @@ mod tests {
|
||||||
let duration = Duration::from_millis(500);
|
let duration = Duration::from_millis(500);
|
||||||
heap._insert(1u64, duration, TimerType::Oneshot, now)
|
heap._insert(1u64, duration, TimerType::Oneshot, now)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
println!("Active Oneshot Timer: {:?}", heap);
|
||||||
assert_eq!(heap._time_remaining(now), Some(Duration::from_millis(500)));
|
assert_eq!(heap._time_remaining(now), Some(Duration::from_millis(500)));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
heap._time_remaining(now + duration),
|
heap._time_remaining(now + duration),
|
||||||
Some(Duration::new(0, 0))
|
Some(Duration::new(0, 0))
|
||||||
);
|
);
|
||||||
|
println!("Expired Oneshot Timer: {:?}", heap);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
heap._time_remaining(now + duration + Duration::from_millis(100)),
|
heap._time_remaining(now + duration + Duration::from_millis(100)),
|
||||||
Some(Duration::new(0, 0))
|
Some(Duration::new(0, 0))
|
||||||
);
|
);
|
||||||
assert_eq!(heap.remove(2), false);
|
assert_eq!(heap.remove(2), false);
|
||||||
assert!(heap.remove(1));
|
assert!(heap.remove(1));
|
||||||
|
println!("Empty heap: {:?}", heap);
|
||||||
assert_eq!(heap._time_remaining(now), None);
|
assert_eq!(heap._time_remaining(now), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue