1extern crate alloc;
7
8use alloc::vec::Vec;
9
10use crate::span::ResourceSpan;
11
12pub trait SpanExporter {
14 fn export(&self, spans: &[ResourceSpan]);
17
18 fn flush(&self);
20}
21
22pub struct NullExporter {
45 #[cfg(feature = "std")]
46 spans: std::sync::Mutex<Vec<ResourceSpan>>,
47 #[cfg(not(feature = "std"))]
48 spans: core::cell::RefCell<Vec<ResourceSpan>>,
49}
50
51impl NullExporter {
52 pub fn new() -> Self {
54 NullExporter {
55 #[cfg(feature = "std")]
56 spans: std::sync::Mutex::new(Vec::new()),
57 #[cfg(not(feature = "std"))]
58 spans: core::cell::RefCell::new(Vec::new()),
59 }
60 }
61
62 pub fn len(&self) -> usize {
64 #[cfg(feature = "std")]
65 {
66 self.spans.lock().unwrap().len()
67 }
68 #[cfg(not(feature = "std"))]
69 {
70 self.spans.borrow().len()
71 }
72 }
73
74 pub fn is_empty(&self) -> bool {
76 self.len() == 0
77 }
78
79 pub fn drain(&self) -> Vec<ResourceSpan> {
81 #[cfg(feature = "std")]
82 {
83 let mut guard = self.spans.lock().unwrap();
84 core::mem::take(&mut *guard)
85 }
86 #[cfg(not(feature = "std"))]
87 {
88 let mut guard = self.spans.borrow_mut();
89 core::mem::take(&mut *guard)
90 }
91 }
92
93 pub fn snapshot(&self) -> Vec<ResourceSpan> {
95 #[cfg(feature = "std")]
96 {
97 self.spans.lock().unwrap().clone()
98 }
99 #[cfg(not(feature = "std"))]
100 {
101 self.spans.borrow().clone()
102 }
103 }
104}
105
106impl Default for NullExporter {
107 fn default() -> Self {
108 Self::new()
109 }
110}
111
112impl SpanExporter for NullExporter {
113 fn export(&self, spans: &[ResourceSpan]) {
114 #[cfg(feature = "std")]
115 {
116 let mut guard = self.spans.lock().unwrap();
117 guard.extend(spans.iter().cloned());
118 }
119 #[cfg(not(feature = "std"))]
120 {
121 let mut guard = self.spans.borrow_mut();
122 guard.extend(spans.iter().cloned());
123 }
124 }
125
126 fn flush(&self) {
127 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134 use crate::trace::TraceContext;
135
136 #[test]
137 fn null_exporter_collects_and_drains() {
138 let exporter = NullExporter::new();
139 assert!(exporter.is_empty());
140
141 let span1 = ResourceSpan::new("op1", TraceContext::default());
142 let span2 = ResourceSpan::new("op2", TraceContext::default());
143 exporter.export(&[span1, span2]);
144
145 assert_eq!(exporter.len(), 2);
146
147 let drained = exporter.drain();
148 assert_eq!(drained.len(), 2);
149 assert_eq!(drained[0].name, "op1");
150 assert_eq!(drained[1].name, "op2");
151 assert!(exporter.is_empty());
152 }
153
154 #[test]
155 fn null_exporter_snapshot_does_not_drain() {
156 let exporter = NullExporter::new();
157 let span = ResourceSpan::new("snap", TraceContext::default());
158 exporter.export(&[span]);
159
160 let snap = exporter.snapshot();
161 assert_eq!(snap.len(), 1);
162 assert_eq!(exporter.len(), 1); }
164
165 #[test]
166 fn null_exporter_multiple_exports() {
167 let exporter = NullExporter::new();
168 for i in 0..10 {
169 let name = alloc::format!("span_{i}");
170 let span = ResourceSpan::new(&name, TraceContext::default());
171 exporter.export(&[span]);
172 }
173 assert_eq!(exporter.len(), 10);
174 }
175}