tracing_tunnel/
value.rs

1//! `TracedValue` and closely related types.
2
3use serde::{Deserialize, Serialize};
4
5use core::{borrow::Borrow, fmt};
6
7use crate::alloc::{format, String, ToOwned};
8
9#[cfg(feature = "std")]
10mod error {
11    use serde::{Deserialize, Serialize};
12
13    use std::{error, fmt};
14
15    /// (De)serializable presentation for an error recorded as a value in a tracing span or event.
16    #[derive(Debug, Clone, Serialize, Deserialize)]
17    #[non_exhaustive]
18    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
19    pub struct TracedError {
20        /// Error message produced by its [`Display`](fmt::Display) implementation.
21        pub message: String,
22        /// Error [source](error::Error::source()).
23        pub source: Option<Box<TracedError>>,
24    }
25
26    impl TracedError {
27        pub(super) fn new(err: &(dyn error::Error + 'static)) -> Self {
28            Self {
29                message: err.to_string(),
30                source: err.source().map(|source| Box::new(Self::new(source))),
31            }
32        }
33    }
34
35    impl fmt::Display for TracedError {
36        fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
37            formatter.write_str(&self.message)
38        }
39    }
40
41    impl error::Error for TracedError {
42        fn source(&self) -> Option<&(dyn error::Error + 'static)> {
43            self.source
44                .as_ref()
45                .map(|source| source.as_ref() as &(dyn error::Error + 'static))
46        }
47    }
48}
49
50#[cfg(feature = "std")]
51pub use self::error::TracedError;
52
53/// Opaque wrapper for a [`Debug`](fmt::Debug)gable object recorded as a value
54/// in a tracing span or event.
55#[derive(Clone, Serialize, Deserialize)]
56#[serde(transparent)]
57pub struct DebugObject(String);
58
59impl fmt::Debug for DebugObject {
60    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
61        formatter.write_str(&self.0)
62    }
63}
64
65/// Returns the [`Debug`](fmt::Debug) representation of the object.
66impl AsRef<str> for DebugObject {
67    fn as_ref(&self) -> &str {
68        &self.0
69    }
70}
71
72/// Value recorded in a tracing span or event.
73#[derive(Debug, Clone, Serialize, Deserialize)]
74#[serde(rename_all = "snake_case")]
75#[non_exhaustive]
76pub enum TracedValue {
77    /// Boolean value.
78    Bool(bool),
79    /// Signed integer value.
80    Int(i128),
81    /// Unsigned integer value.
82    UInt(u128),
83    /// Floating-point value.
84    Float(f64),
85    /// String value.
86    String(String),
87    /// Opaque object implementing the [`Debug`](fmt::Debug) trait.
88    Object(DebugObject),
89    /// Opaque error.
90    #[cfg(feature = "std")]
91    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
92    Error(TracedError),
93}
94
95impl TracedValue {
96    #[doc(hidden)] // public for testing purposes
97    pub fn debug(object: &dyn fmt::Debug) -> Self {
98        Self::Object(DebugObject(format!("{object:?}")))
99    }
100
101    /// Returns value as a Boolean, or `None` if it's not a Boolean value.
102    #[inline]
103    pub fn as_bool(&self) -> Option<bool> {
104        bool::from_value(self)
105    }
106
107    /// Returns value as a signed integer, or `None` if it's not one.
108    #[inline]
109    pub fn as_int(&self) -> Option<i128> {
110        i128::from_value(self)
111    }
112
113    /// Returns value as an unsigned integer, or `None` if it's not one.
114    #[inline]
115    pub fn as_uint(&self) -> Option<u128> {
116        u128::from_value(self)
117    }
118
119    /// Returns value as a floating-point value, or `None` if it's not one.
120    #[inline]
121    pub fn as_float(&self) -> Option<f64> {
122        f64::from_value(self)
123    }
124
125    /// Returns value as a string, or `None` if it's not one.
126    #[inline]
127    pub fn as_str(&self) -> Option<&str> {
128        str::from_value(self)
129    }
130
131    /// Checks whether this value is a [`DebugObject`] with the same [`Debug`](fmt::Debug)
132    /// output as the provided `object`.
133    pub fn is_debug(&self, object: &dyn fmt::Debug) -> bool {
134        match self {
135            Self::Object(value) => value.0 == format!("{object:?}"),
136            _ => false,
137        }
138    }
139
140    /// Returns value as a [`Debug`](fmt::Debug) string output, or `None` if this value
141    /// is not [`Self::Object`].
142    pub fn as_debug_str(&self) -> Option<&str> {
143        match self {
144            Self::Object(value) => Some(&value.0),
145            _ => None,
146        }
147    }
148
149    #[cfg(feature = "std")]
150    pub(crate) fn error(err: &(dyn std::error::Error + 'static)) -> Self {
151        Self::Error(TracedError::new(err))
152    }
153}
154
155/// Fallible conversion from a [`TracedValue`] reference.
156pub trait FromTracedValue<'a> {
157    /// Output of the conversion.
158    type Output: Borrow<Self> + 'a;
159    /// Performs the conversion.
160    fn from_value(value: &'a TracedValue) -> Option<Self::Output>;
161}
162
163impl<'a> FromTracedValue<'a> for str {
164    type Output = &'a str;
165
166    fn from_value(value: &'a TracedValue) -> Option<Self::Output> {
167        match value {
168            TracedValue::String(value) => Some(value),
169            _ => None,
170        }
171    }
172}
173
174macro_rules! impl_value_conversions {
175    (TracedValue :: $variant:ident ($source:ty)) => {
176        impl From<$source> for TracedValue {
177            fn from(value: $source) -> Self {
178                Self::$variant(value)
179            }
180        }
181
182        impl PartialEq<$source> for TracedValue {
183            fn eq(&self, other: &$source) -> bool {
184                match self {
185                    Self::$variant(value) => value == other,
186                    _ => false,
187                }
188            }
189        }
190
191        impl PartialEq<TracedValue> for $source {
192            fn eq(&self, other: &TracedValue) -> bool {
193                other == self
194            }
195        }
196
197        impl FromTracedValue<'_> for $source {
198            type Output = Self;
199
200            fn from_value(value: &TracedValue) -> Option<Self::Output> {
201                match value {
202                    TracedValue::$variant(value) => Some(*value),
203                    _ => None,
204                }
205            }
206        }
207    };
208
209    (TracedValue :: $variant:ident ($source:ty as $field_ty:ty)) => {
210        impl From<$source> for TracedValue {
211            fn from(value: $source) -> Self {
212                Self::$variant(value.into())
213            }
214        }
215
216        impl PartialEq<$source> for TracedValue {
217            fn eq(&self, other: &$source) -> bool {
218                match self {
219                    Self::$variant(value) => *value == <$field_ty>::from(*other),
220                    _ => false,
221                }
222            }
223        }
224
225        impl PartialEq<TracedValue> for $source {
226            fn eq(&self, other: &TracedValue) -> bool {
227                other == self
228            }
229        }
230
231        impl FromTracedValue<'_> for $source {
232            type Output = Self;
233
234            fn from_value(value: &TracedValue) -> Option<Self::Output> {
235                match value {
236                    TracedValue::$variant(value) => (*value).try_into().ok(),
237                    _ => None,
238                }
239            }
240        }
241    };
242}
243
244impl_value_conversions!(TracedValue::Bool(bool));
245impl_value_conversions!(TracedValue::Int(i128));
246impl_value_conversions!(TracedValue::Int(i64 as i128));
247impl_value_conversions!(TracedValue::UInt(u128));
248impl_value_conversions!(TracedValue::UInt(u64 as u128));
249impl_value_conversions!(TracedValue::Float(f64));
250
251impl PartialEq<str> for TracedValue {
252    fn eq(&self, other: &str) -> bool {
253        match self {
254            Self::String(value) => value == other,
255            _ => false,
256        }
257    }
258}
259
260impl PartialEq<TracedValue> for str {
261    fn eq(&self, other: &TracedValue) -> bool {
262        other == self
263    }
264}
265
266impl From<&str> for TracedValue {
267    fn from(value: &str) -> Self {
268        Self::String(value.to_owned())
269    }
270}
271
272impl PartialEq<&str> for TracedValue {
273    fn eq(&self, other: &&str) -> bool {
274        match self {
275            Self::String(value) => value == *other,
276            _ => false,
277        }
278    }
279}
280
281impl PartialEq<TracedValue> for &str {
282    fn eq(&self, other: &TracedValue) -> bool {
283        other == self
284    }
285}