Skip to main content

aria_kit/audio/
stop.rs

1//! How a running capture learns it should end.
2
3use std::sync::Arc;
4use std::sync::atomic::{AtomicBool, Ordering};
5
6use crate::cancel::CancelToken;
7
8/// The capture-side stop check. Owned by the capture worker, which drops it
9/// the moment recording ends — owners can rely on that drop for cleanup.
10pub struct StopSignal(Box<dyn Fn() -> bool + Send>);
11
12impl StopSignal {
13    /// A capture that only its mode or timeout ends.
14    pub fn never() -> Self {
15        Self(Box::new(|| false))
16    }
17
18    pub fn from_fn(should_stop: impl Fn() -> bool + Send + 'static) -> Self {
19        Self(Box::new(should_stop))
20    }
21
22    pub fn should_stop(&self) -> bool {
23        (self.0)()
24    }
25}
26
27impl From<CancelToken> for StopSignal {
28    fn from(cancel: CancelToken) -> Self {
29        Self::from_fn(move || cancel.is_cancelled())
30    }
31}
32
33impl From<Stopper> for StopSignal {
34    fn from(stopper: Stopper) -> Self {
35        Self::from_fn(move || stopper.is_stopped())
36    }
37}
38
39/// A handle that ends a recording from anywhere: clone it, hand it to a
40/// hotkey or UI, call `stop()`.
41#[derive(Clone, Debug, Default)]
42pub struct Stopper(Arc<AtomicBool>);
43
44impl Stopper {
45    pub fn new() -> Self {
46        Self::default()
47    }
48
49    pub fn stop(&self) {
50        self.0.store(true, Ordering::Release);
51    }
52
53    pub fn is_stopped(&self) -> bool {
54        self.0.load(Ordering::Acquire)
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn never_stays_quiet() {
64        assert!(!StopSignal::never().should_stop());
65    }
66
67    #[test]
68    fn a_stopper_reaches_the_signal_through_a_clone() {
69        let stopper = Stopper::new();
70        let signal = StopSignal::from(stopper.clone());
71
72        assert!(!signal.should_stop());
73        stopper.stop();
74        assert!(signal.should_stop());
75    }
76
77    #[test]
78    fn a_cancel_token_reads_through() {
79        let cancel = CancelToken::new();
80        let signal = StopSignal::from(cancel.clone());
81
82        assert!(!signal.should_stop());
83        cancel.cancel();
84        assert!(signal.should_stop());
85    }
86}