1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//! # flowd
//!
//! A Redis Streams-backed task queue with automatic struct ↔ `redis::Value`
//! mapping via a derive macro.
//!
//! ## Features
//!
//! - **`Job` derive macro** — generates bidirectional conversions between
//! Rust structs and `Vec<(String, redis::Value)>` pairs for zero-copy Redis
//! integration.
//! - **Consumer queue** — reads from a Redis Stream consumer group with
//! semaphore-based concurrency control and graceful shutdown.
//! - **Claimer** — reclaims stuck messages via `XAUTOCLAIM`, tracks delivery
//! counts, and routes exhausted messages to a dead-letter callback.
//! - **Runtime-agnostic** — works with either `tokio` or `smol`
//! (mutually exclusive feature flags).
//!
//! ## Quick start
//!
//! ```rust,ignore
//! use flowd::prelude::*;
//! use flowd::task::{Queue, QueueBuilder};
//!
//! #[derive(Debug, Job)]
//! struct Email {
//! to: String,
//! subject: String,
//! }
//!
//! # async fn run() -> anyhow::Result<()> {
//! let client = redis::Client::open("redis://127.0.0.1:6379")?;
//! let conn = client.get_multiplexed_async_connection().await?;
//! let read_conn = client.get_multiplexed_async_connection().await?;
//!
//! let queue = Queue::new(
//! QueueBuilder::new(
//! "emails", "senders", "sender-1",
//! |email: Email| async move {
//! println!("sending to {}", email.to);
//! Ok::<(), String>(())
//! },
//! conn,
//! read_conn,
//! )
//! .block_timeout(5000)
//! .max_concurrent_tasks(10),
//! );
//!
//! queue.init(None).await?;
//! let handle = queue.run();
//! // ... later ...
//! handle.shutdown().await;
//! # Ok(()) }
//! ```
//!
//! ## Runtime selection
//!
//! Enable exactly one of:
//! - `tokio` — uses `tokio::sync::Semaphore`, `tokio::task::JoinSet`
//! - `smol` — uses `mea::semaphore::Semaphore`, `FuturesUnordered`
compile_error!;
compile_error!;
pub use crateJob;
use Error;
// Re-export for use by the derive macro's generated code
pub use redis;
// ── Generic utilities ─────────────────────────────────────────────────────────
/// Pretty-print every field of a `Job` value.
/// Round-trip a value through pairs — useful for testing your mapper.
// ── Prelude ───────────────────────────────────────────────────────────────────
/// Convenience re-exports for common usage.
///
/// ```rust,ignore
/// use flowd::prelude::*;
/// ```