Skip to main content

guinea_trace/
point.rs

1use std::fmt;
2
3/// Which bus carried a publication.
4#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
5pub enum Bus {
6    /// Every window hears it.
7    Global,
8    /// One window's own.
9    Window,
10}
11
12/// How a stored value changed.
13#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14pub enum StoreOp {
15    Set,
16    Delete,
17    /// Everything under the path went at once.
18    DeletePrefix,
19}
20
21/// An observable point, with what identifies it.
22///
23/// Names are short type names: `processes::Kill`, not a crate path.
24///
25/// New kinds of point come in patch releases, so a match on it outside this
26/// crate needs an arm for the ones it doesn't know yet:
27///
28/// ```compile_fail
29/// use guinea_trace::Point;
30///
31/// fn known(point: &Point) -> bool {
32///     match point {
33///         Point::Action { .. }
34///         | Point::Send { .. }
35///         | Point::Handle { .. }
36///         | Point::Spawn { .. }
37///         | Point::Settled { .. }
38///         | Point::Cancelled { .. }
39///         | Point::Source { .. }
40///         | Point::Arrived { .. }
41///         | Point::Pull { .. }
42///         | Point::Closed { .. }
43///         | Point::Publish { .. }
44///         | Point::Deliver { .. }
45///         | Point::Push { .. }
46///         | Point::Navigate { .. }
47///         | Point::Tick { .. }
48///         | Point::Store { .. }
49///         | Point::Render { .. }
50///         | Point::Log { .. }
51///         | Point::Span { .. }
52///         | Point::Note(_) => true,
53///     }
54/// }
55/// ```
56#[derive(Clone, Debug, PartialEq)]
57#[non_exhaustive]
58pub enum Point {
59    /// The UI asked a feature for something.
60    Action { message: &'static str },
61    /// A message was queued for an actor.
62    Send {
63        actor: &'static str,
64        message: &'static str,
65    },
66    /// An actor handled a message.
67    Handle {
68        actor: &'static str,
69        message: &'static str,
70    },
71    /// An actor started background work whose result will come back as
72    /// `output`. `actor` is the address it will come back to, by the id the
73    /// snapshot lists it under: what a task belongs to is what that actor
74    /// belongs to.
75    Spawn {
76        actor: &'static str,
77        actor_id: u64,
78        output: &'static str,
79    },
80    /// Background work finished, and its result is on its way to the actor.
81    Settled {
82        actor: &'static str,
83        actor_id: u64,
84        output: &'static str,
85        took_us: u64,
86    },
87    /// Background work was dropped where it last awaited, because the actor
88    /// that started it is gone. Nothing comes back.
89    Cancelled {
90        actor: &'static str,
91        actor_id: u64,
92        output: &'static str,
93        took_us: u64,
94    },
95    /// An actor opened a source whose items will come to it as `output`.
96    /// Recorded under what opened it, which is done with it once it is open:
97    /// the items are not its work.
98    Source {
99        actor: &'static str,
100        actor_id: u64,
101        output: &'static str,
102    },
103    /// An item came from a source, as `output`. A root, as a timer's tick
104    /// is; `source` is the id of the [`Point::Source`] it came from.
105    Arrived {
106        actor: &'static str,
107        actor_id: u64,
108        output: &'static str,
109        source: u64,
110    },
111    /// A source is making its next item, which will come as `output`: what
112    /// its stream does meanwhile is recorded under this. A root, as
113    /// [`Point::Arrived`] is; `source` is the id of the [`Point::Source`].
114    /// Open from when the source is asked for the item until it has it, runs
115    /// dry or is dropped.
116    Pull {
117        actor: &'static str,
118        actor_id: u64,
119        output: &'static str,
120        source: u64,
121    },
122    /// A source ended: it ran dry, or `gone` - the actor that opened it is
123    /// gone and the source was dropped where it last awaited.
124    Closed {
125        actor: &'static str,
126        actor_id: u64,
127        output: &'static str,
128        took_us: u64,
129        gone: bool,
130    },
131    /// An event went out.
132    Publish {
133        event: &'static str,
134        bus: Bus,
135        subscribers: usize,
136    },
137    /// An event reached a subscriber that is not an actor.
138    Deliver { event: &'static str, bus: Bus },
139    /// A reducer was changed.
140    Push { reducer: &'static str },
141    /// A router moved.
142    Navigate { root: String, to: String },
143    /// A timer fired; `timer` is its id, as the application's timers list
144    /// it. The rest says whose it is, so the record reads on its own once the
145    /// timer is gone: the name it was given, if any, and where it was set up.
146    Tick {
147        timer: u64,
148        name: Option<&'static str>,
149        file: &'static str,
150        line: u32,
151    },
152    /// A persisted value changed.
153    Store {
154        op: StoreOp,
155        path: String,
156        /// The declared field the path belongs to, `Settings.theme`, when
157        /// the store knows it.
158        field: Option<String>,
159        /// Whether the change came from outside the process, an edited file.
160        outside: bool,
161    },
162    /// A page or layout drew itself, and how long that took. Recorded only
163    /// for the frames worth looking at - see `observability::Rendering`.
164    Render {
165        segment: &'static str,
166        took_us: u64,
167    },
168    /// An ordinary `tracing` event the application wrote.
169    Log {
170        level: tracing::Level,
171        target: &'static str,
172        /// Where it was written, as the compiler named the file: relative to
173        /// the workspace root for the application's own crates.
174        file: Option<&'static str>,
175        line: Option<u32>,
176        module: Option<&'static str>,
177        /// The message, then the other fields as `name=value`.
178        text: String,
179    },
180    /// A `tracing` span the application opened - an `#[instrument]`ed
181    /// function, say. Open until the span closes, and current while it is
182    /// entered; what it took is the time it was entered, not the time it
183    /// waited in between.
184    Span {
185        name: &'static str,
186        /// The level it was opened at, as [`Point::Log`]'s.
187        level: tracing::Level,
188        target: &'static str,
189        /// Where it was written; see [`Point::Log`].
190        file: Option<&'static str>,
191        line: Option<u32>,
192        module: Option<&'static str>,
193        /// Its fields as `name=value`, as they were when it opened.
194        fields: String,
195    },
196    /// Anything else worth a line.
197    Note(String),
198}
199
200impl Point {
201    /// A short name for the kind of point, for filtering.
202    pub fn kind(&self) -> &'static str {
203        match self {
204            Point::Action { .. } => "action",
205            Point::Send { .. } => "send",
206            Point::Handle { .. } => "handle",
207            Point::Spawn { .. } => "spawn",
208            Point::Settled { .. } => "settled",
209            Point::Cancelled { .. } => "cancelled",
210            Point::Source { .. } => "source",
211            Point::Arrived { .. } => "arrived",
212            Point::Pull { .. } => "pull",
213            Point::Closed { .. } => "closed",
214            Point::Publish { .. } => "publish",
215            Point::Deliver { .. } => "deliver",
216            Point::Push { .. } => "push",
217            Point::Navigate { .. } => "navigate",
218            Point::Tick { .. } => "tick",
219            Point::Store { .. } => "store",
220            Point::Render { .. } => "render",
221            Point::Log { .. } => "log",
222            Point::Span { .. } => "span",
223            Point::Note(_) => "note",
224        }
225    }
226}
227
228impl fmt::Display for Bus {
229    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230        f.write_str(match self {
231            Bus::Global => "global",
232            Bus::Window => "window",
233        })
234    }
235}
236
237impl fmt::Display for StoreOp {
238    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239        f.write_str(match self {
240            StoreOp::Set => "set",
241            StoreOp::Delete => "delete",
242            StoreOp::DeletePrefix => "delete_prefix",
243        })
244    }
245}
246
247impl fmt::Display for Point {
248    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249        match self {
250            Point::Action { message } => write!(f, "action {message}"),
251            Point::Send { actor, message } => write!(f, "send {message} → {actor}"),
252            Point::Handle { actor, message, .. } => write!(f, "{actor} handles {message}"),
253            Point::Spawn { actor, output, .. } => write!(f, "{actor} starts work for {output}"),
254            Point::Settled {
255                actor,
256                output,
257                took_us,
258                ..
259            } => write!(
260                f,
261                "{actor} has its {output} after {:.1} ms",
262                *took_us as f64 / 1000.0
263            ),
264            Point::Cancelled {
265                actor,
266                output,
267                took_us,
268                ..
269            } => write!(
270                f,
271                "{actor} is gone: {output} cancelled after {:.1} ms",
272                *took_us as f64 / 1000.0
273            ),
274            Point::Source { actor, output, .. } => write!(f, "{actor} opens a source of {output}"),
275            Point::Arrived { actor, output, .. } => write!(f, "{output} arrives at {actor}"),
276            Point::Pull { actor, output, .. } => {
277                write!(f, "{actor}'s source makes the next {output}")
278            }
279            Point::Closed {
280                actor,
281                output,
282                took_us,
283                gone: true,
284                ..
285            } => write!(
286                f,
287                "{actor} is gone: its source of {output} closed after {:.1} ms",
288                *took_us as f64 / 1000.0
289            ),
290            Point::Closed {
291                actor,
292                output,
293                took_us,
294                ..
295            } => write!(
296                f,
297                "{actor}'s source of {output} ran dry after {:.1} ms",
298                *took_us as f64 / 1000.0
299            ),
300            Point::Publish {
301                event,
302                bus,
303                subscribers,
304            } => write!(f, "publish {event} on the {bus} bus to {subscribers}"),
305            Point::Deliver { event, bus } => write!(f, "deliver {event} from the {bus} bus"),
306            Point::Push { reducer } => write!(f, "push into {reducer}"),
307            Point::Navigate { root, to } => write!(f, "{root} navigates to {to}"),
308            Point::Tick { name: Some(name), .. } => write!(f, "timer {name}"),
309            Point::Tick { timer, .. } => write!(f, "timer #{timer}"),
310            Point::Render { segment, took_us } => {
311                write!(f, "{segment} drew itself in {:.1} ms", *took_us as f64 / 1000.0)
312            }
313            Point::Store {
314                op,
315                path,
316                field,
317                outside,
318            } => {
319                let who = if *outside { "disk" } else { "store" };
320                let verb = match op {
321                    StoreOp::Set => "sets",
322                    StoreOp::Delete => "deletes",
323                    StoreOp::DeletePrefix => "clears",
324                };
325                write!(f, "{who} {verb} {path}")?;
326                match field {
327                    Some(field) => write!(f, " ({field})"),
328                    None => Ok(()),
329                }
330            }
331            Point::Log {
332                level,
333                target,
334                text,
335                ..
336            } => write!(f, "{level} {target}: {text}"),
337            Point::Span { name, fields, .. } if fields.is_empty() => f.write_str(name),
338            Point::Span { name, fields, .. } => write!(f, "{name} {fields}"),
339            Point::Note(text) => f.write_str(text),
340        }
341    }
342}