Compare commits
6
Commits
7780dabed7
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14b79a170e | ||
|
|
0d384633f2 | ||
|
|
055c430e2d | ||
|
|
0d7d98c463 | ||
|
|
acc6bae9ca | ||
|
|
3d65cf3329 |
No files matched your search
+5
-12
@@ -1,12 +1,5 @@
|
|||||||
[package]
|
[workspace]
|
||||||
name = "treacle"
|
members = [
|
||||||
version = "0.1.0"
|
"treacle",
|
||||||
edition = "2021"
|
"treacle_notify",
|
||||||
authors = ["Tom Panton <pantonshire@gmail.com>"]
|
]
|
||||||
license = "MIT"
|
|
||||||
repository = "https://github.com/pantonshire/treacle"
|
|
||||||
description = "Event debouncer"
|
|
||||||
readme = "README.md"
|
|
||||||
keywords = ["debounce", "debouncer"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
@@ -9,5 +9,39 @@ to the template files in quick succession, it would be wasteful to reload the te
|
|||||||
change; instead, a debouncer could be used to group the changes that occur at a similar time into
|
change; instead, a debouncer could be used to group the changes that occur at a similar time into
|
||||||
a single change, so the templates are only reloaded once.
|
a single change, so the templates are only reloaded once.
|
||||||
|
|
||||||
## Usage
|
A new debouncer can be created with the
|
||||||
|
`debouncer`
|
||||||
|
function, which returns the debouncer in two halves: a tx (send) half and rx (receive) half. The
|
||||||
|
tx sends raw un-debounced events to the debouncer, and the rx receives the debounced events from
|
||||||
|
the debouncer. Both halves can be cloned to allow for multiple senders and receivers.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use std::{thread, time::Duration};
|
||||||
|
|
||||||
|
// Create a new debouncer which takes raw events of type `u32` and outputs
|
||||||
|
// debounced events of type `Vec<u32>`.
|
||||||
|
let (tx, rx) = treacle::debouncer::<u32, Vec<u32>, _>(
|
||||||
|
// Group events which occur in the same 500ms window.
|
||||||
|
Duration::from_millis(500),
|
||||||
|
// Combine raw events by pushing them to a vector.
|
||||||
|
|acc, raw_event| {
|
||||||
|
let mut events_vector = acc.unwrap_or_default();
|
||||||
|
events_vector.push(raw_event);
|
||||||
|
events_vector
|
||||||
|
});
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
// Send two raw events in quick succession.
|
||||||
|
tx.send(10).unwrap();
|
||||||
|
tx.send(20).unwrap();
|
||||||
|
|
||||||
|
// Wait, then send more raw events.
|
||||||
|
thread::sleep(Duration::from_millis(500));
|
||||||
|
tx.send(30).unwrap();
|
||||||
|
tx.send(40).unwrap();
|
||||||
|
tx.send(50).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(rx.recv().unwrap(), &[10, 20]);
|
||||||
|
assert_eq!(rx.recv().unwrap(), &[30, 40, 50]);
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "treacle"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["Tom Panton <pantonshire@gmail.com>"]
|
||||||
|
license = "MIT"
|
||||||
|
repository = "https://github.com/pantonshire/treacle"
|
||||||
|
homepage = "https://github.com/pantonshire/treacle"
|
||||||
|
description = "Event debouncer"
|
||||||
|
readme = "README.md"
|
||||||
|
keywords = ["debounce", "debouncer"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# `treacle`
|
||||||
|
A generic event debouncer for Rust, for grouping events which occur close together in time into a
|
||||||
|
single event.
|
||||||
|
|
||||||
|
For example, you may have some
|
||||||
|
[templates](https://www.arewewebyet.org/topics/templating/)
|
||||||
|
which you want to reload whenever a template file changes. However, if many small changes are made
|
||||||
|
to the template files in quick succession, it would be wasteful to reload the templates for every
|
||||||
|
change; instead, a debouncer could be used to group the changes that occur at a similar time into
|
||||||
|
a single change, so the templates are only reloaded once.
|
||||||
|
|
||||||
|
A new debouncer can be created with the
|
||||||
|
`debouncer`
|
||||||
|
function, which returns the debouncer in two halves: a tx (send) half and rx (receive) half. The
|
||||||
|
tx sends raw un-debounced events to the debouncer, and the rx receives the debounced events from
|
||||||
|
the debouncer. Both halves can be cloned to allow for multiple senders and receivers.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use std::{thread, time::Duration};
|
||||||
|
|
||||||
|
// Create a new debouncer which takes raw events of type `u32` and outputs
|
||||||
|
// debounced events of type `Vec<u32>`.
|
||||||
|
let (tx, rx) = treacle::debouncer::<u32, Vec<u32>, _>(
|
||||||
|
// Group events which occur in the same 500ms window.
|
||||||
|
Duration::from_millis(500),
|
||||||
|
// Combine raw events by pushing them to a vector.
|
||||||
|
|acc, raw_event| {
|
||||||
|
let mut events_vector = acc.unwrap_or_default();
|
||||||
|
events_vector.push(raw_event);
|
||||||
|
events_vector
|
||||||
|
});
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
// Send two raw events in quick succession.
|
||||||
|
tx.send(10).unwrap();
|
||||||
|
tx.send(20).unwrap();
|
||||||
|
|
||||||
|
// Wait, then send more raw events.
|
||||||
|
thread::sleep(Duration::from_millis(500));
|
||||||
|
tx.send(30).unwrap();
|
||||||
|
tx.send(40).unwrap();
|
||||||
|
tx.send(50).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(rx.recv().unwrap(), &[10, 20]);
|
||||||
|
assert_eq!(rx.recv().unwrap(), &[30, 40, 50]);
|
||||||
|
```
|
||||||
File renamed without changes.
File renamed without changes.
@@ -1,3 +1,48 @@
|
|||||||
|
//! A generic event debouncer, for grouping events which occur close together in time into a single
|
||||||
|
//! event.
|
||||||
|
//!
|
||||||
|
//! For example, you may have some
|
||||||
|
//! [templates](https://www.arewewebyet.org/topics/templating/)
|
||||||
|
//! which you want to reload whenever a template file changes. However, if many small changes are
|
||||||
|
//! made to the template files in quick succession, it would be wasteful to reload the templates
|
||||||
|
//! for every change; instead, a debouncer could be used to group the changes that occur at a
|
||||||
|
//! similar time into a single change, so the templates are only reloaded once.
|
||||||
|
//!
|
||||||
|
//! A new debouncer can be created with the [`debouncer`](debouncer) function, which returns the
|
||||||
|
//! debouncer in two halves: a tx (send) half and rx (receive) half. The tx sends raw un-debounced
|
||||||
|
//! events to the debouncer, and the rx receives the debounced events from the debouncer. Both
|
||||||
|
//! halves can be cloned to allow for multiple senders and receivers.
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use std::{thread, time::Duration};
|
||||||
|
//! // Create a new debouncer which takes raw events of type `u32` and outputs
|
||||||
|
//! // debounced events of type `Vec<u32>`.
|
||||||
|
//! let (tx, rx) = treacle::debouncer::<u32, Vec<u32>, _>(
|
||||||
|
//! // Group events which occur in the same 500ms window.
|
||||||
|
//! Duration::from_millis(500),
|
||||||
|
//! // Combine raw events by pushing them to a vector.
|
||||||
|
//! |acc, raw_event| {
|
||||||
|
//! let mut events_vector = acc.unwrap_or_default();
|
||||||
|
//! events_vector.push(raw_event);
|
||||||
|
//! events_vector
|
||||||
|
//! });
|
||||||
|
//!
|
||||||
|
//! thread::spawn(move || {
|
||||||
|
//! // Send two raw events in quick succession.
|
||||||
|
//! tx.send(10).unwrap();
|
||||||
|
//! tx.send(20).unwrap();
|
||||||
|
//!
|
||||||
|
//! // Wait, then send more raw events.
|
||||||
|
//! thread::sleep(Duration::from_millis(500));
|
||||||
|
//! tx.send(30).unwrap();
|
||||||
|
//! tx.send(40).unwrap();
|
||||||
|
//! tx.send(50).unwrap();
|
||||||
|
//! });
|
||||||
|
//!
|
||||||
|
//! assert_eq!(rx.recv().unwrap(), &[10, 20]);
|
||||||
|
//! assert_eq!(rx.recv().unwrap(), &[30, 40, 50]);
|
||||||
|
//! ```
|
||||||
|
|
||||||
pub mod fold;
|
pub mod fold;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
@@ -691,7 +736,7 @@ struct CountOverflowError;
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::{time::Duration, thread};
|
use std::{time::{Duration, Instant}, thread};
|
||||||
|
|
||||||
use super::{debouncer, fold};
|
use super::{debouncer, fold};
|
||||||
|
|
||||||
@@ -728,4 +773,58 @@ mod tests {
|
|||||||
assert_eq!(rx.recv().unwrap(), &[1, 2, 3]);
|
assert_eq!(rx.recv().unwrap(), &[1, 2, 3]);
|
||||||
assert!(rx.recv().is_err());
|
assert!(rx.recv().is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_multi_tx() {
|
||||||
|
let (tx1, rx) = debouncer(Duration::from_millis(100), fold::fold_vec_push::<u8>);
|
||||||
|
|
||||||
|
let tx2 = tx1.clone();
|
||||||
|
|
||||||
|
tx1.send(1).unwrap();
|
||||||
|
tx2.send(2).unwrap();
|
||||||
|
assert_eq!(rx.recv().unwrap(), &[1, 2]);
|
||||||
|
|
||||||
|
drop(tx1);
|
||||||
|
|
||||||
|
tx2.send(3).unwrap();
|
||||||
|
tx2.send(4).unwrap();
|
||||||
|
assert_eq!(rx.recv().unwrap(), &[3, 4]);
|
||||||
|
|
||||||
|
let start_time = Instant::now();
|
||||||
|
tx2.send(5).unwrap();
|
||||||
|
tx2.send(6).unwrap();
|
||||||
|
assert_eq!(rx.recv().unwrap(), &[5, 6]);
|
||||||
|
assert!(Instant::now().duration_since(start_time) >= Duration::from_millis(100));
|
||||||
|
|
||||||
|
tx2.send(7).unwrap();
|
||||||
|
tx2.send(8).unwrap();
|
||||||
|
drop(tx2);
|
||||||
|
assert_eq!(rx.recv().unwrap(), &[7, 8]);
|
||||||
|
assert!(rx.recv().is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_multi_rx() {
|
||||||
|
let (tx, rx1) = debouncer(Duration::from_millis(100), fold::fold_vec_push::<u8>);
|
||||||
|
|
||||||
|
let rx2 = rx1.clone();
|
||||||
|
|
||||||
|
tx.send(1).unwrap();
|
||||||
|
tx.send(2).unwrap();
|
||||||
|
assert_eq!(rx1.recv().unwrap(), &[1, 2]);
|
||||||
|
|
||||||
|
tx.send(3).unwrap();
|
||||||
|
tx.send(4).unwrap();
|
||||||
|
assert_eq!(rx2.recv().unwrap(), &[3, 4]);
|
||||||
|
|
||||||
|
drop(rx1);
|
||||||
|
|
||||||
|
tx.send(5).unwrap();
|
||||||
|
tx.send(6).unwrap();
|
||||||
|
assert_eq!(rx2.recv().unwrap(), &[5, 6]);
|
||||||
|
|
||||||
|
drop(rx2);
|
||||||
|
|
||||||
|
assert!(tx.send(7).is_err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "treacle_notify"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
notify = "6.1.1"
|
||||||
|
treacle = { version = "0.1.0", path = "../treacle" }
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
pub fn add(left: usize, right: usize) -> usize {
|
||||||
|
left + right
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
let result = add(2, 2);
|
||||||
|
assert_eq!(result, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user