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:
35
crates/notedeck/src/debouncer.rs
Normal file
35
crates/notedeck/src/debouncer.rs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ mod accounts;
|
|||||||
mod app;
|
mod app;
|
||||||
mod args;
|
mod args;
|
||||||
mod context;
|
mod context;
|
||||||
|
pub mod debouncer;
|
||||||
mod error;
|
mod error;
|
||||||
pub mod filter;
|
pub mod filter;
|
||||||
pub mod fonts;
|
pub mod fonts;
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
use std::time::{Duration, Instant};
|
use crate::debouncer::Debouncer;
|
||||||
|
|
||||||
use crate::{storage, DataPath, DataPathType, Directory};
|
use crate::{storage, DataPath, DataPathType, Directory};
|
||||||
use serde::{Deserialize, Serialize};
|
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>> {
|
pub struct TimedSerializer<T: PartialEq + Copy + Serialize + for<'de> Deserialize<'de>> {
|
||||||
directory: Directory,
|
directory: Directory,
|
||||||
file_name: String,
|
file_name: String,
|
||||||
delay: Duration,
|
debouncer: Debouncer,
|
||||||
last_saved: Instant,
|
|
||||||
saved_item: Option<T>,
|
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 {
|
pub fn new(path: &DataPath, path_type: DataPathType, file_name: String) -> Self {
|
||||||
let directory = Directory::new(path.path(path_type));
|
let directory = Directory::new(path.path(path_type));
|
||||||
let delay = Duration::from_millis(1000);
|
let delay = Duration::from_millis(1000);
|
||||||
|
let debouncer = Debouncer::new(delay);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
directory,
|
directory,
|
||||||
file_name,
|
file_name,
|
||||||
delay,
|
debouncer,
|
||||||
last_saved: Instant::now() - delay,
|
|
||||||
saved_item: None,
|
saved_item: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_delay(mut self, delay: Duration) -> Self {
|
pub fn with_delay(mut self, delay: Duration) -> Self {
|
||||||
self.delay = delay;
|
self.debouncer = self.debouncer.with_delay(delay);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_save(&self) -> bool {
|
|
||||||
self.last_saved.elapsed() >= self.delay
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns whether successful
|
// returns whether successful
|
||||||
pub fn try_save(&mut self, cur_item: T) -> bool {
|
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 let Some(saved_item) = self.saved_item {
|
||||||
if saved_item != cur_item {
|
if saved_item != cur_item {
|
||||||
return self.save(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() {
|
if self.saved_item.is_some() {
|
||||||
return self.saved_item;
|
return self.saved_item;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(file_contents) = self.directory.get_file(self.file_name.clone()) {
|
if let Ok(file_contents) = self.directory.get_file(self.file_name.clone()) {
|
||||||
if let Ok(item) = serde_json::from_str::<T>(&file_contents) {
|
if let Ok(item) = serde_json::from_str::<T>(&file_contents) {
|
||||||
return Some(item);
|
return Some(item);
|
||||||
@@ -61,7 +55,6 @@ impl<T: PartialEq + Copy + Serialize + for<'de> Deserialize<'de>> TimedSerialize
|
|||||||
} else {
|
} else {
|
||||||
info!("Could not find file {}", self.file_name);
|
info!("Could not find file {}", self.file_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,12 +68,11 @@ impl<T: PartialEq + Copy + Serialize + for<'de> Deserialize<'de>> TimedSerialize
|
|||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
info!("wrote item {}", serialized_item);
|
info!("wrote item {}", serialized_item);
|
||||||
self.last_saved = Instant::now();
|
self.debouncer.bounce();
|
||||||
self.saved_item = Some(cur_item);
|
self.saved_item = Some(cur_item);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user