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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
//! [`PunnuExecutor`] — internal abstraction over runtime spawn / sleep
//! / now primitives.
//!
//! This is crate-internal in v0.1.0-beta.2. The public runtime surface stays
//! focused on feature selection (`runtime-tokio` or `runtime-wasm`) while the
//! crate keeps scheduling and clock reads behind one internal trait.
//!
//! # Why an internal trait
//!
//! The TTL sweep task and (later) the periodic-refresh helper need
//! `spawn`, `sleep`, and a monotonic clock. Routing those through a
//! trait lets sassi compile cleanly on **both** native (tokio) and
//! `wasm32-unknown-unknown` (gloo-timers + wasm-bindgen-futures)
//! targets without `cfg`-gating every call site.
//!
//! # Three primitives
//!
//! - [`PunnuExecutor::spawn`] — fire-and-forget background task.
//! - [`PunnuExecutor::sleep`] — async sleep on the runtime's timer.
//! - [`PunnuExecutor::now`] — read the monotonic clock. The clock
//! primitive is part of the executor (rather than a separate
//! `Clock` trait) because executor-internal cancellation, sleep
//! anchoring, and TTL bookkeeping all read the same clock; keeping
//! them on one type avoids a "which clock is which?" confusion.
//!
//! # Test determinism note
//!
//! On native, [`DefaultExecutor::now`] returns
//! [`tokio::time::Instant::now()`] — the paused-clock-aware variant.
//! Sassi's TTL tests use `#[tokio::test(start_paused = true)]` and
//! `tokio::time::advance(...)`; routing reads through the executor
//! preserves that determinism without exposing a tokio-specific knob
//! to consumers. The wasm-target counterpart wraps
//! [`web_time::Instant`] (which uses `Performance.now()` in the
//! browser). Direct WASM runtime tests are outside the current v0.1.0-beta.2
//! release gate.
use crateInstant;
use Duration;
// `BoxFut` is the executor's spawn / sleep payload. On native it's
// the `Send + 'static` `BoxFuture` (sassi assumes a multi-threaded
// runtime by default). On wasm it's `LocalBoxFuture` — the wasm
// browser runtime is single-threaded by construction, and several
// wasm-only primitives (`gloo_timers::future::TimeoutFuture`,
// `wasm_bindgen_futures::JsFuture`) hold `!Send` JS callbacks.
// Forcing `Send` would force every wasm sleep through a `Send`
// shim that doesn't add value on a single-threaded runtime.
//
// The split is `cfg(target_arch = "wasm32")` rather than
// `cfg(feature = "runtime-wasm")` because the bound depends on the
// target's threading model, not on which feature flag is selected.
// A native build with `runtime-wasm` enabled (inert per Cargo.toml
// docs) still uses the `Send` bound.
pub type BoxFut<'a> = BoxFuture;
pub type BoxFut<'a> = LocalBoxFuture;
/// Internal abstraction over runtime primitives: `spawn`, `sleep`, and `now`.
///
/// The trait is `Send + Sync` on native (the executor handle gets
/// shared across threads). On wasm it's still `Send + Sync` — the
/// trait object lives in `Arc<dyn PunnuExecutor>` which propagates
/// the bounds — but the futures it produces ([`BoxFut`]) are
/// `!Send` on wasm to allow `gloo_timers` and JS callback closures.
pub
/// Default runtime impl. Selected at compile time based on which
/// `runtime-*` feature is active and which target we're compiling for.
///
/// The unit struct carries no state; sassi constructs an `Arc<DefaultExecutor>`
/// at builder time.
pub ;
// Fallback impl for the awkward case where neither runtime feature
// is enabled (e.g., `cargo test --no-default-features` without
// `runtime-tokio`). The fallback panics on spawn / sleep — these
// methods are only called when the user asked for a background sweep
// (`PunnuConfig::ttl_sweep_interval = Some(...)`), periodic refresh, or
// delta refresh; the panic is a loud failure that points at the missing
// feature. `now` still works — the same target-aware alias as the
// runtime impls keeps the lazy-expiry path on `Punnu::get` usable
// without any executor feature.