1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
8pub struct Trace {
9 pub trace_id: String,
11
12 pub spans: Vec<Span>,
14
15 pub resource: Option<super::Resource>,
17}
18
19#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
21pub struct Span {
22 pub trace_id: String,
24
25 pub span_id: String,
27
28 pub parent_span_id: Option<String>,
30
31 pub name: String,
33
34 pub kind: SpanKind,
36
37 pub start_time: i64,
39
40 pub end_time: i64,
42
43 pub attributes: HashMap<String, String>,
45
46 pub events: Vec<SpanEvent>,
48
49 pub status: SpanStatus,
51
52 pub resource: Option<super::Resource>,
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
58pub enum SpanKind {
59 Internal,
61
62 Server,
64
65 Client,
67
68 Producer,
70
71 Consumer,
73}
74
75impl SpanKind {
76 pub fn from_i32(value: i32) -> Option<Self> {
78 match value {
79 0 => Some(Self::Internal),
80 1 => Some(Self::Server),
81 2 => Some(Self::Client),
82 3 => Some(Self::Producer),
83 4 => Some(Self::Consumer),
84 _ => None,
85 }
86 }
87
88 pub fn to_i32(self) -> i32 {
90 self as i32
91 }
92}
93
94#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
96pub struct SpanEvent {
97 pub name: String,
99
100 pub timestamp: i64,
102
103 pub attributes: HashMap<String, String>,
105}
106
107#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
109pub struct SpanStatus {
110 pub code: StatusCode,
112
113 pub message: Option<String>,
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
119pub enum StatusCode {
120 Unset,
122
123 Ok,
125
126 Error,
128}
129
130impl StatusCode {
131 pub fn from_i32(value: i32) -> Option<Self> {
133 match value {
134 0 => Some(Self::Unset),
135 1 => Some(Self::Ok),
136 2 => Some(Self::Error),
137 _ => None,
138 }
139 }
140
141 pub fn to_i32(self) -> i32 {
143 self as i32
144 }
145}
146
147impl Span {
148 pub fn new(
150 trace_id: impl Into<String>,
151 span_id: impl Into<String>,
152 name: impl Into<String>,
153 start_time: i64,
154 end_time: i64,
155 ) -> Self {
156 Self {
157 trace_id: trace_id.into(),
158 span_id: span_id.into(),
159 parent_span_id: None,
160 name: name.into(),
161 kind: SpanKind::Internal,
162 start_time,
163 end_time,
164 attributes: HashMap::new(),
165 events: Vec::new(),
166 status: SpanStatus {
167 code: StatusCode::Unset,
168 message: None,
169 },
170 resource: None,
171 }
172 }
173
174 pub fn with_parent(mut self, parent_span_id: impl Into<String>) -> Self {
176 self.parent_span_id = Some(parent_span_id.into());
177 self
178 }
179
180 pub fn with_kind(mut self, kind: SpanKind) -> Self {
182 self.kind = kind;
183 self
184 }
185
186 pub fn with_attribute(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
188 self.attributes.insert(key.into(), value.into());
189 self
190 }
191
192 pub fn with_event(mut self, event: SpanEvent) -> Self {
194 self.events.push(event);
195 self
196 }
197
198 pub fn with_status(mut self, code: StatusCode, message: Option<String>) -> Self {
200 self.status = SpanStatus { code, message };
201 self
202 }
203
204 pub fn duration_ns(&self) -> i64 {
206 self.end_time - self.start_time
207 }
208}
209
210impl Trace {
211 pub fn new(trace_id: impl Into<String>) -> Self {
213 Self {
214 trace_id: trace_id.into(),
215 spans: Vec::new(),
216 resource: None,
217 }
218 }
219
220 pub fn with_span(mut self, span: Span) -> Self {
222 self.spans.push(span);
223 self
224 }
225
226 pub fn with_resource(mut self, resource: super::Resource) -> Self {
228 self.resource = Some(resource);
229 self
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use super::*;
236
237 #[test]
238 fn test_span_creation() {
239 let span = Span::new("trace123", "span456", "http.request", 1000, 2000);
240 assert_eq!(span.trace_id, "trace123");
241 assert_eq!(span.span_id, "span456");
242 assert_eq!(span.name, "http.request");
243 assert_eq!(span.start_time, 1000);
244 assert_eq!(span.end_time, 2000);
245 }
246
247 #[test]
248 fn test_span_with_parent() {
249 let span = Span::new("trace123", "span456", "db.query", 1000, 2000).with_parent("span123");
250
251 assert_eq!(span.parent_span_id, Some("span123".to_string()));
252 }
253
254 #[test]
255 fn test_span_duration() {
256 let span = Span::new("trace123", "span456", "operation", 1000, 3500);
257 assert_eq!(span.duration_ns(), 2500);
258 }
259
260 #[test]
261 fn test_span_with_attributes() {
262 let span = Span::new("trace123", "span456", "http.request", 1000, 2000)
263 .with_attribute("http.method", "GET")
264 .with_attribute("http.url", "/api/users");
265
266 assert_eq!(span.attributes.len(), 2);
267 assert_eq!(span.attributes.get("http.method"), Some(&"GET".to_string()));
268 }
269
270 #[test]
271 fn test_trace_with_spans() {
272 let span1 = Span::new("trace123", "span1", "parent", 1000, 3000);
273 let span2 = Span::new("trace123", "span2", "child", 1500, 2500).with_parent("span1");
274
275 let trace = Trace::new("trace123").with_span(span1).with_span(span2);
276
277 assert_eq!(trace.spans.len(), 2);
278 assert_eq!(trace.trace_id, "trace123");
279 }
280}