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}
185