use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Trace {
pub trace_id: String,
pub spans: Vec<Span>,
pub resource: Option<super::Resource>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Span {
pub trace_id: String,
pub span_id: String,
pub parent_span_id: Option<String>,
pub name: String,
pub kind: SpanKind,
pub start_time: i64,
pub end_time: i64,
pub attributes: HashMap<String, String>,
pub events: Vec<SpanEvent>,
pub status: SpanStatus,
pub resource: Option<super::Resource>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SpanKind {
Internal,
Server,
Client,
Producer,
Consumer,
}
impl SpanKind {
pub fn from_i32(value: i32) -> Option<Self> {
match value {
0 => Some(Self::Internal),
1 => Some(Self::Server),
2 => Some(Self::Client),
3 => Some(Self::Producer),
4 => Some(Self::Consumer),
_ => None,
}
}
pub fn to_i32(self) -> i32 {
self as i32
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpanEvent {
pub name: String,
pub timestamp: i64,
pub attributes: HashMap<String, String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpanStatus {
pub code: StatusCode,
pub message: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum StatusCode {
Unset,
Ok,
Error,
}
impl StatusCode {
pub fn from_i32(value: i32) -> Option<Self> {
match value {
0 => Some(Self::Unset),
1 => Some(Self::Ok),
2 => Some(Self::Error),
_ => None,
}
}
pub fn to_i32(self) -> i32 {
self as i32
}
}
impl Span {
pub fn new(
trace_id: impl Into<String>,
span_id: impl Into<String>,
name: impl Into<String>,
start_time: i64,
end_time: i64,
) -> Self {
Self {
trace_id: trace_id.into(),
span_id: span_id.into(),
parent_span_id: None,
name: name.into(),
kind: SpanKind::Internal,
start_time,
end_time,
attributes: HashMap::new(),
events: Vec::new(),
status: SpanStatus {
code: StatusCode::Unset,
message: None,
},
resource: None,
}
}
pub fn with_parent(mut self, parent_span_id: impl Into<String>) -> Self {
self.parent_span_id = Some(parent_span_id.into());
self
}
pub fn with_kind(mut self, kind: SpanKind) -> Self {
self.kind = kind;
self
}
pub fn with_attribute(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.attributes.insert(key.into(), value.into());
self
}
pub fn with_event(mut self, event: SpanEvent) -> Self {
self.events.push(event);
self
}
pub fn with_status(mut self, code: StatusCode, message: Option<String>) -> Self {
self.status = SpanStatus { code, message };
self
}
pub fn duration_ns(&self) -> i64 {
self.end_time - self.start_time
}
}
impl Trace {
pub fn new(trace_id: impl Into<String>) -> Self {
Self {
trace_id: trace_id.into(),
spans: Vec::new(),
resource: None,
}
}
pub fn with_span(mut self, span: Span) -> Self {
self.spans.push(span);
self
}
pub fn with_resource(mut self, resource: super::Resource) -> Self {
self.resource = Some(resource);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_span_creation() {
let span = Span::new("trace123", "span456", "http.request", 1000, 2000);
assert_eq!(span.trace_id, "trace123");
assert_eq!(span.span_id, "span456");
assert_eq!(span.name, "http.request");
assert_eq!(span.start_time, 1000);
assert_eq!(span.end_time, 2000);
}
#[test]
fn test_span_with_parent() {
let span = Span::new("trace123", "span456", "db.query", 1000, 2000).with_parent("span123");
assert_eq!(span.parent_span_id, Some("span123".to_string()));
}
#[test]
fn test_span_duration() {
let span = Span::new("trace123", "span456", "operation", 1000, 3500);
assert_eq!(span.duration_ns(), 2500);
}
#[test]
fn test_span_with_attributes() {
let span = Span::new("trace123", "span456", "http.request", 1000, 2000)
.with_attribute("http.method", "GET")
.with_attribute("http.url", "/api/users");
assert_eq!(span.attributes.len(), 2);
assert_eq!(span.attributes.get("http.method"), Some(&"GET".to_string()));
}
#[test]
fn test_trace_with_spans() {
let span1 = Span::new("trace123", "span1", "parent", 1000, 3000);
let span2 = Span::new("trace123", "span2", "child", 1500, 2500).with_parent("span1");
let trace = Trace::new("trace123").with_span(span1).with_span(span2);
assert_eq!(trace.spans.len(), 2);
assert_eq!(trace.trace_id, "trace123");
}
}