use chrono;
use chrono::DateTime;
use chrono::Utc;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::default::Default;
use std::time::Instant;
#[derive(Default)]
struct MockClockPerState {
utc_list: VecDeque<DateTime<Utc>>,
instant_list: VecDeque<Instant>,
utc_call_count: u64,
instant_call_count: u64,
}
#[derive(Default)]
struct MockClockPerThread {
mock: Option<MockClockPerState>,
}
impl MockClockPerThread {
fn with<F, T>(f: F) -> T
where
F: FnOnce(&mut MockClockPerThread) -> T,
{
thread_local! {
static INSTANCE: RefCell<MockClockPerThread> = RefCell::default()
}
INSTANCE.with(|it| f(&mut *it.borrow_mut()))
}
}
pub struct MockClockGuard {}
impl MockClockGuard {
pub fn add_utc(&self, mock_date: DateTime<chrono::Utc>) {
MockClockPerThread::with(|clock| match &mut clock.mock {
Some(clock) => {
clock.utc_list.push_back(mock_date);
}
None => {
panic!("Use MockClockGuard in your test");
}
});
}
pub fn add_instant(&self, mock_date: Instant) {
MockClockPerThread::with(|clock| match &mut clock.mock {
Some(clock) => {
clock.instant_list.push_back(mock_date);
}
None => {
panic!("Use MockClockGuard in your test");
}
});
}
pub fn utc_call_count(&self) -> u64 {
MockClockPerThread::with(|clock| match &mut clock.mock {
Some(clock) => clock.utc_call_count,
None => {
panic!("Use MockClockGuard in your test");
}
})
}
pub fn instant_call_count(&self) -> u64 {
MockClockPerThread::with(|clock| match &mut clock.mock {
Some(clock) => clock.instant_call_count,
None => {
panic!("Use MockClockGuard in your test");
}
})
}
}
impl Default for MockClockGuard {
fn default() -> Self {
StaticClock::set_mock();
Self {}
}
}
impl Drop for MockClockGuard {
fn drop(&mut self) {
StaticClock::reset();
}
}
pub struct StaticClock {}
impl StaticClock {
fn set_mock() {
MockClockPerThread::with(|clock| {
assert!(clock.mock.is_none());
clock.mock = Some(MockClockPerState::default())
})
}
fn reset() {
MockClockPerThread::with(|clock| clock.mock = None);
}
pub fn instant() -> Instant {
MockClockPerThread::with(|clock| match &mut clock.mock {
Some(clock) => {
clock.instant_call_count += 1;
let x = clock.instant_list.pop_front();
match x {
Some(t) => t,
None => {
panic!("Mock clock run out of samples");
}
}
}
None => Instant::now(),
})
}
pub fn utc() -> DateTime<chrono::Utc> {
MockClockPerThread::with(|clock| match &mut clock.mock {
Some(clock) => {
clock.utc_call_count += 1;
let x = clock.utc_list.pop_front();
match x {
Some(t) => t,
None => {
panic!("Mock clock run out of samples");
}
}
}
None => chrono::Utc::now(),
})
}
}