bouncing 0.1.0

A flexible async debouncer for Rust with cancellation support, max wait limits, and event hooks
Documentation
# bouncing

Async debouncer for Rust/Tokio with cancellation support, max wait limits, and event hooks.

## Quick Start

```rust
use bouncing::Debouncer;
use std::time::Duration;
use tokio_util::sync::CancellationToken;

let cancel_token = CancellationToken::new();
let debouncer = Debouncer::new(Duration::from_millis(100), cancel_token.clone(), "my_task");

// Rapid calls - only the last one executes after 100ms of quiet
debouncer.debounce(|token| async move {
    // your async work here
    // check token.cancelled() for graceful shutdown
}).await;
```

## API

### Debouncer

Core debouncer - pass a new closure each call.

```rust
let debouncer = Debouncer::new(duration, cancel_token, "name")
    .with_task_timeout(Duration::from_secs(5))  // abort timeout after cancel
    .with_max_wait(Duration::from_secs(30))     // force execution after max wait
    .with_event_handler(|event| { /* ... */ });

debouncer.debounce(|token| async move { /* ... */ }).await;
debouncer.run_now(|token| async move { /* ... */ }).await;  // bypass debounce
debouncer.stop().await;
```

### StoredTaskDebouncer

Stores a fixed task function - just call `debounce()`.

```rust
let debouncer = StoredTaskDebouncer::new(
    Duration::from_millis(100),
    cancel_token,
    "my_task",
    |token| async move { /* your work */ },
);

debouncer.debounce().await;  // no closure needed
debouncer.set_task(|token| async move { /* new work */ });  // swap task
```

### TaskDebouncer\<T\>

Trait-based - implement `DebouncedTask` on your type.

```rust
use bouncing::{DebouncedTask, TaskDebouncer};

struct MyTask { /* state */ }

impl DebouncedTask for MyTask {
    async fn execute(&self, token: CancellationToken) {
        // your work
    }
}

let debouncer = TaskDebouncer::new(Duration::from_millis(100), cancel_token, "name", MyTask {});
debouncer.debounce().await;
```

## Cancellation

All task closures receive a `CancellationToken`. Check it for graceful shutdown:

```rust
debouncer.debounce(|token| async move {
    tokio::select! {
        _ = token.cancelled() => { /* cleanup */ }
        _ = do_work() => {}
    }
}).await;
```

**Exit states:**
- `Normal` - task completed
- `Cancelled` - task responded to cancellation
- `Aborted` - task didn't exit within timeout, was force-aborted
- `NotStarted` - timer cancelled before task started

## Max Wait

Prevents starvation when events fire continuously:

```rust
let debouncer = Debouncer::new(Duration::from_millis(100), token, "name")
    .with_max_wait(Duration::from_secs(5));  // force run after 5s of continuous debouncing
```

## Events

Hook into lifecycle events:

```rust
use bouncing::DebounceEvent;

let debouncer = Debouncer::new(duration, token, "name")
    .with_event_handler(|event| match event {
        DebounceEvent::Debounced { instant, first_debounce_at, debounce_ends_at } => {}
        DebounceEvent::Started { instant } => {}
        DebounceEvent::Ended { instant, exit_status } => {}
    });
```

## Builder Methods

All three debouncer types support:
- `with_task_timeout(Duration)` - time to wait for graceful exit before abort
- `with_max_wait(Duration)` - max time to debounce before forcing execution
- `with_event_handler(Fn)` - lifecycle event callback
- `stop()` - cancel current task and timer