use serde::{Deserialize, Serialize};
#[derive(Default, Serialize, Deserialize, Debug, Clone)]
pub struct SyncToggle {
#[serde(default)]
inner: std::sync::Arc<std::sync::atomic::AtomicBool>,
}
const ORDERING: std::sync::atomic::Ordering = std::sync::atomic::Ordering::Relaxed;
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum CurrentToggleState {
JustToggled,
AlreadyToggled,
Untoggled,
}
impl SyncToggle {
pub fn new() -> Self {
Self {
inner: Default::default(),
}
}
pub fn toggle_on_if_untoggled(&self) -> CurrentToggleState {
if self.inner.fetch_nand(false, ORDERING) {
CurrentToggleState::AlreadyToggled
} else {
CurrentToggleState::JustToggled
}
}
pub fn reset_and_get_previous(&self) -> CurrentToggleState {
if self.inner.fetch_and(false, ORDERING) {
CurrentToggleState::AlreadyToggled
} else {
CurrentToggleState::Untoggled
}
}
pub fn toggle_off(&self) {
self.inner.store(false, ORDERING)
}
pub fn state(&self) -> CurrentToggleState {
if self.inner.load(ORDERING) {
CurrentToggleState::AlreadyToggled
} else {
CurrentToggleState::Untoggled
}
}
}
#[cfg(test)]
mod tests {
use crate::sync_toggle::{CurrentToggleState, SyncToggle};
#[test]
fn test_sync_toggle() {
let toggle = SyncToggle::new();
assert_eq!(toggle.state(), CurrentToggleState::Untoggled);
assert_eq!(
toggle.toggle_on_if_untoggled(),
CurrentToggleState::JustToggled
);
toggle.toggle_off();
assert_eq!(toggle.state(), CurrentToggleState::Untoggled);
assert_eq!(
toggle.toggle_on_if_untoggled(),
CurrentToggleState::JustToggled
);
assert_eq!(toggle.state(), CurrentToggleState::AlreadyToggled);
assert_eq!(
toggle.toggle_on_if_untoggled(),
CurrentToggleState::AlreadyToggled
);
assert_eq!(toggle.state(), CurrentToggleState::AlreadyToggled);
assert_eq!(
toggle.toggle_on_if_untoggled(),
CurrentToggleState::AlreadyToggled
);
assert_eq!(toggle.state(), CurrentToggleState::AlreadyToggled);
toggle.toggle_off();
assert_eq!(toggle.state(), CurrentToggleState::Untoggled);
assert_eq!(
toggle.toggle_on_if_untoggled(),
CurrentToggleState::JustToggled
);
}
#[test]
fn test_sync_toggle_reset_and_get_previous() {
let toggle = SyncToggle::new();
assert_eq!(toggle.state(), CurrentToggleState::Untoggled);
assert_eq!(
toggle.reset_and_get_previous(),
CurrentToggleState::Untoggled
);
assert_eq!(toggle.state(), CurrentToggleState::Untoggled);
let _ = toggle.toggle_on_if_untoggled();
assert_eq!(toggle.state(), CurrentToggleState::AlreadyToggled);
assert_eq!(
toggle.reset_and_get_previous(),
CurrentToggleState::AlreadyToggled
);
assert_eq!(toggle.state(), CurrentToggleState::Untoggled);
}
}