use std::sync::{Arc, Condvar, Mutex, MutexGuard, PoisonError};
use std::time::{Duration as StdDuration, Instant};
use crossbeam_channel::Sender;
use crate::engine::WorkerMsg;
use crate::events::{
FinishReason, IconAnimationState, StartContext, StopMode, TickContext,
};
use crate::{FinalCommitOutcome, IconId};
pub(crate) type ObserverStart = Arc<dyn Fn(&StartContext) + Send + Sync>;
pub(crate) type ObserverTick = Arc<dyn Fn(&TickContext) + Send + Sync>;
pub(crate) type ObserverIconComplete = Arc<dyn Fn(&IconId) + Send + Sync>;
pub(crate) type ObserverFinish = Arc<dyn Fn(&FinishReason) + Send + Sync>;
#[derive(Default)]
pub(crate) struct Observers {
pub on_start: Vec<ObserverStart>,
pub on_tick: Vec<ObserverTick>,
pub on_icon_complete: Vec<ObserverIconComplete>,
pub on_finish: Vec<ObserverFinish>,
}
#[derive(Default, Clone)]
pub struct PreObservers {
pub(crate) on_start: Vec<ObserverStart>,
pub(crate) on_tick: Vec<ObserverTick>,
pub(crate) on_icon_complete: Vec<ObserverIconComplete>,
pub(crate) on_finish: Vec<ObserverFinish>,
}
impl PreObservers {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn on_start<F>(mut self, cb: F) -> Self
where
F: Fn(&StartContext) + Send + Sync + 'static,
{
self.on_start.push(Arc::new(cb));
self
}
#[must_use]
pub fn on_tick<F>(mut self, cb: F) -> Self
where
F: Fn(&TickContext) + Send + Sync + 'static,
{
self.on_tick.push(Arc::new(cb));
self
}
#[must_use]
pub fn on_icon_complete<F>(mut self, cb: F) -> Self
where
F: Fn(&IconId) + Send + Sync + 'static,
{
self.on_icon_complete.push(Arc::new(cb));
self
}
#[must_use]
pub fn on_finish<F>(mut self, cb: F) -> Self
where
F: Fn(&FinishReason) + Send + Sync + 'static,
{
self.on_finish.push(Arc::new(cb));
self
}
}
#[derive(Default)]
pub(crate) struct HandleState {
pub running: bool,
pub progress: f32,
pub per_icon: Vec<IconAnimationState>,
pub missing: Vec<IconId>,
pub finish_reason: Option<FinishReason>,
pub final_commit: Option<FinalCommitOutcome>,
}
pub(crate) struct HandleInner {
pub(crate) ctrl_tx: Sender<WorkerMsg>,
pub(crate) state: Mutex<HandleState>,
pub(crate) observers: Mutex<Observers>,
pub(crate) finish_cv: Condvar,
}
impl HandleInner {
pub(crate) fn new(ctrl_tx: Sender<WorkerMsg>) -> Arc<Self> {
Arc::new(Self {
ctrl_tx,
state: Mutex::new(HandleState {
running: true,
progress: 0.0,
per_icon: Vec::new(),
missing: Vec::new(),
finish_reason: None,
final_commit: None,
}),
observers: Mutex::new(Observers::default()),
finish_cv: Condvar::new(),
})
}
pub(crate) fn observers_snapshot(&self) -> Observers {
let g = self.lock_observers();
Observers {
on_start: g.on_start.clone(),
on_tick: g.on_tick.clone(),
on_icon_complete: g.on_icon_complete.clone(),
on_finish: g.on_finish.clone(),
}
}
pub(crate) fn install_pre_observers(&self, pre: PreObservers) {
let mut g = self.lock_observers();
g.on_start.extend(pre.on_start);
g.on_tick.extend(pre.on_tick);
g.on_icon_complete.extend(pre.on_icon_complete);
g.on_finish.extend(pre.on_finish);
}
#[inline]
pub(crate) fn lock_state(&self) -> MutexGuard<'_, HandleState> {
self.state.lock().unwrap_or_else(PoisonError::into_inner)
}
#[inline]
pub(crate) fn lock_observers(&self) -> MutexGuard<'_, Observers> {
self.observers
.lock()
.unwrap_or_else(PoisonError::into_inner)
}
}
#[derive(Clone)]
pub struct AnimationHandle {
inner: Arc<HandleInner>,
}
impl AnimationHandle {
pub(crate) fn from_inner(inner: Arc<HandleInner>) -> Self {
Self { inner }
}
pub fn is_running(&self) -> bool {
self.inner.lock_state().running
}
pub fn progress(&self) -> f32 {
self.inner.lock_state().progress
}
pub fn snapshot(&self) -> Vec<IconAnimationState> {
self.inner.lock_state().per_icon.clone()
}
pub fn missing_icons(&self) -> Vec<IconId> {
self.inner.lock_state().missing.clone()
}
pub fn finish_reason(&self) -> Option<FinishReason> {
self.inner.lock_state().finish_reason.clone()
}
pub fn final_commit(&self) -> Option<FinalCommitOutcome> {
self.inner.lock_state().final_commit.clone()
}
pub fn stop(&self, mode: StopMode) {
let _ = self.inner.ctrl_tx.send(WorkerMsg::AnimStop { mode, owner: Arc::downgrade(&self.inner) });
}
pub fn wait(&self) -> FinishReason {
let mut state = self.inner.lock_state();
while state.running {
state = self
.inner
.finish_cv
.wait(state)
.unwrap_or_else(PoisonError::into_inner);
}
state
.finish_reason
.clone()
.unwrap_or(FinishReason::Completed)
}
pub fn wait_timeout(&self, timeout: StdDuration) -> Option<FinishReason> {
let mut state = self.inner.lock_state();
let deadline = Instant::now() + timeout;
while state.running {
let now = Instant::now();
if now >= deadline {
return None;
}
let (new_state, result) = self
.inner
.finish_cv
.wait_timeout(state, deadline - now)
.unwrap_or_else(PoisonError::into_inner);
state = new_state;
if result.timed_out() && state.running {
return None;
}
}
Some(
state
.finish_reason
.clone()
.unwrap_or(FinishReason::Completed),
)
}
pub fn on_start<F>(&self, cb: F)
where
F: Fn(&StartContext) + Send + Sync + 'static,
{
self.inner.lock_observers().on_start.push(Arc::new(cb));
}
pub fn on_tick<F>(&self, cb: F)
where
F: Fn(&TickContext) + Send + Sync + 'static,
{
self.inner.lock_observers().on_tick.push(Arc::new(cb));
}
pub fn on_icon_complete<F>(&self, cb: F)
where
F: Fn(&IconId) + Send + Sync + 'static,
{
self.inner
.lock_observers()
.on_icon_complete
.push(Arc::new(cb));
}
pub fn on_finish<F>(&self, cb: F)
where
F: Fn(&FinishReason) + Send + Sync + 'static,
{
self.inner.lock_observers().on_finish.push(Arc::new(cb));
}
}