timer-lib 0.4.0

A feature-rich Rust library for creating and managing timers.
Documentation
# timer-lib

`timer-lib` is a Tokio-based timer crate for one-shot and recurring async work.

It is built around a small set of handle types:

- `Timer` starts, pauses, resumes, stops, cancels, and joins runs.
- `TimerBuilder` reduces setup boilerplate for common configurations.
- `TimerRegistry` tracks timers by ID and provides bulk operations.
- `TimerEvents` exposes lossy lifecycle broadcasts.
- `TimerCompletion` exposes lossless completed-run delivery.

## Features

- One-shot and recurring timers
- Deadline-based one-shot scheduling
- Optional initial delay for recurring timers
- Optional recurring jitter
- Pause, resume, graceful stop, and immediate cancel
- Dynamic interval adjustment for live runs
- Per-callback timeout support
- Retry policy and retry backoff support for failed callbacks
- Run outcomes and execution statistics
- Broadcast lifecycle events plus lossless completion waiting
- Labels, metadata tags, timer snapshots, and registry listing/filtering helpers
- Registry helpers for managing many timers, including bulk pause/resume
- Closure-first API with optional trait-based callbacks
- Optional `test-util` feature for deterministic mocked time

## Installation

```toml
[dependencies]
timer-lib = "0.4.0"
tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] }
```

## Quick Start

```rust
use std::time::Duration;
use timer_lib::{RecurringSchedule, Timer, TimerFinishReason};

#[tokio::main]
async fn main() {
    let timer = Timer::new();

    timer
        .start_once(Duration::from_millis(250), || async {
            println!("ran once");
            Ok(())
        })
        .await
        .unwrap();

    let outcome = timer.join().await.unwrap();
    assert_eq!(outcome.reason, TimerFinishReason::Completed);
}
```

## Recurring Timers

```rust
use std::time::Duration;
use timer_lib::{RecurringSchedule, Timer, TimerFinishReason};

#[tokio::main]
async fn main() {
    let timer = Timer::recurring(
        RecurringSchedule::new(Duration::from_secs(1)).with_expiration_count(3),
    )
        .start(|| async {
            println!("tick");
            Ok(())
        })
        .await
        .unwrap();

    let outcome = timer.join().await.unwrap();
    assert_eq!(outcome.reason, TimerFinishReason::Completed);
    assert_eq!(outcome.statistics.execution_count, 3);
}
```

## Retry And Timeout Controls

```rust
use std::time::Duration;
use timer_lib::{Timer, TimerError};

#[tokio::main]
async fn main() {
    let timer = Timer::once(Duration::from_secs(1))
        .callback_timeout(Duration::from_secs(2))
        .max_retries(1)
        .start(|| async {
            // Do some async work here.
            Ok::<(), TimerError>(())
        })
        .await
        .unwrap();

    let outcome = timer.join().await.unwrap();
    println!("attempted: {}", outcome.statistics.execution_count);
}
```

## Deadlines, Jitter, And Backoff

```rust
use std::time::Duration;
use timer_lib::{RecurringSchedule, Timer, TimerError};
use tokio::time::Instant;

#[tokio::main]
async fn main() {
    let deadline = Instant::now() + Duration::from_secs(1);
    let timer = Timer::at(deadline)
        .start(|| async { Ok::<(), TimerError>(()) })
        .await
        .unwrap();

    let recurring = Timer::recurring(
        RecurringSchedule::new(Duration::from_secs(5))
            .with_jitter(Duration::from_secs(1))
            .with_expiration_count(3),
    )
    .max_retries(2)
    .exponential_backoff(Duration::from_millis(250))
    .start(|| async { Ok::<(), TimerError>(()) })
    .await
    .unwrap();

    let _ = timer.join().await.unwrap();
    let _ = recurring.join().await.unwrap();
}
```

## Events And Completion

Use `subscribe()` when you want a best-effort event stream and `completion()` when you need to reliably observe the final outcome of a run.

```rust
use std::time::Duration;
use timer_lib::{Timer, TimerEvent};

#[tokio::main]
async fn main() {
    let timer = Timer::new();
    let mut events = timer.subscribe();
    let mut completion = timer.completion();

    let run_id = timer
        .start_once(Duration::from_millis(100), || async { Ok(()) })
        .await
        .unwrap();

    if let Some(TimerEvent::Started { run_id: seen, .. }) = events.wait_started().await {
        assert_eq!(seen, run_id);
    }

    let outcome = completion.wait_for_run(run_id).await.unwrap();
    println!("finished: {:?}", outcome.reason);
}
```

## Managing Many Timers

```rust
use std::time::Duration;
use timer_lib::TimerRegistry;

#[tokio::main]
async fn main() {
    let registry = TimerRegistry::new();

    let (timer_id, timer) = registry
        .start_once(Duration::from_secs(1), || async { Ok(()) })
        .await
        .unwrap();

    assert!(registry.contains(timer_id).await);
    let _ = timer.join().await.unwrap();

    let completed = registry.join_all().await;
    assert!(!completed.is_empty());
}
```

## Labels And Snapshots

```rust
use std::time::Duration;
use timer_lib::Timer;

#[tokio::main]
async fn main() {
    let timer = Timer::once(Duration::from_secs(1))
        .label("billing")
        .tag("tenant", "acme")
        .start(|| async { Ok::<(), timer_lib::TimerError>(()) })
        .await
        .unwrap();

    let snapshot = timer.snapshot().await;
    assert_eq!(snapshot.metadata.label.as_deref(), Some("billing"));
}
```

## Callback Styles

The simplest API uses closures:

```rust
# use std::time::Duration;
# use timer_lib::Timer;
# async fn demo() {
let timer = Timer::new();
let _ = timer
    .start_once(Duration::from_secs(1), || async { Ok(()) })
    .await;
# }
```

If you need a reusable callback type, implement `TimerCallback`:

```rust
use async_trait::async_trait;
use timer_lib::{TimerCallback, TimerError};

struct MyCallback;

#[async_trait]
impl TimerCallback for MyCallback {
    async fn execute(&self) -> Result<(), TimerError> {
        Ok(())
    }
}
```

## Current Scope

`timer-lib` currently targets Tokio runtimes. It does not provide cron scheduling or `async-std` support.

For deterministic test control, enable the `test-util` feature and use `Timer::new_mocked()` or `TimerRegistry::new_mocked()`.

The next major documentation pass should live on docs.rs. For now, the most complete usage sample is [examples/feature_showcase.rs](examples/feature_showcase.rs).