Skip to main content

context_logger/
records.rs

1use std::{borrow::Cow, collections::HashMap};
2
3use crate::LogValue;
4
5pub type LogRecordsIter<'a> = std::collections::hash_map::Iter<'a, Cow<'static, str>, LogValue>;
6pub type LogRecordsIntoIter = std::collections::hash_map::IntoIter<Cow<'static, str>, LogValue>;
7pub type LogRecord = (Cow<'static, str>, LogValue);
8pub type LogRecordRef<'a> = (&'a Cow<'static, str>, &'a LogValue);
9
10/// A set of records that can be attached to a logging scope.
11///
12/// [`LogRecords`] represents a set of key-value pairs that can be
13/// added to log messages when the log context scope is active.
14///
15/// # Ordering
16///
17/// The order in which records appear is **not guaranteed**. Do not rely on any specific
18/// ordering of keys.
19#[derive(Debug, Clone, Default)]
20pub struct LogRecords(pub(crate) HashMap<Cow<'static, str>, LogValue>);
21
22impl LogRecords {
23    #[must_use]
24    pub fn new() -> Self {
25        Self::default()
26    }
27
28    /// Adds a key-value record to this collection, returning the collection for chained calls.
29    ///
30    /// # Examples
31    ///
32    /// This method takes ownership of `self`, so it can be used as part of a
33    /// builder-style chain:
34    ///
35    /// ```
36    /// use context_logger::LogRecords;
37    ///
38    /// let records = LogRecords::new()
39    ///     .field("user_id", "user-123")
40    ///     .field("request_id", 42);
41    /// ```
42    #[must_use]
43    pub fn field(mut self, key: impl Into<Cow<'static, str>>, value: impl Into<LogValue>) -> Self {
44        self.insert(key, value);
45        self
46    }
47
48    /// Adds a key-value record to this collection.
49    pub fn insert(&mut self, key: impl Into<Cow<'static, str>>, value: impl Into<LogValue>) {
50        self.0.insert(key.into(), value.into());
51    }
52
53    /// Extends this collection with the records from another collection.
54    pub fn extend(&mut self, other: impl IntoIterator<Item = LogRecord>) {
55        self.0.extend(other);
56    }
57
58    /// Returns an iterator over the records in this collection.
59    #[must_use]
60    pub fn iter(&self) -> LogRecordsIter<'_> {
61        self.0.iter()
62    }
63
64    /// Returns `true` if this collection contains no records.
65    #[must_use]
66    pub fn is_empty(&self) -> bool {
67        self.0.is_empty()
68    }
69}
70
71impl<'a> IntoIterator for &'a LogRecords {
72    type Item = LogRecordRef<'a>;
73    type IntoIter = LogRecordsIter<'a>;
74
75    fn into_iter(self) -> Self::IntoIter {
76        self.iter()
77    }
78}
79
80impl IntoIterator for LogRecords {
81    type Item = LogRecord;
82    type IntoIter = LogRecordsIntoIter;
83
84    fn into_iter(self) -> Self::IntoIter {
85        self.0.into_iter()
86    }
87}
88
89#[cfg(test)]
90impl LogRecords {
91    /// Returns a reference to the value associated with the given key, if it exists.
92    pub(crate) fn find(&self, key: impl AsRef<str>) -> Option<&LogValue> {
93        self.0.get(&Cow::Owned(key.as_ref().to_owned()))
94    }
95}
96
97#[cfg(test)]
98impl std::ops::Index<&str> for LogRecords {
99    type Output = LogValue;
100
101    fn index(&self, index: &str) -> &Self::Output {
102        self.0
103            .get(&Cow::Owned(index.to_owned()))
104            .expect("No record found for the given key")
105    }
106}