Skip to main content

context_logger/
value.rs

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