clawless_core/event/process/run_id.rs
1//! The identity of one run of an external program
2
3use std::fmt::{self, Display};
4use std::sync::atomic::{AtomicU64, Ordering};
5
6/// The source of the identities that [`RunId::next`] hands out
7///
8/// The counter is a single static, so every identity is unique within the
9/// process regardless of which [`Process`] handed it out. A counter per handle
10/// would repeat itself as soon as a command cloned its context.
11///
12/// [`Process`]: crate::process::Process
13static NEXT_RUN_ID: AtomicU64 = AtomicU64::new(0);
14
15/// The identity of one run of an external program
16///
17/// A command can run several programs at the same time, and the lines of those
18/// programs then reach the presenter interleaved. Every [`ProcessEvent`]
19/// carries this value so that a consumer can put a line back with the run that
20/// produced it, group the output of one program, and know when that program
21/// ended.
22///
23/// The identity is Clawless's own, and it counts runs. It is not the
24/// [`ProcessId`] that the operating system assigns, which names the program
25/// while it runs and which the operating system reuses afterwards. A run has an
26/// identity before the program starts and keeps it after the program ended,
27/// which is what an event stream needs.
28///
29/// [`ProcessId`]: crate::process::ProcessId
30///
31/// # Examples
32///
33/// ```
34/// use clawless_core::event::process::RunId;
35///
36/// let first = RunId::next();
37/// let second = RunId::next();
38///
39/// assert_ne!(first, second);
40/// ```
41///
42/// [`ProcessEvent`]: super::ProcessEvent
43// r[impl process.event.correlation]
44#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
45pub struct RunId(u64);
46
47impl RunId {
48 /// Returns an identity that no earlier call returned
49 ///
50 /// [`Process::run`] calls this once per run, so a command that runs a
51 /// program does not need it. A test that stands in for a run builds its
52 /// events with identities from here.
53 ///
54 /// The counter wraps after `2^64` runs. An application that started a
55 /// program every nanosecond would reach that point after more than five
56 /// hundred years, so the wrap is not a case that callers handle.
57 ///
58 /// # Examples
59 ///
60 /// ```
61 /// use clawless_core::event::process::RunId;
62 ///
63 /// let id = RunId::next();
64 ///
65 /// assert_eq!(id, id);
66 /// ```
67 ///
68 /// [`Process::run`]: crate::process::Process::run
69 // r[impl process.event.correlation.unique]
70 pub fn next() -> Self {
71 Self(NEXT_RUN_ID.fetch_add(1, Ordering::Relaxed))
72 }
73
74 /// Returns the identity as a number
75 ///
76 /// A consumer that keys a map or a widget by the run reads the number here.
77 ///
78 /// # Examples
79 ///
80 /// ```
81 /// use clawless_core::event::process::RunId;
82 ///
83 /// let id = RunId::next();
84 ///
85 /// assert_eq!(id.get(), id.get());
86 /// ```
87 pub fn get(&self) -> u64 {
88 self.0
89 }
90}
91
92/// Shows the identity for a reader
93///
94/// The text is the number alone, so that a log line can name the run without
95/// the punctuation that [`Debug`] adds.
96impl Display for RunId {
97 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
98 Display::fmt(&self.0, formatter)
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 // An assertion in a test panics by design. A `# Panics` section on every test
105 // would repeat that and give the reader no information.
106 #![allow(clippy::missing_panics_doc)]
107
108 use super::*;
109
110 #[test]
111 fn display_returns_the_number() {
112 let id = RunId::next();
113
114 let text = id.to_string();
115
116 assert_eq!(text, id.get().to_string());
117 }
118
119 // r[verify process.event.correlation]
120 // r[verify process.event.correlation.unique]
121 #[test]
122 fn next_returns_a_new_identity_every_time() {
123 let first = RunId::next();
124 let second = RunId::next();
125
126 assert_ne!(first, second);
127 }
128
129 #[test]
130 fn trait_send() {
131 fn assert_send<T: Send>() {}
132 assert_send::<RunId>();
133 }
134
135 #[test]
136 fn trait_sync() {
137 fn assert_sync<T: Sync>() {}
138 assert_sync::<RunId>();
139 }
140
141 #[test]
142 fn trait_unpin() {
143 fn assert_unpin<T: Unpin>() {}
144 assert_unpin::<RunId>();
145 }
146}