1 //! A `MakeVisitor` wrapper that separates formatted fields with a delimiter.
2 use super::{MakeVisitor, VisitFmt, VisitOutput};
3 
4 use core::fmt;
5 use 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)]
10 pub 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)]
18 pub struct VisitDelimited<D, V> {
19     delimiter: D,
20     seen: bool,
21     inner: V,
22     err: fmt::Result,
23 }
24 
25 // === impl Delimited ===
26 
27 impl<D, V, T> MakeVisitor<T> for Delimited<D, V>
28 where
29     D: AsRef<str> + Clone,
30     V: MakeVisitor<T>,
31     V::Visitor: VisitFmt,
32 {
33     type Visitor = VisitDelimited<D, V::Visitor>;
make_visitor(&self, target: T) -> Self::Visitor34     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 
40 impl<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
new(delimiter: D, inner: V) -> Self45     pub fn new(delimiter: D, inner: V) -> Self {
46         Self { delimiter, inner }
47     }
48 }
49 
50 // === impl VisitDelimited ===
51 
52 impl<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
new(delimiter: D, inner: V) -> Self57     pub fn new(delimiter: D, inner: V) -> Self {
58         Self {
59             delimiter,
60             inner,
61             seen: false,
62             err: Ok(()),
63         }
64     }
65 
delimit(&mut self) where V: VisitFmt, D: AsRef<str>,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 
83 impl<D, V> Visit for VisitDelimited<D, V>
84 where
85     V: VisitFmt,
86     D: AsRef<str>,
87 {
record_i64(&mut self, field: &Field, value: i64)88     fn record_i64(&mut self, field: &Field, value: i64) {
89         self.delimit();
90         self.inner.record_i64(field, value);
91     }
92 
record_u64(&mut self, field: &Field, value: u64)93     fn record_u64(&mut self, field: &Field, value: u64) {
94         self.delimit();
95         self.inner.record_u64(field, value);
96     }
97 
record_bool(&mut self, field: &Field, value: bool)98     fn record_bool(&mut self, field: &Field, value: bool) {
99         self.delimit();
100         self.inner.record_bool(field, value);
101     }
102 
record_str(&mut self, field: &Field, value: &str)103     fn record_str(&mut self, field: &Field, value: &str) {
104         self.delimit();
105         self.inner.record_str(field, value);
106     }
107 
record_debug(&mut self, field: &Field, value: &dyn fmt::Debug)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 
114 impl<D, V> VisitOutput<fmt::Result> for VisitDelimited<D, V>
115 where
116     V: VisitFmt,
117     D: AsRef<str>,
118 {
finish(self) -> fmt::Result119     fn finish(self) -> fmt::Result {
120         self.err?;
121         self.inner.finish()
122     }
123 }
124 
125 impl<D, V> VisitFmt for VisitDelimited<D, V>
126 where
127     V: VisitFmt,
128     D: AsRef<str>,
129 {
writer(&mut self) -> &mut dyn fmt::Write130     fn writer(&mut self) -> &mut dyn fmt::Write {
131         self.inner.writer()
132     }
133 }
134 
135 #[cfg(test)]
136 #[cfg(all(test, feature = "alloc"))]
137 mod test {
138     use super::*;
139     use crate::field::test_util::*;
140 
141     #[test]
delimited_visitor()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]
delimited_new_visitor()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 }
185