use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use super::sink::KeyLogSink;
#[derive(Debug)]
pub struct ToggleableKeyLogSink<S> {
inner: S,
enabled: Arc<AtomicBool>,
}
impl<S> ToggleableKeyLogSink<S> {
pub fn new(inner: S) -> Self {
Self {
inner,
enabled: Arc::new(AtomicBool::new(false)),
}
}
pub fn toggle(&self) -> KeyLogToggle {
KeyLogToggle {
enabled: Arc::clone(&self.enabled),
}
}
#[must_use]
pub fn is_enabled(&self) -> bool {
self.enabled.load(Ordering::Relaxed)
}
pub fn set_enabled(&self, on: bool) {
self.enabled.store(on, Ordering::Relaxed);
}
#[must_use]
pub fn inner(&self) -> &S {
&self.inner
}
}
impl<S: KeyLogSink> KeyLogSink for ToggleableKeyLogSink<S> {
#[inline]
fn write_line(&self, line: &str) {
if self.enabled.load(Ordering::Relaxed) {
self.inner.write_line(line);
}
}
}
#[derive(Debug, Clone)]
pub struct KeyLogToggle {
enabled: Arc<AtomicBool>,
}
impl KeyLogToggle {
pub fn enable(&self) {
self.enabled.store(true, Ordering::Relaxed);
}
pub fn disable(&self) {
self.enabled.store(false, Ordering::Relaxed);
}
pub fn set(&self, on: bool) {
self.enabled.store(on, Ordering::Relaxed);
}
#[must_use]
pub fn is_enabled(&self) -> bool {
self.enabled.load(Ordering::Relaxed)
}
}
#[cfg(test)]
mod tests {
use super::*;
use parking_lot::Mutex;
#[derive(Debug, Default)]
struct Capture(Mutex<Vec<String>>);
impl KeyLogSink for Capture {
fn write_line(&self, line: &str) {
self.0.lock().push(line.to_owned());
}
}
#[test]
fn defaults_off_and_drops_lines() {
let inner = Capture::default();
let wrap = ToggleableKeyLogSink::new(inner);
assert!(!wrap.is_enabled());
wrap.write_line("a\n");
wrap.write_line("b\n");
assert!(wrap.inner().0.lock().is_empty());
}
#[test]
fn enable_then_forwards() {
let inner = Capture::default();
let wrap = ToggleableKeyLogSink::new(inner);
wrap.set_enabled(true);
wrap.write_line("x\n");
wrap.set_enabled(false);
wrap.write_line("dropped\n");
assert_eq!(wrap.inner().0.lock().as_slice(), &["x\n"]);
}
#[test]
fn toggle_handle_flips_state() {
let wrap = ToggleableKeyLogSink::new(Capture::default());
let toggle = wrap.toggle();
assert!(!toggle.is_enabled());
toggle.enable();
assert!(wrap.is_enabled());
wrap.write_line("on\n");
toggle.disable();
wrap.write_line("off\n");
assert_eq!(wrap.inner().0.lock().as_slice(), &["on\n"]);
}
#[test]
fn toggle_handle_set_is_idempotent() {
let wrap = ToggleableKeyLogSink::new(Capture::default());
let toggle = wrap.toggle();
toggle.set(true);
toggle.set(true);
assert!(toggle.is_enabled());
toggle.set(false);
toggle.set(false);
assert!(!toggle.is_enabled());
}
}