1use core::fmt;
17use core::str::FromStr;
18
19#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
28pub struct ProcessId(u128);
29
30impl ProcessId {
31 pub fn random(rng: &mut impl rand::Rng) -> Self {
33 Self(rng.random())
34 }
35
36 pub const fn from_raw(raw: u128) -> Self {
42 Self(raw)
43 }
44
45 pub fn to_raw(self) -> u128 {
47 self.0
48 }
49}
50
51impl fmt::Display for ProcessId {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 write!(f, "{:032x}", self.0)
54 }
55}
56
57impl FromStr for ProcessId {
58 type Err = core::num::ParseIntError;
59
60 fn from_str(s: &str) -> Result<Self, Self::Err> {
61 u128::from_str_radix(s, 16).map(ProcessId)
62 }
63}
64
65impl serde::Serialize for ProcessId {
66 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
67 where
68 S: serde::Serializer,
69 {
70 let mut hex_bytes = [0u8; size_of::<u128>() * 2];
71 hex::encode_to_slice(self.0.to_le_bytes(), &mut hex_bytes).unwrap();
72
73 serializer.serialize_str(str::from_utf8(&hex_bytes).unwrap())
74 }
75}
76
77impl<'de> serde::Deserialize<'de> for ProcessId {
78 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
79 where
80 D: serde::Deserializer<'de>,
81 {
82 let bytes: [u8; size_of::<u128>()] = hex::serde::deserialize(deserializer)?;
83
84 Ok(ProcessId(u128::from_le_bytes(bytes)))
85 }
86}
87
88#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
90pub struct SpanId(pub u64);
91
92#[cfg(feature = "enable")]
93impl SpanId {
94 #[inline]
95 #[doc(hidden)]
96 pub fn next_id() -> Self {
98 use core::sync::atomic;
99 static COUNTER: atomic::AtomicU64 = atomic::AtomicU64::new(1);
100 SpanId(COUNTER.fetch_add(1, atomic::Ordering::Relaxed))
101 }
102}
103
104impl fmt::Display for SpanId {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 write!(f, "{:016x}", self.0)
107 }
108}
109
110impl FromStr for SpanId {
111 type Err = core::num::ParseIntError;
112
113 fn from_str(s: &str) -> Result<Self, Self::Err> {
114 u64::from_str_radix(s, 16).map(SpanId)
115 }
116}
117
118impl serde::Serialize for SpanId {
119 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
120 where
121 S: serde::Serializer,
122 {
123 let mut hex_bytes = [0u8; size_of::<u64>() * 2];
124 hex::encode_to_slice(self.0.to_le_bytes(), &mut hex_bytes).unwrap();
125
126 serializer.serialize_str(str::from_utf8(&hex_bytes).unwrap())
127 }
128}
129
130impl<'de> serde::Deserialize<'de> for SpanId {
131 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
132 where
133 D: serde::Deserializer<'de>,
134 {
135 let bytes: [u8; size_of::<u64>()] = hex::serde::deserialize(deserializer)?;
136
137 Ok(SpanId(u64::from_le_bytes(bytes)))
138 }
139}
140
141#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
143pub struct SpanContext {
144 pub process_id: ProcessId,
146 pub span_id: SpanId,
148}
149
150impl SpanContext {
151 pub fn new(process_id: ProcessId, span_id: SpanId) -> Self {
161 Self {
162 process_id,
163 span_id,
164 }
165 }
166}
167
168impl fmt::Display for SpanContext {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 let Self {
171 process_id,
172 span_id,
173 } = self;
174 write!(f, "{process_id}:{span_id}")
175 }
176}
177
178#[derive(Clone, Debug)]
180pub enum ParseSpanContextError {
181 MissingSeparator,
183
184 InvalidProcessId(core::num::ParseIntError),
186
187 InvalidSpanId(core::num::ParseIntError),
189}
190
191impl fmt::Display for ParseSpanContextError {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 match self {
194 Self::MissingSeparator => f.write_str("missing ':' separator"),
195 Self::InvalidProcessId(_) => f.write_str("failed to parse process id"),
196 Self::InvalidSpanId(_) => f.write_str("failed to parse span id"),
197 }
198 }
199}
200
201impl core::error::Error for ParseSpanContextError {
202 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
203 match self {
204 Self::MissingSeparator => None,
205 Self::InvalidProcessId(error) => Some(error),
206 Self::InvalidSpanId(error) => Some(error),
207 }
208 }
209}
210
211impl FromStr for SpanContext {
212 type Err = ParseSpanContextError;
213
214 fn from_str(s: &str) -> Result<Self, Self::Err> {
215 let Some((process_id, span_id)) = s.split_once(":") else {
216 return Err(ParseSpanContextError::MissingSeparator);
217 };
218 let process_id =
219 ProcessId::from_str(process_id).map_err(ParseSpanContextError::InvalidProcessId)?;
220 let span_id = SpanId::from_str(span_id).map_err(ParseSpanContextError::InvalidSpanId)?;
221 Ok(Self {
222 process_id,
223 span_id,
224 })
225 }
226}
227
228impl serde::Serialize for SpanContext {
229 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
230 where
231 S: serde::Serializer,
232 {
233 let mut bytes = [0u8; 49];
234
235 hex::encode_to_slice(self.process_id.to_raw().to_le_bytes(), &mut bytes[..32]).unwrap();
236 bytes[32] = b':';
237 hex::encode_to_slice(self.span_id.0.to_le_bytes(), &mut bytes[33..]).unwrap();
238
239 serializer.serialize_str(str::from_utf8(&bytes).unwrap())
240 }
241}
242
243impl<'de> serde::Deserialize<'de> for SpanContext {
244 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
245 where
246 D: serde::Deserializer<'de>,
247 {
248 use serde::de::Error;
249
250 let string = <&str>::deserialize(deserializer)?;
251
252 if string.len() != 49 {
253 return Err(D::Error::invalid_length(
254 string.len(),
255 &"expected 49 byte string",
256 ));
257 }
258
259 let bytes = string.as_bytes();
260
261 if bytes[32] != b':' {
262 return Err(D::Error::invalid_value(
263 serde::de::Unexpected::Str(string),
264 &"expected : separator at byte 32",
265 ));
266 }
267
268 let mut process = [0; 16];
269 hex::decode_to_slice(&bytes[..32], &mut process).map_err(D::Error::custom)?;
270
271 let mut span = [0; 8];
272 hex::decode_to_slice(&bytes[33..], &mut span).map_err(D::Error::custom)?;
273
274 Ok(Self {
275 process_id: ProcessId::from_raw(u128::from_le_bytes(process)),
276 span_id: SpanId(u64::from_le_bytes(span)),
277 })
278 }
279}
280
281#[cfg(all(test, feature = "std"))]
282mod tests {
283 use std::collections::HashSet;
284 use std::format;
285 use std::string::String;
286 use std::vec::Vec;
287
288 use super::*;
289
290 #[test]
291 #[cfg(not(miri))] #[allow(clippy::needless_collect)]
293 fn unique_id() {
294 let handles = std::iter::repeat_with(|| {
295 std::thread::spawn(|| {
296 std::iter::repeat_with(SpanId::next_id)
297 .take(1000)
298 .collect::<Vec<_>>()
299 })
300 })
301 .take(32)
302 .collect::<Vec<_>>();
303
304 let k = handles
305 .into_iter()
306 .flat_map(|h| h.join().unwrap())
307 .collect::<HashSet<_>>();
308
309 assert_eq!(k.len(), 32 * 1000);
310 }
311
312 #[test]
313 fn span_id_formatting() {
314 assert_eq!(format!("{}", SpanId(0)), "0000000000000000");
315 assert_eq!(format!("{}", SpanId(u64::MAX)), "ffffffffffffffff");
316 assert_eq!(
317 format!("{}", SpanId(0xFEDCBA9876543210)),
318 "fedcba9876543210"
319 );
320 assert_eq!(format!("{}", SpanId(0x123)), "0000000000000123");
321 }
322
323 #[test]
324 fn span_id_from_str() {
325 assert_eq!(
326 "fedcba9876543210".parse::<SpanId>().unwrap(),
327 SpanId(0xFEDCBA9876543210)
328 );
329 assert_eq!(
330 "FEDCBA9876543210".parse::<SpanId>().unwrap(),
331 SpanId(0xFEDCBA9876543210)
332 );
333 assert_eq!("0000000000000000".parse::<SpanId>().unwrap(), SpanId(0));
334 assert_eq!(
335 "ffffffffffffffff".parse::<SpanId>().unwrap(),
336 SpanId(u64::MAX)
337 );
338 assert_eq!("123".parse::<SpanId>().unwrap(), SpanId(0x123));
339
340 assert!("xyz".parse::<SpanId>().is_err());
341 assert!("".parse::<SpanId>().is_err());
342 }
343
344 #[test]
345 fn span_id_format_from_str_roundtrip() {
346 let test_cases = [0u64, 1, 0x123, 0xFEDCBA9876543210, u64::MAX, u64::MAX - 1];
347
348 for value in test_cases {
349 let span_id = SpanId(value);
350 let formatted = format!("{span_id}");
351 let parsed = formatted.parse::<SpanId>().unwrap();
352 assert_eq!(span_id, parsed, "Failed roundtrip for value {value:#x}");
353 }
354 }
355
356 #[test]
357 fn span_id_serde_roundtrip() {
358 let test_cases = [
359 SpanId(0),
360 SpanId(1),
361 SpanId(0x123),
362 SpanId(0xFEDCBA9876543210),
363 SpanId(u64::MAX),
364 SpanId(u64::MAX - 1),
365 ];
366
367 for original in test_cases {
368 let json = serde_json::to_string(&original).unwrap();
369 let deserialized: SpanId = serde_json::from_str(&json).unwrap();
370 assert_eq!(
371 original, deserialized,
372 "JSON roundtrip failed for {:#x}",
373 original.0
374 );
375 }
376 }
377
378 #[test]
379 fn span_context_serde_roundtrip() {
380 let test_cases = [
381 SpanContext::new(ProcessId::from_raw(0), SpanId(0)),
382 SpanContext::new(
383 ProcessId::from_raw(0x123456789ABCDEF0FEDCBA9876543210),
384 SpanId(0xFEDCBA9876543210),
385 ),
386 SpanContext::new(ProcessId::from_raw(u128::MAX), SpanId(u64::MAX)),
387 SpanContext::new(ProcessId::from_raw(1), SpanId(1)),
388 ];
389
390 for original in test_cases {
391 let json = serde_json::to_string(&original).unwrap();
392 let deserialized: SpanContext = serde_json::from_str(&json).unwrap();
393 assert_eq!(
394 original.process_id, deserialized.process_id,
395 "JSON roundtrip failed for process_id"
396 );
397 assert_eq!(
398 original.span_id, deserialized.span_id,
399 "JSON roundtrip failed for span_id"
400 );
401 }
402 }
403
404 #[test]
405 fn span_id_serialization_format() {
406 let span_id = SpanId(0xFEDCBA9876543210);
407 let json = serde_json::to_string(&span_id).unwrap();
408
409 let expected_le_bytes = 0xFEDCBA9876543210u64.to_le_bytes();
410 let mut expected_hex = String::new();
411 for byte in &expected_le_bytes {
412 expected_hex.push_str(&format!("{byte:02x}"));
413 }
414 let expected_json = format!("\"{expected_hex}\"");
415
416 assert_eq!(json, expected_json);
417 }
418
419 #[test]
420 fn span_context_new_and_fields() {
421 let process_id = ProcessId::from_raw(0x123);
422 let span_id = SpanId(0x456);
423 let context = SpanContext::new(process_id, span_id);
424
425 assert_eq!(context.process_id, process_id);
426 assert_eq!(context.span_id, span_id);
427 }
428
429 #[test]
430 fn process_id_format_from_str_roundtrip() {
431 let test_cases = [
432 0u128,
433 1,
434 0x123,
435 0xFEDCBA9876543210,
436 0x123456789ABCDEF0FEDCBA9876543210,
437 u128::MAX,
438 u128::MAX - 1,
439 ];
440
441 for value in test_cases {
442 let process_id = ProcessId::from_raw(value);
443 let formatted = format!("{process_id}");
444 let parsed = formatted.parse::<ProcessId>().unwrap();
445 assert_eq!(process_id, parsed, "Failed roundtrip for value {value:#x}");
446 }
447 }
448
449 #[test]
450 fn process_id_serde_roundtrip() {
451 let test_cases = [
452 ProcessId::from_raw(0),
453 ProcessId::from_raw(1),
454 ProcessId::from_raw(0x123),
455 ProcessId::from_raw(0xFEDCBA9876543210),
456 ProcessId::from_raw(0x123456789ABCDEF0FEDCBA9876543210),
457 ProcessId::from_raw(u128::MAX),
458 ProcessId::from_raw(u128::MAX - 1),
459 ];
460
461 for original in test_cases {
462 let json = serde_json::to_string(&original).unwrap();
463 let deserialized: ProcessId = serde_json::from_str(&json).unwrap();
464 assert_eq!(
465 original,
466 deserialized,
467 "JSON roundtrip failed for {:#x}",
468 original.to_raw()
469 );
470 }
471 }
472
473 #[test]
474 fn span_context_format_from_str_roundtrip() {
475 let test_cases = [
476 SpanContext::new(ProcessId::from_raw(0), SpanId(0)),
477 SpanContext::new(
478 ProcessId::from_raw(0x123456789ABCDEF0FEDCBA9876543210),
479 SpanId(0xFEDCBA9876543210),
480 ),
481 SpanContext::new(ProcessId::from_raw(u128::MAX), SpanId(u64::MAX)),
482 SpanContext::new(ProcessId::from_raw(1), SpanId(1)),
483 ];
484
485 for context in test_cases {
486 let formatted = format!("{context}");
487 let parsed = formatted.parse::<SpanContext>().unwrap();
488 assert_eq!(
489 context,
490 parsed,
491 "Failed roundtrip for {:#x}:{:#x}",
492 context.process_id.to_raw(),
493 context.span_id.0
494 );
495 }
496 }
497
498 #[test]
499 fn span_id_next_id_produces_non_zero_values() {
500 let ids: Vec<SpanId> = (0..100).map(|_| SpanId::next_id()).collect();
501
502 for id in &ids {
503 assert_ne!(id.0, 0, "SpanId::next_id() should not produce zero values");
504 }
505
506 let mut unique_ids = HashSet::new();
507 for id in &ids {
508 assert!(
509 unique_ids.insert(id.0),
510 "SpanId::next_id() should produce unique values"
511 );
512 }
513 }
514}