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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
//! Library-internal diagnostics sink.
//!
//! quadraui must never write to stdout/stderr on its own: a host embedding
//! one of quadraui's backends (vimcode's TUI, in raw mode on the alternate
//! screen, is the motivating case) owns the terminal, and a stray
//! `eprintln!` from library code lands as raw bytes in the host's live
//! cell grid — bypassing ratatui entirely, so the framework never learns
//! those cells were overwritten and the text persists until something
//! forces a full redraw.
//!
//! Instead, library code that wants to surface a diagnostic calls a
//! crate-internal `emit` function, which forwards the message to whatever
//! sink the host installed via [`set_sink`]. With no sink installed — the
//! default, and the state of every host that never calls `set_sink` —
//! diagnostics are silently dropped. This holds in debug and release
//! builds alike; unlike the old call sites, nothing here is gated on
//! `debug_assertions`.
//!
//! Hosts decide what "surfacing" means: a status line, a log file, a
//! `tracing` event, a debug panel. This module makes no assumption about
//! any of that — the sink is just `Fn(&str)`.
//!
//! # Example
//!
//! ```
//! use std::sync::atomic::{AtomicUsize, Ordering};
//! use std::sync::Arc;
//!
//! let seen = Arc::new(AtomicUsize::new(0));
//! let seen_in_sink = Arc::clone(&seen);
//! quadraui::diagnostics::set_sink(move |_msg: &str| {
//! seen_in_sink.fetch_add(1, Ordering::SeqCst);
//! });
//!
//! // A host only ever *installs* a sink; `emit` itself is
//! // crate-internal, called from the small set of library call sites
//! // listed in #619.
//!
//! quadraui::diagnostics::clear_sink();
//! ```
use ;
/// A host-installed diagnostic sink. See the module doc.
type Sink = ;
/// Install the sink that receives every future `emit` call, replacing
/// any previously installed sink.
///
/// The host owns delivery — log to a file, forward to `tracing`, append
/// to an in-app diagnostics panel, whatever fits. quadraui never touches
/// stdout/stderr on its own; a host that never calls this gets a
/// guaranteed-silent library.
///
/// # Example
///
/// ```
/// quadraui::diagnostics::set_sink(|msg: &str| {
/// eprintln!("[quadraui via host sink] {msg}");
/// });
/// # quadraui::diagnostics::clear_sink();
/// ```
/// Remove any installed sink, restoring the default silent behaviour.
/// Mainly useful for tests that install a sink temporarily — the sink is
/// process-global, so a test that installs one and doesn't clear it can
/// leak into an unrelated test running later in the same binary.
/// Emit a library diagnostic. Never writes anywhere itself — forwards to
/// the host's sink if one is installed via [`set_sink`], otherwise drops
/// the message on the floor.
///
/// Library-internal call sites only (not `pub`): the small, named set of
/// places in modal-paint tracking, vt100 panic recovery, the GTK/macOS
/// siblings of the same modal-paint check, `win/backend.rs`'s `end_frame`
/// reporting a dropped render target, and `desktop.rs`'s
/// `report_caught_panic_once` (the dedup wrapper `macos::run`/`win::run`
/// call after a `catch_unwind` guard catches a panic at their C-ABI
/// entry points). Genuinely CLI-shaped tools (examples, the GTK
/// headless-smoke harness) print directly and carry their own justified
/// `#[allow(clippy::print_stderr)]` instead of going through this sink —
/// they *are* the host, not a library embedded in one.
///
/// Every one of those call sites lives in a feature-gated module —
/// `tui/backend.rs`, `gtk/backend.rs`, `macos/backend.rs` (itself
/// additionally `target_os = "macos"`-gated in `lib.rs`, so on Linux the
/// `macos` feature compiles none of it), `win/backend.rs`'s `end_frame`
/// (itself additionally `target_os = "windows"`-gated, so on Linux the
/// `win` feature compiles none of it either — mirrors the `macos` case
/// exactly), `desktop.rs`'s `report_caught_panic_once` (live under plain
/// `feature = "win"` on any OS — unlike `end_frame`'s call site, its
/// callers `win::run::dispatch_event`/`render_frame` are deliberately
/// *not* `target_os`-gated, see those functions' docs — or under
/// `all(feature = "macos", target_os = "macos")`), and
/// `terminal_engine.rs`. This module is not gated, so a configuration
/// that enables none of them — a plain `cargo check -p quadraui` with no
/// features, or `--features macos` on a non-macOS host — compiles `emit`
/// with zero callers and trips `dead_code`. The allow is scoped by
/// `cfg_attr` to precisely that case rather than applied unconditionally,
/// so dead-code detection stays live in every configuration where a call
/// site actually compiles.
///
/// **The gate is per-profile as well as per-feature.** The three
/// modal-paint call sites (`tui`/`gtk`/`macos` `end_frame`) are each
/// additionally `#[cfg(debug_assertions)]` — the modal-paint check is a
/// debug-build-only diagnostic — so `--features tui` alone (or
/// `--features gtk,tui`) compiles *zero* `emit` callers in any profile
/// with assertions off. That is exactly what `cargo bench` does, and
/// CI's benchmark steps hit it in both the `tui` and `gtk` jobs. Hence
/// `all(debug_assertions, any(feature = "tui", feature = "gtk"))`
/// rather than a bare `any(…)` for those two. `terminal`, `win` and
/// macOS-on-macOS stay profile-independent because `terminal_engine.rs`'s
/// vt100-panic reporting and `desktop.rs`'s `report_caught_panic_once`
/// are live in release builds too.
pub
/// Serialises every test — in this module or any other — that exercises
/// the process-global sink slot via [`set_sink`]/[`clear_sink`]. `cargo
/// test` runs `#[test]` fns on separate threads by default, and two tests
/// mutating the same global `Option<Sink>` concurrently would each risk
/// observing (or clobbering) the other's installed sink instead of their
/// own.
///
/// `pub(crate)`, not module-private: `desktop.rs`'s panic-report-dedup
/// tests also call `set_sink`/`clear_sink` (to observe
/// `report_caught_panic_once`'s output), and need to serialise against
/// *this* module's tests too, not just against each other — a single
/// shared lock is what makes that true, rather than each module quietly
/// guarding its own tests against itself while still racing the other
/// module's.
pub