1616use core:: fmt;
1717use core:: str:: FromStr ;
1818
19- use serde:: { Deserialize , Serialize } ;
20-
2119/// A globally-unique id identifying a process.
2220///
2321/// The primary purpose of this id is to provide a globally-unique context within which
@@ -26,9 +24,7 @@ use serde::{Deserialize, Serialize};
2624/// most embedded setups it should be unique for each time the system is restarted.
2725///
2826/// [`ThreadId`]: crate::protocol::ThreadId
29- #[ derive(
30- Copy , Clone , Debug , Eq , PartialEq , Ord , PartialOrd , Hash , Default , Serialize , Deserialize ,
31- ) ]
27+ #[ derive( Copy , Clone , Debug , Eq , PartialEq , Ord , PartialOrd , Hash , Default ) ]
3228pub struct ProcessId ( u128 ) ;
3329
3430impl ProcessId {
@@ -52,6 +48,43 @@ impl ProcessId {
5248 }
5349}
5450
51+ impl fmt:: Display for ProcessId {
52+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
53+ write ! ( f, "{:032x}" , self . 0 )
54+ }
55+ }
56+
57+ impl 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+
65+ impl 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+
77+ impl < ' 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+
5588/// A process-unique id for a span.
5689#[ derive( Copy , Clone , Debug , Eq , PartialEq , Ord , PartialOrd , Hash ) ]
5790pub struct SpanId ( pub u64 ) ;
@@ -106,7 +139,7 @@ impl<'de> serde::Deserialize<'de> for SpanId {
106139}
107140
108141/// A struct representing the context of a span, including its [`ProcessId`] and [`SpanId`].
109- #[ derive( Copy , Clone , Debug , Eq , PartialEq , Ord , PartialOrd , Hash , Serialize , Deserialize ) ]
142+ #[ derive( Copy , Clone , Debug , Eq , PartialEq , Ord , PartialOrd , Hash ) ]
110143pub struct SpanContext {
111144 /// The id of the process this span belongs to.
112145 pub process_id : ProcessId ,
@@ -160,6 +193,119 @@ impl SpanContext {
160193 }
161194}
162195
196+ impl fmt:: Display for SpanContext {
197+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
198+ let Self {
199+ process_id,
200+ span_id,
201+ } = self ;
202+ write ! ( f, "{process_id}:{span_id}" )
203+ }
204+ }
205+
206+ /// Errors that can occur while parsing [`SpanContext`] from a string.
207+ #[ derive( Clone , Debug ) ]
208+ pub enum ParseSpanContextError {
209+ /// The string is missing a `:` separator.
210+ MissingSeparator ,
211+
212+ /// The embedded [`ProcessId`] failed to parse.
213+ InvalidProcessId ( core:: num:: ParseIntError ) ,
214+
215+ /// The embedded [`SpanId`] failed to parse.
216+ InvalidSpanId ( core:: num:: ParseIntError ) ,
217+ }
218+
219+ impl fmt:: Display for ParseSpanContextError {
220+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
221+ match self {
222+ Self :: MissingSeparator => f. write_str ( "missing ':' separator" ) ,
223+ Self :: InvalidProcessId ( _) => f. write_str ( "failed to parse process id" ) ,
224+ Self :: InvalidSpanId ( _) => f. write_str ( "failed to parse span id" ) ,
225+ }
226+ }
227+ }
228+
229+ impl core:: error:: Error for ParseSpanContextError {
230+ fn source ( & self ) -> Option < & ( dyn core:: error:: Error + ' static ) > {
231+ match self {
232+ Self :: MissingSeparator => None ,
233+ Self :: InvalidProcessId ( error) => Some ( error) ,
234+ Self :: InvalidSpanId ( error) => Some ( error) ,
235+ }
236+ }
237+ }
238+
239+ impl FromStr for SpanContext {
240+ type Err = ParseSpanContextError ;
241+
242+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
243+ let Some ( ( process_id, span_id) ) = s. split_once ( ":" ) else {
244+ return Err ( ParseSpanContextError :: MissingSeparator ) ;
245+ } ;
246+ let process_id =
247+ ProcessId :: from_str ( process_id) . map_err ( ParseSpanContextError :: InvalidProcessId ) ?;
248+ let span_id = SpanId :: from_str ( span_id) . map_err ( ParseSpanContextError :: InvalidSpanId ) ?;
249+ Ok ( Self {
250+ process_id,
251+ span_id,
252+ } )
253+ }
254+ }
255+
256+ impl serde:: Serialize for SpanContext {
257+ fn serialize < S > ( & self , serializer : S ) -> Result < S :: Ok , S :: Error >
258+ where
259+ S : serde:: Serializer ,
260+ {
261+ let mut bytes = [ 0u8 ; 49 ] ;
262+
263+ hex:: encode_to_slice ( self . process_id . to_raw ( ) . to_le_bytes ( ) , & mut bytes[ ..32 ] ) . unwrap ( ) ;
264+ bytes[ 32 ] = b':' ;
265+ hex:: encode_to_slice ( self . span_id . 0 . to_le_bytes ( ) , & mut bytes[ 33 ..] ) . unwrap ( ) ;
266+
267+ serializer. serialize_str ( str:: from_utf8 ( & bytes) . unwrap ( ) )
268+ }
269+ }
270+
271+ impl < ' de > serde:: Deserialize < ' de > for SpanContext {
272+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
273+ where
274+ D : serde:: Deserializer < ' de > ,
275+ {
276+ use serde:: de:: Error ;
277+
278+ let string = <& str >:: deserialize ( deserializer) ?;
279+
280+ if string. len ( ) != 49 {
281+ return Err ( D :: Error :: invalid_length (
282+ string. len ( ) ,
283+ & "expected 49 byte string" ,
284+ ) ) ;
285+ }
286+
287+ let bytes = string. as_bytes ( ) ;
288+
289+ if bytes[ 32 ] != b':' {
290+ return Err ( D :: Error :: invalid_value (
291+ serde:: de:: Unexpected :: Str ( string) ,
292+ & "expected : separator at byte 32" ,
293+ ) ) ;
294+ }
295+
296+ let mut process = [ 0 ; 16 ] ;
297+ hex:: decode_to_slice ( & bytes[ ..32 ] , & mut process) . map_err ( D :: Error :: custom) ?;
298+
299+ let mut span = [ 0 ; 8 ] ;
300+ hex:: decode_to_slice ( & bytes[ 33 ..] , & mut span) . map_err ( D :: Error :: custom) ?;
301+
302+ Ok ( Self {
303+ process_id : ProcessId :: from_raw ( u128:: from_le_bytes ( process) ) ,
304+ span_id : SpanId ( u64:: from_le_bytes ( span) ) ,
305+ } )
306+ }
307+ }
308+
163309#[ cfg( all( test, feature = "std" ) ) ]
164310mod tests {
165311 use std:: collections:: HashSet ;
@@ -308,6 +454,75 @@ mod tests {
308454 assert_eq ! ( context. span_id, span_id) ;
309455 }
310456
457+ #[ test]
458+ fn process_id_format_from_str_roundtrip ( ) {
459+ let test_cases = [
460+ 0u128 ,
461+ 1 ,
462+ 0x123 ,
463+ 0xFEDCBA9876543210 ,
464+ 0x123456789ABCDEF0FEDCBA9876543210 ,
465+ u128:: MAX ,
466+ u128:: MAX - 1 ,
467+ ] ;
468+
469+ for value in test_cases {
470+ let process_id = ProcessId :: from_raw ( value) ;
471+ let formatted = format ! ( "{process_id}" ) ;
472+ let parsed = formatted. parse :: < ProcessId > ( ) . unwrap ( ) ;
473+ assert_eq ! ( process_id, parsed, "Failed roundtrip for value {value:#x}" ) ;
474+ }
475+ }
476+
477+ #[ test]
478+ fn process_id_serde_roundtrip ( ) {
479+ let test_cases = [
480+ ProcessId :: from_raw ( 0 ) ,
481+ ProcessId :: from_raw ( 1 ) ,
482+ ProcessId :: from_raw ( 0x123 ) ,
483+ ProcessId :: from_raw ( 0xFEDCBA9876543210 ) ,
484+ ProcessId :: from_raw ( 0x123456789ABCDEF0FEDCBA9876543210 ) ,
485+ ProcessId :: from_raw ( u128:: MAX ) ,
486+ ProcessId :: from_raw ( u128:: MAX - 1 ) ,
487+ ] ;
488+
489+ for original in test_cases {
490+ let json = serde_json:: to_string ( & original) . unwrap ( ) ;
491+ let deserialized: ProcessId = serde_json:: from_str ( & json) . unwrap ( ) ;
492+ assert_eq ! (
493+ original,
494+ deserialized,
495+ "JSON roundtrip failed for {:#x}" ,
496+ original. to_raw( )
497+ ) ;
498+ }
499+ }
500+
501+ #[ test]
502+ fn span_context_format_from_str_roundtrip ( ) {
503+ let test_cases = [
504+ SpanContext :: new ( ProcessId :: from_raw ( 0 ) , SpanId ( 0 ) ) ,
505+ SpanContext :: new (
506+ ProcessId :: from_raw ( 0x123456789ABCDEF0FEDCBA9876543210 ) ,
507+ SpanId ( 0xFEDCBA9876543210 ) ,
508+ ) ,
509+ SpanContext :: new ( ProcessId :: from_raw ( u128:: MAX ) , SpanId ( u64:: MAX ) ) ,
510+ SpanContext :: new ( ProcessId :: from_raw ( 1 ) , SpanId ( 1 ) ) ,
511+ ] ;
512+
513+ for context in test_cases {
514+ let formatted = format ! ( "{context}" ) ;
515+ let parsed = formatted. parse :: < SpanContext > ( ) . unwrap ( ) ;
516+ assert_eq ! (
517+ context,
518+ parsed,
519+ "Failed roundtrip for {:#x}:{:#x}" ,
520+ context. process_id. to_raw( ) ,
521+ context. span_id. 0
522+ ) ;
523+ }
524+ }
525+
311526 #[ test]
312527 fn span_id_next_id_produces_non_zero_values ( ) {
313528 let ids: Vec < SpanId > = ( 0 ..100 ) . map ( |_| SpanId :: next_id ( ) ) . collect ( ) ;
0 commit comments