context_logger/
value.rs

1//! Value types for the context logger.
2
3/// Represents a type of value that can be stored in the log context.
4///
5/// The `ContextValue` type is a flexible container designed to hold various kinds of data
6/// that can be associated with a log entry. It supports primitive types, strings, and
7/// more complex types such as those implementing [`std::fmt::Debug`], [`std::fmt::Display`],
8/// [`std::error::Error`], or `serde::Serialize`.
9///
10/// This allows for rich and structured logging, enabling developers to attach meaningful
11/// context to log messages.
12///
13/// # Examples
14///
15/// ```
16/// use context_logger::ContextValue;
17///
18/// let value = ContextValue::display("example string");
19/// let number = ContextValue::from(42);
20/// let debug_value = ContextValue::debug(vec![1, 2, 3]);
21/// ```
22pub struct ContextValue(ContextValueInner);
23
24enum ContextValueInner {
25    Null,
26    String(String),
27    Bool(bool),
28    Char(char),
29    I64(i64),
30    U64(u64),
31    F64(f64),
32    I128(i128),
33    U128(u128),
34    Debug(Box<dyn std::fmt::Debug + Send + Sync + 'static>),
35    Display(Box<dyn std::fmt::Display + Send + Sync + 'static>),
36    Error(Box<dyn std::error::Error + Send + Sync + 'static>),
37    Serde(Box<dyn erased_serde::Serialize + Send + Sync + 'static>),
38}
39
40impl From<ContextValueInner> for ContextValue {
41    fn from(inner: ContextValueInner) -> Self {
42        Self(inner)
43    }
44}
45
46impl ContextValue {
47    /// Creates a null context value.
48    #[allow(clippy::must_use_candidate)]
49    pub fn null() -> Self {
50        ContextValueInner::Null.into()
51    }
52
53    /// Creates a context value from a [`serde::Serialize`].
54    pub fn serde<S>(value: S) -> Self
55    where
56        S: serde::Serialize + Send + Sync + 'static,
57    {
58        let value = Box::new(value);
59        ContextValueInner::Serde(value).into()
60    }
61
62    /// Creates a context value from a [`std::fmt::Display`].
63    pub fn display<T>(value: T) -> Self
64    where
65        T: std::fmt::Display + Send + Sync + 'static,
66    {
67        let value = Box::new(value);
68        ContextValueInner::Display(value).into()
69    }
70
71    /// Creates a context value from a [`std::fmt::Debug`].
72    pub fn debug<T>(value: T) -> Self
73    where
74        T: std::fmt::Debug + Send + Sync + 'static,
75    {
76        let value = Box::new(value);
77        ContextValueInner::Debug(value).into()
78    }
79
80    /// Creates a context value from a [`std::error::Error`].
81    pub fn error<T>(value: T) -> Self
82    where
83        T: std::error::Error + Send + Sync + 'static,
84    {
85        let value = Box::new(value);
86        ContextValueInner::Error(value).into()
87    }
88
89    /// Represents a context value that can be used with the [`log`] crate.
90    #[must_use]
91    pub fn as_log_value(&self) -> log::kv::Value<'_> {
92        match &self.0 {
93            ContextValueInner::Null => log::kv::Value::null(),
94            ContextValueInner::String(s) => log::kv::Value::from(&**s),
95            ContextValueInner::Bool(b) => log::kv::Value::from(*b),
96            ContextValueInner::Char(c) => log::kv::Value::from(*c),
97            ContextValueInner::I64(i) => log::kv::Value::from(*i),
98            ContextValueInner::U64(u) => log::kv::Value::from(*u),
99            ContextValueInner::F64(f) => log::kv::Value::from(*f),
100            ContextValueInner::I128(i) => log::kv::Value::from(*i),
101            ContextValueInner::U128(u) => log::kv::Value::from(*u),
102            ContextValueInner::Display(value) => log::kv::Value::from_dyn_display(value),
103            ContextValueInner::Debug(value) => log::kv::Value::from_dyn_debug(value),
104            ContextValueInner::Error(value) => log::kv::Value::from_dyn_error(&**value),
105            ContextValueInner::Serde(value) => log::kv::Value::from_serde(value),
106        }
107    }
108}
109
110macro_rules! impl_context_value_from_primitive {
111    ($($ty:ty => $arm:ident),*) => {
112        $(
113            impl From<$ty> for ContextValue {
114                fn from(value: $ty) -> Self {
115                    ContextValue(ContextValueInner::$arm(value.into()))
116                }
117            }
118        )*
119    };
120}
121
122impl_context_value_from_primitive!(
123    bool => Bool,
124    char => Char,
125    &str => String,
126    std::borrow::Cow<'_, str> => String,
127    String => String,
128    i8 => I64,
129    i16 => I64,
130    i32 => I64,
131    i64 => I64,
132    u8 => U64,
133    u16 => U64,
134    u32 => U64,
135    u64 => U64,
136    f64 => F64,
137    i128 => I128,
138    u128 => U128
139);
140
141impl std::fmt::Display for ContextValue {
142    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143        self.as_log_value().fmt(f)
144    }
145}
146
147impl std::fmt::Debug for ContextValue {
148    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149        self.as_log_value().fmt(f)
150    }
151}