notedeck: add debouncer util

I wanted this separate from the timed serializer so I could use it for
other things

Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
William Casarin
2025-03-06 15:10:47 -08:00
parent d19e4b1d2b
commit 5fde3277a1
3 changed files with 45 additions and 17 deletions

View File

@@ -0,0 +1,35 @@
use std::time::{Duration, Instant};
/// A simple debouncer that tracks when an action was last performed
/// and determines if enough time has passed to perform it again.
#[derive(Debug)]
pub struct Debouncer {
delay: Duration,
last_action: Instant,
}
impl Debouncer {
/// Creates a new Debouncer with the specified delay
pub fn new(delay: Duration) -> Self {
Self {
delay,
last_action: Instant::now() - delay, // Start ready to act
}
}
/// Sets a new delay value and returns self for method chaining
pub fn with_delay(mut self, delay: Duration) -> Self {
self.delay = delay;
self
}
/// Checks if enough time has passed since the last action
pub fn should_act(&self) -> bool {
self.last_action.elapsed() >= self.delay
}
/// Marks an action as performed, updating the timestamp
pub fn bounce(&mut self) {
self.last_action = Instant::now();
}
}

View File

@@ -2,6 +2,7 @@ mod accounts;
mod app;
mod args;
mod context;
pub mod debouncer;
mod error;
pub mod filter;
pub mod fonts;

View File

@@ -1,14 +1,13 @@
use std::time::{Duration, Instant};
use crate::debouncer::Debouncer;
use crate::{storage, DataPath, DataPathType, Directory};
use serde::{Deserialize, Serialize};
use tracing::info;
use std::time::Duration;
use tracing::info; // Adjust this import path as needed
pub struct TimedSerializer<T: PartialEq + Copy + Serialize + for<'de> Deserialize<'de>> {
directory: Directory,
file_name: String,
delay: Duration,
last_saved: Instant,
debouncer: Debouncer,
saved_item: Option<T>,
}
@@ -16,28 +15,24 @@ impl<T: PartialEq + Copy + Serialize + for<'de> Deserialize<'de>> TimedSerialize
pub fn new(path: &DataPath, path_type: DataPathType, file_name: String) -> Self {
let directory = Directory::new(path.path(path_type));
let delay = Duration::from_millis(1000);
let debouncer = Debouncer::new(delay);
Self {
directory,
file_name,
delay,
last_saved: Instant::now() - delay,
debouncer,
saved_item: None,
}
}
pub fn with_delay(mut self, delay: Duration) -> Self {
self.delay = delay;
self.debouncer = self.debouncer.with_delay(delay);
self
}
fn should_save(&self) -> bool {
self.last_saved.elapsed() >= self.delay
}
// returns whether successful
pub fn try_save(&mut self, cur_item: T) -> bool {
if self.should_save() {
if self.debouncer.should_act() {
if let Some(saved_item) = self.saved_item {
if saved_item != cur_item {
return self.save(cur_item);
@@ -53,7 +48,6 @@ impl<T: PartialEq + Copy + Serialize + for<'de> Deserialize<'de>> TimedSerialize
if self.saved_item.is_some() {
return self.saved_item;
}
if let Ok(file_contents) = self.directory.get_file(self.file_name.clone()) {
if let Ok(item) = serde_json::from_str::<T>(&file_contents) {
return Some(item);
@@ -61,7 +55,6 @@ impl<T: PartialEq + Copy + Serialize + for<'de> Deserialize<'de>> TimedSerialize
} else {
info!("Could not find file {}", self.file_name);
}
None
}
@@ -75,12 +68,11 @@ impl<T: PartialEq + Copy + Serialize + for<'de> Deserialize<'de>> TimedSerialize
.is_ok()
{
info!("wrote item {}", serialized_item);
self.last_saved = Instant::now();
self.debouncer.bounce();
self.saved_item = Some(cur_item);
return true;
}
}
false
}
}