tracing_subscriber/field/
delimited.rs

1//! A `MakeVisitor` wrapper that separates formatted fields with a delimiter.
2use super::{MakeVisitor, VisitFmt, VisitOutput};
3
4use core::fmt;
5use tracing_core::field::{Field, Visit};
6
7/// A `MakeVisitor` wrapper that wraps a visitor that writes formatted output so
8/// that a delimiter is inserted between writing formatted field values.
9#[derive(Debug, Clone)]
10pub struct Delimited<D, V> {
11    delimiter: D,
12    inner: V,
13}
14
15/// A visitor wrapper that inserts a delimiter after the wrapped visitor formats
16/// a field value.
17#[derive(Debug)]
18pub struct VisitDelimited<D, V> {
19    delimiter: D,
20    seen: bool,
21    inner: V,
22    err: fmt::Result,
23}
24
25// === impl Delimited ===
26
27impl<D, V, T> MakeVisitor<T> for Delimited<D, V>
28where
29    D: AsRef<str> + Clone,
30    V: MakeVisitor<T>,
31    V::Visitor: VisitFmt,
32{
33    type Visitor = VisitDelimited<D, V::Visitor>;
34    fn make_visitor(&self, target: T) -> Self::Visitor {
35        let inner = self.inner.make_visitor(target);
36        VisitDelimited::new(self.delimiter.clone(), inner)
37    }
38}
39
40impl<D, V> Delimited<D, V> {
41    /// Returns a new [`MakeVisitor`] implementation that wraps `inner` so that
42    /// it will format each visited field separated by the provided `delimiter`.
43    ///
44    /// [`MakeVisitor`]: super::MakeVisitor
45    pub fn new(delimiter: D, inner: V) -> Self {
46        Self { delimiter, inner }
47    }
48}
49
50// === impl VisitDelimited ===
51
52impl<D, V> VisitDelimited<D, V> {
53    /// Returns a new [`Visit`] implementation that wraps `inner` so that
54    /// each formatted field is separated by the provided `delimiter`.
55    ///
56    /// [`Visit`]: tracing_core::field::Visit
57    pub fn new(delimiter: D, inner: V) -> Self {
58        Self {
59            delimiter,
60            inner,
61            seen: false,
62            err: Ok(()),
63        }
64    }
65
66    fn delimit(&mut self)
67    where
68        V: VisitFmt,
69        D: AsRef<str>,
70    {
71        if self.err.is_err() {
72            return;
73        }
74
75        if self.seen {
76            self.err = self.inner.writer().write_str(self.delimiter.as_ref());
77        }
78
79        self.seen = true;
80    }
81}
82
83impl<D, V> Visit for VisitDelimited<D, V>
84where
85    V: VisitFmt,
86    D: AsRef<str>,
87{
88    fn record_i64(&mut self, field: &Field, value: i64) {
89        self.delimit();
90        self.inner.record_i64(field, value);
91    }
92
93    fn record_u64(&mut self, field: &Field, value: u64) {
94        self.delimit();
95        self.inner.record_u64(field, value);
96    }
97
98    fn record_bool(&mut self, field: &Field, value: bool) {
99        self.delimit();
100        self.inner.record_bool(field, value);
101    }
102
103    fn record_str(&mut self, field: &Field, value: &str) {
104        self.delimit();
105        self.inner.record_str(field, value);
106    }
107
108    fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
109        self.delimit();
110        self.inner.record_debug(field, value);
111    }
112}
113
114impl<D, V> VisitOutput<fmt::Result> for VisitDelimited<D, V>
115where
116    V: VisitFmt,
117    D: AsRef<str>,
118{
119    fn finish(self) -> fmt::Result {
120        self.err?;
121        self.inner.finish()
122    }
123}
124
125impl<D, V> VisitFmt for VisitDelimited<D, V>
126where
127    V: VisitFmt,
128    D: AsRef<str>,
129{
130    fn writer(&mut self) -> &mut dyn fmt::Write {
131        self.inner.writer()
132    }
133}
134
135#[cfg(test)]
136#[cfg(all(test, feature = "alloc"))]
137mod test {
138    use super::*;
139    use crate::field::test_util::*;
140
141    #[test]
142    fn delimited_visitor() {
143        let mut s = String::new();
144        let visitor = DebugVisitor::new(&mut s);
145        let mut visitor = VisitDelimited::new(", ", visitor);
146
147        TestAttrs1::with(|attrs| attrs.record(&mut visitor));
148        visitor.finish().unwrap();
149
150        assert_eq!(
151            s.as_str(),
152            "question=\"life, the universe, and everything\", tricky=true, can_you_do_it=true"
153        );
154    }
155
156    #[test]
157    fn delimited_new_visitor() {
158        let make = Delimited::new("; ", MakeDebug);
159
160        TestAttrs1::with(|attrs| {
161            let mut s = String::new();
162            {
163                let mut v = make.make_visitor(&mut s);
164                attrs.record(&mut v);
165            }
166            assert_eq!(
167                s.as_str(),
168                "question=\"life, the universe, and everything\"; tricky=true; can_you_do_it=true"
169            );
170        });
171
172        TestAttrs2::with(|attrs| {
173            let mut s = String::new();
174            {
175                let mut v = make.make_visitor(&mut s);
176                attrs.record(&mut v);
177            }
178            assert_eq!(
179                s.as_str(),
180                "question=None; question.answer=42; tricky=true; can_you_do_it=false"
181            );
182        });
183    }
184}