veecle_telemetry/collector/
builder.rs1use super::global::SetGlobalError;
2use super::{Collector, Export, ProcessId};
3
4use veecle_osal_api::thread::ThreadAbstraction;
5use veecle_osal_api::time::{Instant, SystemTime, SystemTimeError, TimeAbstraction};
6
7fn timestamp_fn_monotonic<T>() -> u64
10where
11 T: TimeAbstraction,
12{
13 let timestamp_micros: u64 = T::now()
14 .duration_since(Instant::MIN)
15 .expect("now should be later than MIN")
16 .as_micros();
17
18 timestamp_micros * 1000
19}
20
21fn timestamp_fn_system_time<T>() -> u64
25where
26 T: TimeAbstraction + SystemTime,
27{
28 match T::duration_since_epoch() {
29 Ok(duration) => duration.as_micros() * 1000,
30 Err(SystemTimeError::Unsynchronized) => {
31 timestamp_fn_monotonic::<T>()
33 }
34 Err(SystemTimeError::EpochIsLaterThanStartTime) => {
35 panic!(
36 "Failed to get duration since epoch: {:?}",
37 SystemTimeError::EpochIsLaterThanStartTime
38 );
39 }
40 }
41}
42
43mod state {
45 #[derive(Debug)]
46 pub struct NoProcessId;
47 #[derive(Debug)]
48 pub struct WithProcessId;
49 #[derive(Debug)]
50 pub struct NoExporter;
51 #[derive(Debug)]
52 pub struct WithExporter;
53 #[derive(Debug)]
54 pub struct NoTime;
55 #[derive(Debug)]
56 pub struct WithTime;
57 #[derive(Debug)]
58 pub struct NoThread;
59 #[derive(Debug)]
60 pub struct WithThread;
61}
62
63#[derive(Debug)]
68#[must_use]
69pub struct Builder<PID, EXP, TIME, THREAD> {
70 process_id: Option<ProcessId>,
71 exporter: Option<&'static (dyn Export + Sync)>,
72 timestamp_fn: Option<fn() -> u64>,
73 thread_id_fn: Option<fn() -> core::num::NonZeroU64>,
74 _pid: core::marker::PhantomData<PID>,
75 _exp: core::marker::PhantomData<EXP>,
76 _time: core::marker::PhantomData<TIME>,
77 _thread: core::marker::PhantomData<THREAD>,
78}
79
80pub fn build() -> Builder<state::NoProcessId, state::NoExporter, state::NoTime, state::NoThread> {
96 Builder {
97 process_id: None,
98 exporter: None,
99 timestamp_fn: None,
100 thread_id_fn: None,
101 _pid: core::marker::PhantomData,
102 _exp: core::marker::PhantomData,
103 _time: core::marker::PhantomData,
104 _thread: core::marker::PhantomData,
105 }
106}
107
108impl<PID, EXP, TIME, THREAD> Builder<PID, EXP, TIME, THREAD> {
109 pub fn process_id(
111 self,
112 process_id: ProcessId,
113 ) -> Builder<state::WithProcessId, EXP, TIME, THREAD> {
114 Builder {
115 process_id: Some(process_id),
116 exporter: self.exporter,
117 timestamp_fn: self.timestamp_fn,
118 thread_id_fn: self.thread_id_fn,
119 _pid: core::marker::PhantomData,
120 _exp: core::marker::PhantomData,
121 _time: core::marker::PhantomData,
122 _thread: core::marker::PhantomData,
123 }
124 }
125
126 pub fn exporter(
128 self,
129 exporter: &'static (dyn Export + Sync),
130 ) -> Builder<PID, state::WithExporter, TIME, THREAD> {
131 Builder {
132 process_id: self.process_id,
133 exporter: Some(exporter),
134 timestamp_fn: self.timestamp_fn,
135 thread_id_fn: self.thread_id_fn,
136 _pid: core::marker::PhantomData,
137 _exp: core::marker::PhantomData,
138 _time: core::marker::PhantomData,
139 _thread: core::marker::PhantomData,
140 }
141 }
142
143 pub fn time<T>(self) -> Builder<PID, EXP, state::WithTime, THREAD>
145 where
146 T: TimeAbstraction,
147 {
148 Builder {
149 process_id: self.process_id,
150 exporter: self.exporter,
151 timestamp_fn: Some(timestamp_fn_monotonic::<T>),
152 thread_id_fn: self.thread_id_fn,
153 _pid: core::marker::PhantomData,
154 _exp: core::marker::PhantomData,
155 _time: core::marker::PhantomData,
156 _thread: core::marker::PhantomData,
157 }
158 }
159
160 pub fn system_time<T>(self) -> Builder<PID, EXP, state::WithTime, THREAD>
162 where
163 T: TimeAbstraction + SystemTime,
164 {
165 Builder {
166 process_id: self.process_id,
167 exporter: self.exporter,
168 timestamp_fn: Some(timestamp_fn_system_time::<T>),
169 thread_id_fn: self.thread_id_fn,
170 _pid: core::marker::PhantomData,
171 _exp: core::marker::PhantomData,
172 _time: core::marker::PhantomData,
173 _thread: core::marker::PhantomData,
174 }
175 }
176
177 pub fn thread<Th>(self) -> Builder<PID, EXP, TIME, state::WithThread>
179 where
180 Th: ThreadAbstraction,
181 {
182 Builder {
183 process_id: self.process_id,
184 exporter: self.exporter,
185 timestamp_fn: self.timestamp_fn,
186 thread_id_fn: Some(Th::current_thread_id),
187 _pid: core::marker::PhantomData,
188 _exp: core::marker::PhantomData,
189 _time: core::marker::PhantomData,
190 _thread: core::marker::PhantomData,
191 }
192 }
193}
194
195impl<EXP, TIME, THREAD> Builder<state::NoProcessId, EXP, TIME, THREAD> {
196 #[cfg(feature = "std")]
215 pub fn random_process_id(self) -> Builder<state::WithProcessId, EXP, TIME, THREAD> {
216 self.process_id(ProcessId::random(&mut rand::rng()))
217 }
218}
219
220impl<PID, TIME, THREAD> Builder<PID, state::NoExporter, TIME, THREAD> {
221 #[cfg(feature = "alloc")]
241 pub fn leaked_exporter(
242 self,
243 exporter: impl Export + Sync + 'static,
244 ) -> Builder<PID, state::WithExporter, TIME, THREAD> {
245 self.exporter(alloc::boxed::Box::leak(alloc::boxed::Box::new(exporter)))
246 }
247
248 #[cfg(feature = "std")]
266 pub fn console_json_exporter(self) -> Builder<PID, state::WithExporter, TIME, THREAD> {
267 self.exporter(&super::ConsoleJsonExporter::DEFAULT)
268 }
269}
270
271impl Builder<state::WithProcessId, state::WithExporter, state::WithTime, state::WithThread> {
272 pub fn build(self) -> Collector {
274 Collector::new(
275 self.process_id.unwrap(),
276 self.exporter.unwrap(),
277 self.timestamp_fn.unwrap(),
278 self.thread_id_fn.unwrap(),
279 )
280 }
281
282 pub fn set_global(self) -> Result<(), SetGlobalError> {
286 super::global::set_collector(self.build())
287 }
288}