use super::{InteractionUpdate, Label};
use std::{
sync::{
atomic::{AtomicU8, Ordering},
mpsc::Sender,
Arc,
},
time::Instant,
};
#[derive(Debug)]
pub struct Interaction {
result: AtomicU8,
weight: i16,
sender: Sender<InteractionUpdate>,
labels: Arc<Vec<u128>>,
}
impl Interaction {
const REJECT: u8 = 0;
const CANCEL: u8 = 1;
const ACCEPT: u8 = 2;
pub(super) fn new(sender: Sender<InteractionUpdate>, labels: Arc<Vec<u128>>) -> Self {
Self {
result: AtomicU8::new(Interaction::ACCEPT),
weight: 0,
sender,
labels,
}
}
pub fn labels<'a>(&'a self) -> impl Iterator<Item = Label<'static>> + 'a {
self.labels.iter().cloned().map(Label::from)
}
pub fn start(&self, weight: u8) -> Option<Interaction> {
if !self.score(-(weight as i16)).is_active() {
return None;
}
Some(Interaction {
result: AtomicU8::new(Interaction::REJECT),
weight: weight as i16,
sender: self.sender.clone(),
labels: self.labels.clone(),
})
}
pub fn finish(&self, success: impl Into<Option<bool>>) -> &Self {
match success.into() {
Some(true) => self.accept(),
Some(false) => self.reject(),
None => self.cancel(),
}
}
pub fn reject(&self) -> &Self {
self.set_result(Interaction::REJECT)
}
pub fn cancel(&self) -> &Self {
self.set_result(Interaction::CANCEL)
}
pub fn accept(&self) -> &Self {
self.set_result(Interaction::ACCEPT)
}
pub fn score(&self, score: i16) -> &Self {
self.sender.send(InteractionUpdate::Score(score)).ok();
self
}
pub fn is_active(&self) -> bool {
self.sender.send(InteractionUpdate::Score(0)).is_ok()
}
pub fn bye(self) -> bool {
self.is_active()
}
fn set_result(&self, new: u8) -> &Self {
let was = self.result.swap(new, Ordering::Relaxed) as i16;
self.score(self.weight * (new as i16 - was).clamp(-2, 2))
}
fn finished(&self) -> bool {
self.sender
.send(InteractionUpdate::Finished(Instant::now()))
.is_ok()
}
}
impl Drop for Interaction {
fn drop(&mut self) {
self.finished();
}
}