1#![warn(clippy::nursery)]
4#![warn(clippy::pedantic)]
5#![warn(clippy::cargo)]
6#![warn(missing_docs)]
7#![allow(clippy::needless_doctest_main)]
8
9use std::sync::{
10 atomic::{AtomicUsize, Ordering},
11 Arc,
12};
13
14use hyper::Request;
15use tower_http::request_id::{MakeRequestId, RequestId};
16
17#[cfg(feature = "uuid")]
18use uuid::Uuid;
19
20#[cfg(feature = "ulid")]
21use ulid::Ulid;
22
23#[derive(Clone, Copy, Debug, Default)]
29#[cfg(feature = "uuid")]
30pub struct MakeRequestUuid;
31
32#[cfg(feature = "uuid")]
33impl MakeRequestId for MakeRequestUuid {
34 fn make_request_id<B>(
35 &mut self,
36 #[allow(unused_variables)] request: &Request<B>,
37 ) -> Option<RequestId> {
38 Some(RequestId::new(
39 Uuid::new_v4()
40 .to_string()
41 .parse()
42 .expect("uuid should only contain ascii characters"),
43 ))
44 }
45}
46
47#[derive(Clone, Copy, Debug, Default)]
53#[cfg(feature = "ulid")]
54pub struct MakeRequestUlid;
55
56#[cfg(feature = "ulid")]
57impl MakeRequestId for MakeRequestUlid {
58 fn make_request_id<B>(
59 &mut self,
60 #[allow(unused_variables)] request: &Request<B>,
61 ) -> Option<RequestId> {
62 Some(RequestId::new(
63 Ulid::new()
64 .to_string()
65 .parse()
66 .expect("ulid should only contain ascii characters"),
67 ))
68 }
69}
70
71#[derive(Clone, Debug, Default)]
76pub struct MakeRequestIdCounter {
77 counter: Arc<AtomicUsize>,
78}
79
80impl MakeRequestIdCounter {
81 #[must_use]
83 pub fn new() -> Self {
84 Self::default()
85 }
86}
87
88impl MakeRequestId for MakeRequestIdCounter {
89 fn make_request_id<B>(
90 &mut self,
91 #[allow(unused_variables)] request: &Request<B>,
92 ) -> Option<RequestId> {
93 Some(RequestId::new(
94 self.counter
95 .fetch_add(1, Ordering::Relaxed)
96 .to_string()
97 .parse()
98 .expect("usize should only contain ascii characters"),
99 ))
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use hyper::Request;
106 use tower_http::request_id::MakeRequestId;
107
108 use super::*;
109
110 #[test]
111 fn make_request_id_counter() {
112 let mut make_request_id = MakeRequestIdCounter::new();
113
114 let next = make_request_id
115 .make_request_id(&Request::new(()))
116 .unwrap()
117 .into_header_value();
118
119 assert_eq!("0", next.to_str().unwrap());
120
121 let next = make_request_id
122 .make_request_id(&Request::new(()))
123 .unwrap()
124 .into_header_value();
125
126 assert_eq!("1", next.to_str().unwrap());
127 }
128
129 #[test]
130 #[cfg(feature = "uuid")]
131 fn make_request_uuid() {
132 let mut make_request_id = MakeRequestUuid;
133
134 let next = make_request_id
135 .make_request_id(&Request::new(()))
136 .unwrap()
137 .into_header_value();
138
139 assert!(uuid::Uuid::try_parse_ascii(next.as_bytes()).is_ok());
140 }
141
142 #[test]
143 #[cfg(feature = "ulid")]
144 fn make_request_ulid() {
145 let mut make_request_id = MakeRequestUlid;
146
147 let next = make_request_id
148 .make_request_id(&Request::new(()))
149 .unwrap()
150 .into_header_value();
151
152 assert!(ulid::Ulid::from_string(next.to_str().unwrap()).is_ok());
153 }
154}