sum_queue/lib.rs
1//! `SumQueue` it's a queue struct that keeps a fixed number of
2//! items by time, not capacity, similar to a cache, but with a simpler
3//! and faster implementation. It also allows to get summarized stats
4//! of the values on it at any time.
5//!
6//! ## Examples
7//!
8//! ```
9//! use std::time::Duration;
10//! use std::thread;
11//! use sum_queue::SumQueue;
12//!
13//! // creates a queue where elements expire after 2 seconds
14//! let mut queue: SumQueue<i32> = SumQueue::new(Duration::from_secs(2));
15//! queue.push(1);
16//! queue.push(10);
17//! queue.push(3);
18//!
19//! // Check the peek without removing the element
20//! assert_eq!(queue.peek(), Some(&1));
21//! // elements are removed in the same order were pushed
22//! assert_eq!(queue.pop(), Some(1));
23//! assert_eq!(queue.pop(), Some(10));
24//! assert_eq!(queue.pop(), Some(3));
25//! assert_eq!(queue.pop(), None);
26//!
27//! // Lets puts elements again
28//! queue.push(1);
29//! queue.push(5);
30//! queue.push(2);
31//! // Elements can be iterated as many times as you want
32//! println!("heap data: {:?}", queue.iter().collect::<Vec<_>>()); // [1, 5, 2]
33//!
34//! // Check stats
35//! let stats = queue.stats();
36//! println!("Stats - min value in queue: {}", stats.min.unwrap()); // 1
37//! println!("Stats - max value in queue: {}", stats.max.unwrap()); // 5
38//! println!("Stats - sum all values in queue: {}", stats.sum.unwrap()); // 8
39//! println!("Stats - length of queue: {}", stats.len); // 3
40//!
41//! assert_eq!(queue.pop(), Some(1));
42//! assert_eq!(queue.iter().collect::<Vec<_>>(), vec![&5, &2]);
43//! println!("Elements after pop: {:?}", queue.iter().collect::<Vec<_>>()); // [5, 2]
44//!
45//! // After a second the elements are still the same
46//! thread::sleep(Duration::from_secs(1));
47//! println!("Same after 1 sec: {:?}", queue.iter().collect::<Vec<_>>()); // [5, 2]
48//!
49//! queue.push(50); // Add an element 1 second younger than the rest of elements
50//! println!("Same elements + 50: {:?}", queue.iter().collect::<Vec<_>>()); // [5, 2, 50]
51//!
52//! // Now let sleep 1 sec so the first elements expire
53//! thread::sleep(Duration::from_secs(1));
54//! println!("Just 50: {:?}", queue.iter().collect::<Vec<_>>()); // [50]
55//!
56//! // 1 second more later the last element also expires
57//! thread::sleep(Duration::from_secs(1));
58//! println!("No elements: {:?}", queue.iter().collect::<Vec<_>>()); // []
59//! ```
60//!
61//! ## Implementation
62//!
63//! Underneath uses a [`BinaryHeap`] struct to keep the values,
64//! and implements the same methods: `push()`, `pop()`, `peek()` ...
65//! although worth to note that the implementations of the `SumQueue` type take mutable
66//! ownership of the `self` reference (eg. `peek(&mut self) -> Option<&T>`). That is
67//! because the cleaning of the expired elements of the queue occurs each time
68//! a method is called to read or write a value, including the `len()` method.
69//!
70//! So as long you manage only one instance of `SumQueue`, there is no
71//! risk of excessive memory allocation, because while you push elements with the `push()`
72//! method, or call any other method to read the queue you are taking care of removing
73//! and deallocating the expired elements, but if you are using multiple instances, and
74//! pushing too many items to some queues and not accessing others further, the memory usage
75//! may growth with elements expired not been deallocated because you are not accessing
76//! those queues to push, pop or get the stats of them. In that case you can at least
77//! try to call often to the `len()` method to force the unused queues to remove and
78//! deallocate the expired elements.
79
80use std::cmp::Ordering;
81use std::collections::BinaryHeap;
82use std::collections::binary_heap;
83use std::ops::Add;
84use std::time::{Instant, Duration};
85
86/// Internal element used by `SumQueue` to hold the values.
87struct QueueElement<T> {
88 time: Instant,
89 value: T
90}
91
92/// Stats of the queue.
93///
94/// It provides the following statistics: **min** and **max** value
95/// in the queue, the **sum** of all the values and the **length**
96/// of all elements hold in the queue.
97///
98/// The values are computed taking into account only
99/// the existent elements in the queue, and not past
100/// elements removed because expiration or because
101/// they were removed.
102///
103/// You can get the stats object calling to
104/// the [`SumQueue::stats()`] method of the queue:
105///
106/// ```
107/// use std::time::Duration;
108/// use sum_queue::SumQueue;
109/// let mut queue = SumQueue::new(Duration::from_millis(800));
110/// queue.push(-1);
111/// queue.push(5);
112/// queue.push(2);
113/// let stats = queue.stats();
114/// assert_eq!(stats.min, Some(-1));
115/// assert_eq!(stats.max, Some(5));
116/// assert_eq!(stats.sum, Some(6));
117/// assert_eq!(stats.len, 3);
118/// ```
119///
120/// But you can also get the stats
121/// while pushing elements, which it's more
122/// efficient than push and then get the stats:
123///
124/// ```
125/// use std::time::Duration;
126/// use sum_queue::SumQueue;
127/// let mut queue = SumQueue::new(Duration::from_secs(1000));
128/// queue.push(-1);
129/// queue.push(5);
130/// let stats = queue.push_and_stats(2);
131/// assert_eq!(stats.min, Some(-1));
132/// assert_eq!(stats.max, Some(5));
133/// assert_eq!(stats.sum, Some(6));
134/// assert_eq!(stats.len, 3);
135/// ```
136pub struct QueueStats<T: Ord + Add<Output = T>> {
137 /// min value of the queue
138 pub min: Option<T>,
139 /// max value of the queue
140 pub max: Option<T>,
141 /// sum of all the values in the queue
142 pub sum: Option<T>,
143 /// size of the queue, same than [`SumQueue::len()`]
144 pub len: usize
145}
146
147impl<T> PartialEq for QueueElement<T> {
148 fn eq(&self, other: &Self) -> bool {
149 self.time == other.time
150 }
151}
152impl<T> Eq for QueueElement<T> {}
153
154impl<T> Ord for QueueElement<T> {
155 fn cmp(&self, other: &Self) -> Ordering {
156 //! Reverse order to set lower number higher
157 other.time.cmp(&self.time)
158 }
159}
160
161impl<T> PartialOrd for QueueElement<T> {
162 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
163 Some(self.cmp(other))
164 }
165}
166
167fn now() -> Instant {
168 Instant::now()
169}
170
171/// Main struct that holds the queue of elements.
172///
173/// There are different ways to create the queue:
174///
175/// ```
176/// use std::time::Duration;
177/// use sum_queue::SumQueue;
178///
179/// let mut queue: SumQueue<i32>;
180///
181/// // Create a queue with elements that expires after 60 seconds
182/// queue = SumQueue::new(Duration::from_secs(60));
183/// // Create with 500 milliseconds expiration and an initial capacity of 20 elements
184/// queue = SumQueue::with_capacity(Duration::from_millis(500), 20);
185/// ```
186pub struct SumQueue<T> {
187 /// the heap with the data
188 heap: BinaryHeap<QueueElement<T>>,
189 /// max time the elements will
190 /// live in the queue.
191 max_age: Duration,
192}
193
194impl<T> SumQueue<T> {
195 /// Creates an empty `SumQueue`, where the elements inside
196 /// will live `max_age_duration` at maximum.
197 pub fn new(max_age_duration: Duration) -> SumQueue<T> {
198 SumQueue {
199 heap: BinaryHeap::<QueueElement<T>>::new(),
200 max_age: max_age_duration,
201 }
202 }
203
204 /// Creates an empty `SumQueue` with a specific initial capacity.
205 /// This preallocates enough memory for `capacity` elements,
206 /// so that the [`BinaryHeap`] inside the `SumQueue` does not have
207 /// to be reallocated until it contains at least that many values.
208 /// The elements inside the queue will live `max_age_duration` time at maximum.
209 pub fn with_capacity(max_age_duration: Duration, capacity: usize) -> SumQueue<T> {
210 SumQueue {
211 heap: BinaryHeap::<QueueElement<T>>::with_capacity(capacity),
212 max_age: max_age_duration,
213 }
214 }
215
216 /// Pushes an item onto the heap of the queue.
217 ///
218 /// See [`BinaryHeap::push`] to known more about the time complexity.
219 ///
220 /// It returns the size of the queue, and before the element is pushed to the heap,
221 /// it also drops all expired elements in the queue.
222 ///
223 /// ```
224 /// use std::time::Duration;
225 /// use sum_queue::SumQueue;
226 /// let mut queue = SumQueue::new(Duration::from_secs(60));
227 /// queue.push(1);
228 /// queue.push(5);
229 /// assert_eq!(queue.push(2), 3);
230 /// assert_eq!(queue.iter().collect::<Vec<_>>(), vec![&1, &5, &2]);
231 /// ```
232 pub fn push(&mut self, item: T) -> usize {
233 let now = now();
234 self.clear_oldest(now);
235 self.heap.push(QueueElement {
236 time: now,
237 value: item
238 });
239 self.heap.len()
240 }
241
242 fn clear_oldest(&mut self, now: Instant) {
243 while let Some(el) = self.heap.peek() {
244 let peek_age = now - el.time;
245 if peek_age > self.max_age {
246 self.heap.pop();
247 } else {
248 break;
249 }
250 }
251 }
252
253 /// Drops all items.
254 pub fn clear(&mut self) {
255 self.heap.clear();
256 }
257
258 /// Returns the length of the heap.
259 ///
260 /// It takes a mutable reference of `self` because
261 /// before return the size it also cleans all the
262 /// expired elements of the queue, so only
263 /// no expired elements are count.
264 pub fn len(&mut self) -> usize {
265 self.clear_oldest(now());
266 self.heap.len()
267 }
268
269 /// Checks if the heap is empty. Expired elements are not taken
270 /// into account because are droped by `is_empty()` before
271 /// return the result.
272 ///
273 /// ```
274 /// use std::time::Duration;
275 /// use std::thread;
276 /// use sum_queue::SumQueue;
277 /// let mut queue = SumQueue::new(Duration::from_millis(600));
278 ///
279 /// assert!(queue.is_empty());
280 ///
281 /// queue.push(123);
282 /// queue.push(555);
283 ///
284 /// assert!(!queue.is_empty());
285 ///
286 /// thread::sleep(Duration::from_secs(1));
287 ///
288 /// assert!(queue.is_empty());
289 /// ```
290 pub fn is_empty(&mut self) -> bool {
291 self.len() == 0
292 }
293
294 /// Returns the number of elements the heap can hold without reallocating.
295 ///
296 /// ```
297 /// use std::time::Duration;
298 /// use sum_queue::SumQueue;
299 /// let mut queue: SumQueue<char> = SumQueue::with_capacity(Duration::from_secs(60), 5);
300 /// assert_eq!(queue.capacity(), 5);
301 /// assert_eq!(queue.len(), 0);
302 /// ```
303 pub fn capacity(&self) -> usize {
304 self.heap.capacity()
305 }
306
307 /// Returns the max time the elements will live in the queue.
308 ///
309 /// ```
310 /// use std::time::Duration;
311 /// use sum_queue::SumQueue;
312 /// let mut queue: SumQueue<char> = SumQueue::new(Duration::from_secs(60));
313 /// assert_eq!(queue.max_age().as_secs(), 60);
314 /// ```
315 pub fn max_age(&self) -> Duration {
316 self.max_age
317 }
318
319 /// Returns the first item in the heap, or `None` if it is empty.
320 ///
321 /// Before the element is returned, it also drops all expired
322 /// elements from the queue.
323 ///
324 /// ```
325 /// use std::time::Duration;
326 /// use sum_queue::SumQueue;
327 /// let mut queue = SumQueue::new(Duration::from_secs(60));
328 /// assert_eq!(queue.peek(), None);
329 /// queue.push("Hello");
330 /// queue.push("World");
331 /// queue.push("!");
332 /// assert_eq!(queue.peek(), Some(&"Hello"));
333 /// ```
334 pub fn peek(&mut self) -> Option<&T> {
335 self.clear_oldest(now());
336 self.heap.peek().map( |q_element| &q_element.value)
337 }
338
339 /// Removes the first item from the heap and returns it, or `None` if it
340 /// is empty.
341 ///
342 /// Before the element is dropped from the queue and returned,
343 /// it also drops all expired elements.
344 ///
345 /// ```
346 /// use std::time::Duration;
347 /// use sum_queue::SumQueue;
348 /// let mut queue = SumQueue::with_capacity(Duration::from_secs(60), 5);
349 /// assert_eq!(queue.pop(), None);
350 /// queue.push('a');
351 /// queue.push('x');
352 /// queue.push('c');
353 /// assert_eq!(queue.pop(), Some('a'));
354 /// assert_eq!(queue.pop(), Some('x'));
355 /// assert_eq!(queue.pop(), Some('c'));
356 /// assert_eq!(queue.pop(), None);
357 /// ```
358 pub fn pop(&mut self) -> Option<T> {
359 self.clear_oldest(now());
360 self.heap.pop().map( |q_element| q_element.value)
361 }
362
363 /// Returns an iterator visiting all values in the underlying heap, in
364 /// same order they were pushed.
365 ///
366 /// Before return the iterator, it also drops all expired elements.
367 ///
368 /// The iterator does not change the state of the queue, this
369 /// method takes ownership of the queue because as mentioned above
370 /// it clears the expired elements before return the iterator, even
371 /// if the iterator is not consumed later on.
372 ///
373 /// ```
374 /// use std::time::Duration;
375 /// use sum_queue::SumQueue;
376 /// let mut queue = SumQueue::new(Duration::from_secs(60));
377 /// queue.push('a');
378 /// queue.push('z');
379 /// queue.push('x');
380 /// assert_eq!(queue.iter().collect::<Vec<_>>(), vec![&'a', &'z', &'x']);
381 /// ```
382 pub fn iter(&mut self) -> Iter<'_, T> {
383 self.clear_oldest(now());
384 Iter { iter: self.heap.iter() }
385 }
386}
387
388impl<T: Copy + Ord + Add<Output = T>> SumQueue<T> {
389
390 fn _stats(&mut self, len: usize) -> QueueStats<T> {
391 let mut min = None; let mut max = None; let mut sum = None;
392 for i in self.heap.iter().map(|x| x.value) {
393 if min == None || Some(i) < min {
394 min = Some(i);
395 }
396 if max == None || Some(i) > max {
397 max = Some(i);
398 }
399 sum = match sum {
400 Some(s) => Some(s + i),
401 None => Some(i)
402 };
403 }
404 QueueStats {
405 min, max, sum, len
406 }
407 }
408
409 /// Get statistics of the queue. The type of the elements
410 /// on it needs to implements the `Copy`, `Ord` and `Add` traits.
411 ///
412 /// Before the stats are returned, it also drops all expired elements.
413 ///
414 /// ```
415 /// use std::time::Duration;
416 /// use sum_queue::SumQueue;
417 /// let mut queue: SumQueue<i64> = SumQueue::new(Duration::from_secs(1000));
418 /// queue.push(-10);
419 /// queue.push(50);
420 /// queue.push(40);
421 /// queue.push(20);
422 /// let stats = queue.stats();
423 /// assert_eq!(stats.min, Some(-10));
424 /// assert_eq!(stats.max, Some(50));
425 /// assert_eq!(stats.sum, Some(100));
426 /// assert_eq!(stats.len, 4);
427 /// ```
428 ///
429 /// See also `push_and_stats`.
430 pub fn stats(&mut self) -> QueueStats<T> {
431 let len = self.len();
432 self._stats(len)
433 }
434
435 /// Pushes an item onto the heap of the queue, and returns
436 /// the stats of the queue. The type of the elements
437 /// on it need to implements the `Copy`, `Ord` and `Add`
438 /// traits.
439 ///
440 /// Before push and return the stats, it also drops all expired elements.
441 ///
442 /// ```
443 /// use std::time::Duration;
444 /// use sum_queue::SumQueue;
445 /// let mut queue: SumQueue<i64> = SumQueue::new(Duration::from_secs(1000));
446 /// queue.push(-10);
447 /// queue.push(50);
448 /// queue.push(40);
449 /// let stats = queue.push_and_stats(20);
450 /// assert_eq!(stats.min, Some(-10));
451 /// assert_eq!(stats.max, Some(50));
452 /// assert_eq!(stats.sum, Some(100));
453 /// assert_eq!(stats.len, 4);
454 /// ```
455 ///
456 /// Use `push` instead if you don't need the stats
457 /// or the elements in the heap don't implement
458 /// any of the required traits.
459 pub fn push_and_stats(&mut self, item: T) -> QueueStats<T> {
460 let len = self.push(item);
461 self._stats(len)
462 }
463}
464
465/// An iterator over the elements of a `SumQueue`.
466///
467/// This `struct` is created by [`SumQueue::iter()`]. See its
468/// documentation for more.
469pub struct Iter<'a, T: 'a> {
470 iter: binary_heap::Iter<'a, QueueElement<T>>,
471}
472
473impl<'a, T> Iterator for Iter<'a, T> {
474 type Item = &'a T;
475
476 fn next(&mut self) -> Option<&'a T> {
477 let element = self.iter.next()?;
478 Some(&element.value)
479 }
480}
481
482mod tests {
483 pub use std::thread;
484 pub use std::time::Duration;
485 pub use crate::SumQueue;
486
487 #[test]
488 fn push_pop_peek() {
489 let mut queue: SumQueue<i32> = SumQueue::new(Duration::from_secs(60));
490 queue.push(1);
491 queue.push(5);
492 assert_eq!(queue.push(2), 3); // push return queue length
493 assert_eq!(queue.peek(), Some(&1));
494 assert_eq!(queue.peek(), Some(&1)); // still the same
495 assert_eq!(queue.pop(), Some(1));
496 assert_eq!(queue.pop(), Some(5));
497 assert_eq!(queue.pop(), Some(2));
498 assert_eq!(queue.pop(), None);
499 assert_eq!(queue.peek(), None);
500 queue.push(1_000);
501 assert_eq!(queue.peek(), Some(&1_000));
502 }
503
504 #[test]
505 fn push_pop_peek_refs() {
506 let mut queue: SumQueue<&i32> = SumQueue::new(Duration::from_secs(60));
507 queue.push(&1);
508 queue.push(&5);
509 assert_eq!(queue.push(&2), 3);
510 assert_eq!(queue.peek(), Some(&&1));
511 assert_eq!(queue.peek(), Some(&&1));
512 assert_eq!(queue.pop(), Some(&1));
513 assert_eq!(queue.pop(), Some(&5));
514 assert_eq!(queue.pop(), Some(&2));
515 assert_eq!(queue.pop(), None);
516 assert_eq!(queue.peek(), None);
517 queue.push(&1_000);
518 assert_eq!(queue.peek(), Some(&&1_000));
519 }
520
521 #[test]
522 fn len_clear() {
523 let mut queue: SumQueue<char> =SumQueue::with_capacity(
524 Duration::from_secs(60), 2); // small capacity shouldn't be a problem
525 assert_eq!(queue.len(), 0);
526 queue.push('a');
527 queue.push('b');
528 queue.push('c');
529 assert_eq!(queue.len(), 3);
530 queue.pop();
531 assert_eq!(queue.len(), 2);
532 queue.clear();
533 assert_eq!(queue.len(), 0);
534 queue.push('$');
535 assert_eq!(queue.len(), 1);
536 }
537
538 #[test]
539 fn iter() {
540 let mut queue: SumQueue<&str> = SumQueue::with_capacity(
541 Duration::from_secs(60), 20);
542 queue.push("Hey");
543 queue.push("You");
544 queue.push("!");
545 println!("heap data with &str references: {:?}", queue.iter().collect::<Vec<_>>());
546 // data can be iterated as many time as you want
547 assert_eq!(queue.iter().collect::<Vec<_>>(), vec![&"Hey", &"You", &"!"]);
548 print!("heap data, iterate one by one... :");
549 for word in queue.iter() { // iterate one by one don't crash
550 print!(" {}", word)
551 }
552 println!();
553 }
554
555 #[test]
556 fn expire() {
557 let max_age_secs = 2;
558 let mut queue: SumQueue<i32> = SumQueue::with_capacity(
559 Duration::from_secs(max_age_secs), 20);
560 queue.push(1);
561 queue.push(5);
562 queue.push(2);
563 assert_eq!(queue.iter().collect::<Vec<_>>(), vec![&1, &5, &2]);
564 println!("Elements in queue with max age of {} secs: {:?}",
565 max_age_secs, queue.iter().collect::<Vec<_>>());
566
567 sleep_secs(1);
568 assert_eq!(queue.iter().collect::<Vec<_>>(), vec![&1, &5, &2]);
569 println!("No expiration yet, same elements: {:?}", queue.iter().collect::<Vec<_>>());
570
571 println!("\nAdding element 50 ...");
572 queue.push(50);
573 assert_eq!(queue.iter().collect::<Vec<_>>(), vec![&1, &5, &2, &50]);
574 println!("Same elements + 50: {:?}", queue.iter().collect::<Vec<_>>());
575
576 sleep_secs(1);
577 assert_eq!(queue.iter().collect::<Vec<_>>(), vec![&50]);
578 println!("Expired original list, only 50 in the list: {:?}",
579 queue.iter().collect::<Vec<_>>());
580
581 sleep_secs(2);
582 assert_eq!(queue.iter().collect::<Vec<_>>().len(), 0);
583 println!("No elements kept: {:?}", queue.iter().collect::<Vec<_>>());
584 }
585
586 #[test]
587 fn expire_less_one_sec() {
588 let max_age_millis = 200;
589 let mut queue: SumQueue<i32> = SumQueue::with_capacity(
590 Duration::from_millis(max_age_millis), 20);
591 queue.push(1);
592 queue.push(5);
593 queue.push(2);
594 assert_eq!(queue.iter().collect::<Vec<_>>(), vec![&1, &5, &2]);
595 println!("Elements in queue with max age of {} millis: {:?}",
596 max_age_millis, queue.iter().collect::<Vec<_>>());
597
598 sleep_millis(100);
599 assert_eq!(queue.iter().collect::<Vec<_>>(), vec![&1, &5, &2]);
600 println!("No expiration yet, same elements: {:?}", queue.iter().collect::<Vec<_>>());
601
602 println!("\nAdding element 50 ...");
603 queue.push(50);
604 assert_eq!(queue.iter().collect::<Vec<_>>(), vec![&1, &5, &2, &50]);
605 println!("Same elements + 50: {:?}", queue.iter().collect::<Vec<_>>());
606
607 sleep_millis(100);
608 assert_eq!(queue.iter().collect::<Vec<_>>(), vec![&50]);
609 println!("Expired original list, only 50 in the list: {:?}",
610 queue.iter().collect::<Vec<_>>());
611
612 sleep_millis(200);
613 assert_eq!(queue.iter().collect::<Vec<_>>().len(), 0);
614 println!("No elements kept: {:?}", queue.iter().collect::<Vec<_>>());
615 }
616
617 #[test]
618 fn stats_empty_when_queue_not_initialized() {
619 let mut queue: SumQueue<i64> = SumQueue::new(Duration::from_millis(9000));
620 let stats = queue.stats();
621 assert_eq!(stats.min, None);
622 assert_eq!(stats.max, None);
623 assert_eq!(stats.sum, None);
624 assert_eq!(stats.len, 0);
625 }
626
627 #[test]
628 fn stats() {
629 let mut queue: SumQueue<i64> = SumQueue::new(Duration::from_secs(1000));
630 queue.push(-10);
631 queue.push(50);
632 queue.push(20);
633 queue.push(20);
634
635 let mut stats = queue.stats();
636 assert_eq!(stats.min, Some(-10));
637 assert_eq!(stats.max, Some(50));
638 assert_eq!(stats.sum, Some(80));
639 assert_eq!(stats.len, 4);
640
641 queue.clear();
642 stats = queue.stats();
643 assert_eq!(stats.min, None);
644 assert_eq!(stats.max, None);
645 assert_eq!(stats.sum, None);
646 assert_eq!(stats.len, 0);
647
648 queue.push(100_000);
649 stats = queue.stats();
650 assert_eq!(stats.min, Some(100_000));
651 assert_eq!(stats.max, Some(100_000));
652 assert_eq!(stats.sum, Some(100_000));
653 assert_eq!(stats.len, 1);
654
655 queue.push(5);
656 stats = queue.push_and_stats(1);
657 assert_eq!(stats.min, Some(1));
658 assert_eq!(stats.max, Some(100_000));
659 assert_eq!(stats.sum, Some(100_006));
660 assert_eq!(stats.len, 3);
661 }
662
663 #[cfg(test)]
664 fn sleep_secs(dur_secs: u64) {
665 println!("\nSleeping {} secs ...", dur_secs);
666 thread::sleep(Duration::from_secs(dur_secs));
667 }
668
669 #[cfg(test)]
670 fn sleep_millis(dur_millis: u64) {
671 println!("\nSleeping {} millis ...", dur_millis);
672 thread::sleep(Duration::from_millis(dur_millis));
673 }
674}