#![deny(missing_debug_implementations)]
#![deny(missing_docs)]
#![allow(clippy::trivially_copy_pass_by_ref)]
#![allow(clippy::derive_hash_xor_eq)]
use std::hash::{Hash, Hasher};
use std::ops::Not;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering::SeqCst;
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct AlreadyZappedError;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct Fuse {
initial_state: bool,
zapped: bool,
}
impl Fuse {
pub fn new(initial_state: bool) -> Self {
Self {
initial_state,
zapped: false,
}
}
pub fn initial_state(&self) -> bool {
self.initial_state
}
pub fn as_bool(&self) -> bool {
self.initial_state ^ self.zapped
}
pub fn zap(&mut self) -> bool {
self.zapped |= true;
self.initial_state ^ true
}
pub fn zap_once(&mut self) -> Result<bool, AlreadyZappedError> {
if self.zapped {
return Err(AlreadyZappedError);
}
Ok(self.zap())
}
pub fn is_zapped(&self) -> bool {
self.zapped
}
}
impl From<bool> for Fuse {
fn from(b: bool) -> Self {
Self {
initial_state: b,
zapped: false,
}
}
}
impl Into<bool> for Fuse {
fn into(self) -> bool {
self.initial_state ^ self.zapped
}
}
impl Hash for Fuse {
fn hash<H: Hasher>(&self, state: &mut H) {
self.initial_state.hash(state);
self.is_zapped().hash(state);
}
}
impl Not for Fuse {
type Output = bool;
fn not(self) -> Self::Output {
!self.as_bool()
}
}
#[derive(Debug, Default)]
pub struct AtomicFuse {
initial_state: bool,
zapped: AtomicBool,
}
impl AtomicFuse {
pub fn new(initial_state: bool) -> Self {
Self {
initial_state,
zapped: AtomicBool::new(false),
}
}
pub fn initial_state(&self) -> bool {
self.initial_state
}
pub fn as_bool(&self) -> bool {
self.initial_state ^ self.zapped.load(SeqCst)
}
pub fn zap(&self) -> bool {
self.zapped.fetch_or(true, SeqCst);
self.initial_state ^ true
}
pub fn zap_once(&self) -> Result<bool, AlreadyZappedError> {
if self.zapped.compare_and_swap(false, true, SeqCst) {
return Err(AlreadyZappedError);
}
Ok(self.initial_state ^ true)
}
pub fn is_zapped(&self) -> bool {
self.zapped.load(SeqCst)
}
}
impl From<bool> for AtomicFuse {
fn from(b: bool) -> Self {
Self {
initial_state: b,
zapped: AtomicBool::new(false),
}
}
}
impl Into<bool> for AtomicFuse {
fn into(self) -> bool {
self.initial_state ^ self.zapped.into_inner()
}
}
impl Clone for AtomicFuse {
fn clone(&self) -> Self {
let zapped = self.zapped.load(SeqCst);
Self {
initial_state: self.initial_state,
zapped: AtomicBool::new(zapped),
}
}
}
impl PartialEq for AtomicFuse {
fn eq(&self, other: &Self) -> bool {
self.is_zapped() == other.is_zapped() && self.initial_state == other.initial_state
}
}
impl Eq for AtomicFuse {}
impl Hash for AtomicFuse {
fn hash<H: Hasher>(&self, state: &mut H) {
self.initial_state.hash(state);
self.is_zapped().hash(state);
}
}
impl Not for AtomicFuse {
type Output = bool;
fn not(self) -> Self::Output {
!self.as_bool()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_defaults() {
{
let fuse = Fuse::default();
assert_eq!(fuse.initial_state(), false);
assert_eq!(fuse.as_bool(), false);
assert_eq!(fuse.is_zapped(), false);
}
{
let afuse = AtomicFuse::default();
assert_eq!(afuse.initial_state(), false);
assert_eq!(afuse.as_bool(), false);
assert_eq!(afuse.is_zapped(), false);
}
}
#[test]
fn test_zaps() {
for init in vec![false, true] {
{
let mut fuse = Fuse::new(init);
assert_eq!(fuse.as_bool(), init);
let new1 = fuse.zap_once().unwrap();
assert_eq!(new1, !init);
assert_eq!(fuse.as_bool(), !init);
assert_eq!(fuse.is_zapped(), true);
let err = fuse.zap_once().unwrap_err();
assert_eq!(err, AlreadyZappedError);
assert_eq!(fuse.as_bool(), !init);
let new2 = fuse.zap();
assert_eq!(fuse.as_bool(), !init);
assert_eq!(new2, !init);
}
{
let afuse = AtomicFuse::new(init);
assert_eq!(afuse.as_bool(), init);
let new1 = afuse.zap_once().unwrap();
assert_eq!(new1, !init);
assert_eq!(afuse.as_bool(), !init);
assert_eq!(afuse.is_zapped(), true);
let err = afuse.zap_once().unwrap_err();
assert_eq!(err, AlreadyZappedError);
assert_eq!(afuse.as_bool(), !init);
let new2 = afuse.zap();
assert_eq!(afuse.as_bool(), !init);
assert_eq!(new2, !init);
}
}
}
#[test]
fn test_ops() {
{
let f1 = Fuse::new(false);
assert!(!f1);
let f2 = Fuse::new(true);
assert!(f2);
assert!(!!f2 & true);
}
{
let a1 = AtomicFuse::new(false);
assert!(!a1);
let a2 = AtomicFuse::new(true);
assert!(a2.clone());
assert!(!!a2 & true);
}
{
let f1 = Fuse::from(false);
let f2 = Fuse::from(true);
assert!(f1 == f1);
assert_ne!(f1, f2);
assert_ne!(bool::from(f1.into()), bool::from(f2.into()));
}
{
let a1 = AtomicFuse::from(false);
let a2 = AtomicFuse::new(true);
assert!(a1 == a1);
assert_ne!(a1, a2);
assert_ne!(bool::from(a1.into()), bool::from(a2.into()));
}
}
}