iced_debug/
lib.rs
1pub use iced_core as core;
2pub use iced_futures as futures;
3
4use crate::core::theme;
5use crate::core::window;
6use crate::futures::Subscription;
7
8pub use internal::Span;
9
10#[derive(Debug, Clone, Copy)]
11pub struct Metadata {
12 pub name: &'static str,
13 pub theme: Option<theme::Palette>,
14 pub can_time_travel: bool,
15}
16
17#[derive(Debug, Clone, Copy)]
18pub enum Primitive {
19 Quad,
20 Triangle,
21 Shader,
22 Image,
23 Text,
24}
25
26#[derive(Debug, Clone, Copy)]
27pub enum Command {
28 RewindTo { message: usize },
29 GoLive,
30}
31
32pub fn enable() {
33 internal::enable();
34}
35
36pub fn disable() {
37 internal::disable();
38}
39
40pub fn init(metadata: Metadata) {
41 internal::init(metadata);
42}
43
44pub fn quit() -> bool {
45 internal::quit()
46}
47
48pub fn theme_changed(f: impl FnOnce() -> Option<theme::Palette>) {
49 internal::theme_changed(f);
50}
51
52pub fn tasks_spawned(amount: usize) {
53 internal::tasks_spawned(amount);
54}
55
56pub fn subscriptions_tracked(amount: usize) {
57 internal::subscriptions_tracked(amount);
58}
59
60pub fn layers_rendered(amount: impl FnOnce() -> usize) {
61 internal::layers_rendered(amount);
62}
63
64pub fn boot() -> Span {
65 internal::boot()
66}
67
68pub fn update(message: &impl std::fmt::Debug) -> Span {
69 internal::update(message)
70}
71
72pub fn view(window: window::Id) -> Span {
73 internal::view(window)
74}
75
76pub fn layout(window: window::Id) -> Span {
77 internal::layout(window)
78}
79
80pub fn interact(window: window::Id) -> Span {
81 internal::interact(window)
82}
83
84pub fn draw(window: window::Id) -> Span {
85 internal::draw(window)
86}
87
88pub fn prepare(primitive: Primitive) -> Span {
89 internal::prepare(primitive)
90}
91
92pub fn render(primitive: Primitive) -> Span {
93 internal::render(primitive)
94}
95
96pub fn present(window: window::Id) -> Span {
97 internal::present(window)
98}
99
100pub fn time(name: impl Into<String>) -> Span {
101 internal::time(name)
102}
103
104pub fn time_with<T>(name: impl Into<String>, f: impl FnOnce() -> T) -> T {
105 let span = time(name);
106 let result = f();
107 span.finish();
108
109 result
110}
111
112pub fn commands() -> Subscription<Command> {
113 internal::commands()
114}
115
116#[cfg(all(feature = "enable", not(target_arch = "wasm32")))]
117mod internal {
118 use crate::core::theme;
119 use crate::core::time::Instant;
120 use crate::core::window;
121 use crate::futures::Subscription;
122 use crate::futures::futures::Stream;
123 use crate::{Command, Metadata, Primitive};
124
125 use iced_beacon as beacon;
126
127 use beacon::client::{self, Client};
128 use beacon::span;
129 use beacon::span::present;
130
131 use std::sync::atomic::{self, AtomicBool, AtomicUsize};
132 use std::sync::{LazyLock, RwLock};
133
134 pub fn init(metadata: Metadata) {
135 let name = metadata.name.split("::").next().unwrap_or(metadata.name);
136
137 *METADATA.write().expect("Write application metadata") =
138 client::Metadata {
139 name,
140 theme: metadata.theme,
141 can_time_travel: metadata.can_time_travel,
142 };
143 }
144
145 pub fn quit() -> bool {
146 if BEACON.is_connected() {
147 BEACON.quit();
148
149 true
150 } else {
151 false
152 }
153 }
154
155 pub fn theme_changed(f: impl FnOnce() -> Option<theme::Palette>) {
156 let Some(palette) = f() else {
157 return;
158 };
159
160 if METADATA.read().expect("Read last palette").theme.as_ref()
161 != Some(&palette)
162 {
163 log(client::Event::ThemeChanged(palette));
164
165 METADATA.write().expect("Write last palette").theme = Some(palette);
166 }
167 }
168
169 pub fn tasks_spawned(amount: usize) {
170 log(client::Event::CommandsSpawned(amount));
171 }
172
173 pub fn subscriptions_tracked(amount: usize) {
174 log(client::Event::SubscriptionsTracked(amount));
175 }
176
177 pub fn layers_rendered(amount: impl FnOnce() -> usize) {
178 log(client::Event::LayersRendered(amount()));
179 }
180
181 pub fn boot() -> Span {
182 span(span::Stage::Boot)
183 }
184
185 pub fn update(message: &impl std::fmt::Debug) -> Span {
186 let span = span(span::Stage::Update);
187
188 let number = LAST_UPDATE.fetch_add(1, atomic::Ordering::Relaxed);
189
190 let start = Instant::now();
191 let message = format!("{message:?}");
192 let elapsed = start.elapsed();
193
194 if elapsed.as_millis() >= 1 {
195 log::warn!(
196 "Slow `Debug` implementation of `Message` (took {elapsed:?})!"
197 );
198 }
199
200 let message = if message.len() > 49 {
201 message
202 .chars()
203 .take(49)
204 .chain("...".chars())
205 .collect::<String>()
206 } else {
207 message
208 };
209
210 log(client::Event::MessageLogged { number, message });
211
212 span
213 }
214
215 pub fn view(window: window::Id) -> Span {
216 span(span::Stage::View(window))
217 }
218
219 pub fn layout(window: window::Id) -> Span {
220 span(span::Stage::Layout(window))
221 }
222
223 pub fn interact(window: window::Id) -> Span {
224 span(span::Stage::Interact(window))
225 }
226
227 pub fn draw(window: window::Id) -> Span {
228 span(span::Stage::Draw(window))
229 }
230
231 pub fn prepare(primitive: Primitive) -> Span {
232 span(span::Stage::Prepare(to_primitive(primitive)))
233 }
234
235 pub fn render(primitive: Primitive) -> Span {
236 span(span::Stage::Render(to_primitive(primitive)))
237 }
238
239 pub fn present(window: window::Id) -> Span {
240 span(span::Stage::Present(window))
241 }
242
243 pub fn time(name: impl Into<String>) -> Span {
244 span(span::Stage::Custom(name.into()))
245 }
246
247 pub fn enable() {
248 ENABLED.store(true, atomic::Ordering::Relaxed);
249 }
250
251 pub fn disable() {
252 ENABLED.store(false, atomic::Ordering::Relaxed);
253 }
254
255 pub fn commands() -> Subscription<Command> {
256 fn listen_for_commands() -> impl Stream<Item = Command> {
257 use crate::futures::futures::stream;
258
259 stream::unfold(BEACON.subscribe(), async move |mut receiver| {
260 let command = match receiver.recv().await? {
261 client::Command::RewindTo { message } => {
262 Command::RewindTo { message }
263 }
264 client::Command::GoLive => Command::GoLive,
265 };
266
267 Some((command, receiver))
268 })
269 }
270
271 Subscription::run(listen_for_commands)
272 }
273
274 fn span(span: span::Stage) -> Span {
275 log(client::Event::SpanStarted(span.clone()));
276
277 Span {
278 span,
279 start: Instant::now(),
280 }
281 }
282
283 fn to_primitive(primitive: Primitive) -> present::Primitive {
284 match primitive {
285 Primitive::Quad => present::Primitive::Quad,
286 Primitive::Triangle => present::Primitive::Triangle,
287 Primitive::Shader => present::Primitive::Shader,
288 Primitive::Text => present::Primitive::Text,
289 Primitive::Image => present::Primitive::Image,
290 }
291 }
292
293 fn log(event: client::Event) {
294 if ENABLED.load(atomic::Ordering::Relaxed) {
295 BEACON.log(event);
296 }
297 }
298
299 #[derive(Debug)]
300 pub struct Span {
301 span: span::Stage,
302 start: Instant,
303 }
304
305 impl Span {
306 pub fn finish(self) {
307 log(client::Event::SpanFinished(self.span, self.start.elapsed()));
308 }
309 }
310
311 static BEACON: LazyLock<Client> = LazyLock::new(|| {
312 let metadata = METADATA.read().expect("Read application metadata");
313
314 client::connect(metadata.clone())
315 });
316
317 static METADATA: RwLock<client::Metadata> = RwLock::new(client::Metadata {
318 name: "",
319 theme: None,
320 can_time_travel: false,
321 });
322
323 static LAST_UPDATE: AtomicUsize = AtomicUsize::new(0);
324 static ENABLED: AtomicBool = AtomicBool::new(true);
325}
326
327#[cfg(any(not(feature = "enable"), target_arch = "wasm32"))]
328mod internal {
329 use crate::core::theme;
330 use crate::core::window;
331 use crate::futures::Subscription;
332 use crate::{Command, Metadata, Primitive};
333
334 pub fn enable() {}
335 pub fn disable() {}
336
337 pub fn init(_metadata: Metadata) {}
338
339 pub fn quit() -> bool {
340 false
341 }
342
343 pub fn theme_changed(_f: impl FnOnce() -> Option<theme::Palette>) {}
344
345 pub fn tasks_spawned(_amount: usize) {}
346
347 pub fn subscriptions_tracked(_amount: usize) {}
348
349 pub fn layers_rendered(_amount: impl FnOnce() -> usize) {}
350
351 pub fn boot() -> Span {
352 Span
353 }
354
355 pub fn update(_message: &impl std::fmt::Debug) -> Span {
356 Span
357 }
358
359 pub fn view(_window: window::Id) -> Span {
360 Span
361 }
362
363 pub fn layout(_window: window::Id) -> Span {
364 Span
365 }
366
367 pub fn interact(_window: window::Id) -> Span {
368 Span
369 }
370
371 pub fn draw(_window: window::Id) -> Span {
372 Span
373 }
374
375 pub fn prepare(_primitive: Primitive) -> Span {
376 Span
377 }
378
379 pub fn render(_primitive: Primitive) -> Span {
380 Span
381 }
382
383 pub fn present(_window: window::Id) -> Span {
384 Span
385 }
386
387 pub fn time(_name: impl Into<String>) -> Span {
388 Span
389 }
390
391 pub fn commands() -> Subscription<Command> {
392 Subscription::none()
393 }
394
395 #[derive(Debug)]
396 pub struct Span;
397
398 impl Span {
399 pub fn finish(self) {}
400 }
401}