use std::{
sync::{
mpsc::{channel, Receiver, TryRecvError},
Arc,
},
time::{Duration, Instant},
};
use super::{Interaction, Label};
pub(super) enum InteractionUpdate {
Score(i16),
Finished(Instant),
}
#[derive(Debug)]
pub struct Visit {
started: Instant,
receiver: Option<Receiver<InteractionUpdate>>,
labels: Arc<Vec<u128>>,
duration: Duration,
score: i32,
}
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub struct VisitCounters {
pub duration: Duration,
pub score: i32,
}
impl Visit {
pub fn interaction(labels: Vec<u128>) -> (Visit, Interaction) {
let labels = Arc::new(labels);
let (sender, receiver) = channel();
(
Visit::new(receiver, labels.clone()),
Interaction::new(sender, labels),
)
}
fn new(receiver: Receiver<InteractionUpdate>, labels: Arc<Vec<u128>>) -> Self {
Self {
started: Instant::now(),
receiver: Some(receiver),
labels,
duration: Duration::ZERO,
score: 0,
}
}
pub fn check(&mut self) -> VisitCounters {
while let Some(rcvr) = &self.receiver {
match rcvr.try_recv() {
Err(TryRecvError::Disconnected) => {
self.receiver = None;
break;
}
Err(TryRecvError::Empty) => {
self.duration = Instant::now() - self.started;
break;
}
Ok(InteractionUpdate::Score(score)) => {
self.duration = Instant::now() - self.started;
self.score = self.score.saturating_add(score as i32);
}
Ok(InteractionUpdate::Finished(at)) => {
self.duration = at - self.started;
}
};
}
VisitCounters {
duration: self.duration,
score: self.score,
}
}
pub fn stop(&mut self) {
self.check();
self.receiver = None;
}
pub fn is_finished(&self) -> bool {
self.receiver.is_none()
}
pub fn labels<'a>(&'a self) -> impl Iterator<Item = Label<'static>> + 'a {
self.labels.iter().cloned().map(Label::from)
}
}
#[test]
fn test_visit_duration() {
let sleep = Duration::from_millis(108);
let (mut v, i) = Visit::interaction(vec![]);
std::thread::sleep(sleep);
drop(i);
std::thread::sleep(sleep);
let dur = v.check().duration.as_millis();
assert_eq!(dur, sleep.as_millis());
}
#[test]
fn test_visit_duration_multi() {
let (mut visit, interaction) = Visit::interaction(vec![]);
interaction.score(5);
interaction.accept();
assert_eq!(visit.check().score, 5, "main one completed");
let i7 = interaction.start(7).expect("interaction 7");
let i108 = interaction.start(108).expect("interaction 108");
let i150 = interaction.start(150).expect("interaction 250");
let idur = interaction.start(0).expect("interaction for duration");
assert_eq!(visit.check().score, 5 - 7 - 108 - 150, "pending");
i7.cancel();
i108.accept();
i150.reject();
assert_eq!(
visit.check().score,
5 - 7 - 108 - 150 + 7 + 108 + 108,
"finished"
);
std::thread::sleep(std::time::Duration::from_millis(108));
assert_eq!(visit.check().duration.as_millis(), 108, "pending duration");
drop(interaction);
drop(idur);
drop(i7);
drop(i150);
drop(i108);
std::thread::sleep(std::time::Duration::from_millis(108));
assert_eq!(visit.check().duration.as_millis(), 108, "final duration");
}
#[test]
fn test_visit_duration_multi_interaction() {
let sleep = Duration::from_millis(108);
let (mut v, i) = Visit::interaction(vec![]);
let i2 = i.start(5);
std::thread::sleep(sleep);
drop(i);
std::thread::sleep(sleep);
drop(i2);
let dur = v.check().duration.as_millis();
assert_eq!(dur, 2 * sleep.as_millis());
}
#[test]
fn test_visit_duration_pending() {
let sleep = Duration::from_millis(108);
let (mut v, i) = Visit::interaction(vec![]);
std::thread::sleep(sleep);
let dur = v.check().duration.as_millis();
assert_eq!(dur, sleep.as_millis());
drop(i);
}
#[test]
fn test_visit_duration_quick() {
let sleep = Duration::from_millis(108);
let (mut v, i) = Visit::interaction(vec![]);
drop(i);
std::thread::sleep(sleep);
let dur = v.check().duration.as_millis();
assert_eq!(dur, 0);
}
#[test]
fn test_visit_score() {
let (mut visit, interaction) = Visit::interaction(vec![]);
interaction.score(5);
interaction.accept();
assert_eq!(visit.check().score, 5, "init");
let i7 = interaction.start(7).expect("interaction 7");
let i108 = interaction.start(108).expect("interaction 108");
let i150 = interaction.start(150).expect("interaction 250");
assert_eq!(visit.check().score, 5 - 7 - 108 - 150, "pending");
i7.cancel();
i108.accept();
i150.reject();
assert_eq!(
visit.check().score,
5 - 7 - 108 - 150 + 7 + 2 * 108,
"finished"
);
}