tracing_forest/layer/id.rs
1use crate::fail;
2use crate::layer::OpenedSpan;
3use tracing::Subscriber;
4use tracing_subscriber::{registry::LookupSpan, Registry};
5use uuid::Uuid;
6
7/// Gets the current [`Uuid`] of an entered span within a `tracing-forest`
8/// subscriber.
9///
10/// # Examples
11///
12/// Passing in a `Uuid` to a span, and then retreiving it from within the span:
13/// ```
14/// # use tracing::{info, info_span};
15/// # use uuid::Uuid;
16/// # tracing_forest::init();
17/// let uuid = Uuid::new_v4();
18///
19/// // Tracing's syntax allows us to omit the redundent naming of the field here
20/// info_span!("my_span", %uuid).in_scope(|| {
21/// assert!(tracing_forest::id() == uuid);
22/// });
23/// ```
24///
25/// # Panics
26///
27/// This function panics if there is no current subscriber, if the subscriber
28/// isn't composed with a [`ForestLayer`], or if the subscriber isn't in a span.
29///
30/// [`ForestLayer`]: crate::layer::ForestLayer
31#[must_use]
32pub fn id() -> Uuid {
33 tracing::dispatcher::get_default(|dispatch| {
34 let subscriber = dispatch
35 .downcast_ref::<Registry>()
36 .unwrap_or_else(fail::subscriber_not_found);
37
38 let current = subscriber.current_span();
39
40 let id = current.id().expect(fail::NO_CURRENT_SPAN);
41
42 subscriber
43 .span(id)
44 .expect(fail::SPAN_NOT_IN_CONTEXT)
45 .extensions()
46 .get::<OpenedSpan>()
47 .expect(fail::NO_FOREST_LAYER)
48 .uuid()
49 })
50}
51
52// Credit: https://github.com/uuid-rs/uuid/blob/main/src/parser.rs
53
54pub(crate) const fn try_parse(input: &[u8]) -> Option<Uuid> {
55 match (input.len(), input) {
56 // Inputs of 32 bytes must be a non-hyphenated UUID
57 (32, s) => parse_simple(s),
58 // Hyphenated UUIDs may be wrapped in various ways:
59 // - `{UUID}` for braced UUIDs
60 // - `urn:uuid:UUID` for URNs
61 // - `UUID` for a regular hyphenated UUID
62 (36, s)
63 | (38, [b'{', s @ .., b'}'])
64 | (45, [b'u', b'r', b'n', b':', b'u', b'u', b'i', b'd', b':', s @ ..]) => {
65 parse_hyphenated(s)
66 }
67 // Any other shaped input is immediately invalid
68 _ => None,
69 }
70}
71
72#[inline]
73const fn parse_simple(s: &[u8]) -> Option<Uuid> {
74 // This length check here removes all other bounds
75 // checks in this function
76 if s.len() != 32 {
77 return None;
78 }
79
80 let mut buf: [u8; 16] = [0; 16];
81 let mut i = 0;
82
83 while i < 16 {
84 // Convert a two-char hex value (like `A8`)
85 // into a byte (like `10101000`)
86 let h1 = HEX_TABLE[s[i * 2] as usize];
87 let h2 = HEX_TABLE[s[i * 2 + 1] as usize];
88
89 // We use `0xff` as a sentinel value to indicate
90 // an invalid hex character sequence (like the letter `G`)
91 if h1 | h2 == 0xff {
92 return None;
93 }
94
95 // The upper nibble needs to be shifted into position
96 // to produce the final byte value
97 buf[i] = SHL4_TABLE[h1 as usize] | h2;
98 i += 1;
99 }
100
101 Some(Uuid::from_bytes(buf))
102}
103
104#[inline]
105const fn parse_hyphenated(s: &[u8]) -> Option<Uuid> {
106 // This length check here removes all other bounds
107 // checks in this function
108 if s.len() != 36 {
109 return None;
110 }
111
112 // We look at two hex-encoded values (4 chars) at a time because
113 // that's the size of the smallest group in a hyphenated UUID.
114 // The indexes we're interested in are:
115 //
116 // uuid : 936da01f-9abd-4d9d-80c7-02af85c822a8
117 // | | || || || || | |
118 // hyphens : | | 8| 13| 18| 23| | |
119 // positions: 0 4 9 14 19 24 28 32
120
121 // First, ensure the hyphens appear in the right places
122 match [s[8], s[13], s[18], s[23]] {
123 [b'-', b'-', b'-', b'-'] => {}
124 _ => return None,
125 }
126
127 let positions: [u8; 8] = [0, 4, 9, 14, 19, 24, 28, 32];
128 let mut buf: [u8; 16] = [0; 16];
129 let mut j = 0;
130
131 while j < 8 {
132 let i = positions[j];
133
134 // The decoding here is the same as the simple case
135 // We're just dealing with two values instead of one
136 let h1 = HEX_TABLE[s[i as usize] as usize];
137 let h2 = HEX_TABLE[s[(i + 1) as usize] as usize];
138 let h3 = HEX_TABLE[s[(i + 2) as usize] as usize];
139 let h4 = HEX_TABLE[s[(i + 3) as usize] as usize];
140
141 if h1 | h2 | h3 | h4 == 0xff {
142 return None;
143 }
144
145 buf[j * 2] = SHL4_TABLE[h1 as usize] | h2;
146 buf[j * 2 + 1] = SHL4_TABLE[h3 as usize] | h4;
147 j += 1;
148 }
149
150 Some(Uuid::from_bytes(buf))
151}
152
153const HEX_TABLE: &[u8; 256] = &{
154 let mut buf = [0; 256];
155 let mut i: u8 = 0;
156
157 loop {
158 buf[i as usize] = match i {
159 b'0'..=b'9' => i - b'0',
160 b'a'..=b'f' => i - b'a' + 10,
161 b'A'..=b'F' => i - b'A' + 10,
162 _ => 0xff,
163 };
164
165 if i == 255 {
166 break buf;
167 }
168
169 i += 1;
170 }
171};
172
173const SHL4_TABLE: &[u8; 256] = &{
174 let mut buf = [0; 256];
175 let mut i: u8 = 0;
176
177 loop {
178 buf[i as usize] = i.wrapping_shl(4);
179
180 if i == 255 {
181 break buf;
182 }
183
184 i += 1;
185 }
186};