telemetry_safe_core/
lib.rs1use std::fmt::{self, Debug, Display, Formatter};
4
5pub trait ToTelemetry {
7 fn fmt_telemetry(&self, f: &mut Formatter<'_>) -> fmt::Result;
12}
13
14#[must_use]
16pub struct TelemetryDisplay<'a, T: ?Sized>(&'a T);
17
18impl<'a, T: ToTelemetry + ?Sized> TelemetryDisplay<'a, T> {
19 pub fn new(value: &'a T) -> Self {
21 Self(value)
22 }
23}
24
25impl<T: ToTelemetry + ?Sized> Display for TelemetryDisplay<'_, T> {
26 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
27 self.0.fmt_telemetry(f)
28 }
29}
30
31#[must_use]
33pub struct TelemetryDebug<'a, T: ?Sized>(&'a T);
34
35impl<'a, T: ToTelemetry + ?Sized> TelemetryDebug<'a, T> {
36 pub fn new(value: &'a T) -> Self {
41 Self(value)
42 }
43}
44
45impl<T: ToTelemetry + ?Sized> Debug for TelemetryDebug<'_, T> {
46 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
47 self.0.fmt_telemetry(f)
48 }
49}
50
51pub fn telemetry<T: ToTelemetry + ?Sized>(value: &T) -> TelemetryDisplay<'_, T> {
53 TelemetryDisplay::new(value)
54}
55
56pub fn telemetry_debug<T: ToTelemetry + ?Sized>(value: &T) -> TelemetryDebug<'_, T> {
58 TelemetryDebug::new(value)
59}
60
61macro_rules! impl_to_telemetry_via_display {
62 ($($ty:ty),* $(,)?) => {
63 $(
64 impl ToTelemetry for $ty {
65 fn fmt_telemetry(&self, f: &mut Formatter<'_>) -> fmt::Result {
66 Display::fmt(self, f)
67 }
68 }
69 )*
70 };
71}
72
73impl_to_telemetry_via_display!(
74 bool,
75 char,
76 i8,
77 i16,
78 i32,
79 i64,
80 i128,
81 isize,
82 u8,
83 u16,
84 u32,
85 u64,
86 u128,
87 usize,
88 std::num::NonZeroI8,
89 std::num::NonZeroI16,
90 std::num::NonZeroI32,
91 std::num::NonZeroI64,
92 std::num::NonZeroI128,
93 std::num::NonZeroIsize,
94 std::num::NonZeroU8,
95 std::num::NonZeroU16,
96 std::num::NonZeroU32,
97 std::num::NonZeroU64,
98 std::num::NonZeroU128,
99 std::num::NonZeroUsize,
100 std::net::IpAddr,
101 std::net::Ipv4Addr,
102 std::net::Ipv6Addr,
103 std::net::SocketAddr,
104 std::net::SocketAddrV4,
105 std::net::SocketAddrV6
106);
107
108impl<T: ToTelemetry + ?Sized> ToTelemetry for &T {
109 fn fmt_telemetry(&self, f: &mut Formatter<'_>) -> fmt::Result {
110 (*self).fmt_telemetry(f)
111 }
112}
113
114impl<T: ToTelemetry + ?Sized> ToTelemetry for Box<T> {
115 fn fmt_telemetry(&self, f: &mut Formatter<'_>) -> fmt::Result {
116 self.as_ref().fmt_telemetry(f)
117 }
118}
119
120impl<T: ToTelemetry> ToTelemetry for Option<T> {
121 fn fmt_telemetry(&self, f: &mut Formatter<'_>) -> fmt::Result {
122 match self {
123 Some(value) => f
124 .debug_tuple("Some")
125 .field(&telemetry_debug(value))
126 .finish(),
127 None => f.write_str("None"),
128 }
129 }
130}
131
132impl<T: ToTelemetry, E: ToTelemetry> ToTelemetry for Result<T, E> {
133 fn fmt_telemetry(&self, f: &mut Formatter<'_>) -> fmt::Result {
134 match self {
135 Ok(value) => f.debug_tuple("Ok").field(&telemetry_debug(value)).finish(),
136 Err(err) => f.debug_tuple("Err").field(&telemetry_debug(err)).finish(),
137 }
138 }
139}
140
141impl<T: ToTelemetry> ToTelemetry for [T] {
142 fn fmt_telemetry(&self, f: &mut Formatter<'_>) -> fmt::Result {
143 let mut list = f.debug_list();
146 for item in self {
147 list.entry(&telemetry_debug(item));
148 }
149 list.finish()
150 }
151}
152
153impl<T: ToTelemetry, const N: usize> ToTelemetry for [T; N] {
154 fn fmt_telemetry(&self, f: &mut Formatter<'_>) -> fmt::Result {
155 self.as_slice().fmt_telemetry(f)
156 }
157}
158
159impl<T: ToTelemetry> ToTelemetry for Vec<T> {
160 fn fmt_telemetry(&self, f: &mut Formatter<'_>) -> fmt::Result {
161 self.as_slice().fmt_telemetry(f)
162 }
163}
164
165impl<K: ToTelemetry, V: ToTelemetry> ToTelemetry for std::collections::BTreeMap<K, V> {
166 fn fmt_telemetry(&self, f: &mut Formatter<'_>) -> fmt::Result {
167 let mut map = f.debug_map();
168 for (key, value) in self {
169 map.entry(&telemetry_debug(key), &telemetry_debug(value));
170 }
171 map.finish()
172 }
173}
174
175impl<T: ToTelemetry> ToTelemetry for std::collections::BTreeSet<T> {
176 fn fmt_telemetry(&self, f: &mut Formatter<'_>) -> fmt::Result {
177 let mut set = f.debug_set();
178 for item in self {
179 set.entry(&telemetry_debug(item));
180 }
181 set.finish()
182 }
183}
184
185pub mod prelude {
187 pub use crate::{ToTelemetry, telemetry, telemetry_debug};
188}
189
190#[cfg(test)]
191mod tests {
192 use super::{ToTelemetry, telemetry};
193 use std::fmt::{self, Formatter};
194
195 struct Token(u64);
196
197 impl ToTelemetry for Token {
198 fn fmt_telemetry(&self, f: &mut Formatter<'_>) -> fmt::Result {
199 write!(f, "token-{}", self.0)
200 }
201 }
202
203 #[test]
204 fn primitives_and_manual_types_are_displayable() {
205 assert_eq!(telemetry(&123_u64).to_string(), "123");
206 assert_eq!(telemetry(&Token(7)).to_string(), "token-7");
207 assert_eq!(telemetry(&Some(Token(2))).to_string(), "Some(token-2)");
208 }
209
210 #[test]
211 fn collections_use_safe_rendering_recursively() {
212 let values = vec![Token(1), Token(2)];
213 assert_eq!(telemetry(&values).to_string(), "[token-1, token-2]");
214 }
215}