use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TraceFile {
pub id: String,
pub filename: String,
pub format: TraceFileFormat,
pub size: u64,
pub modified: DateTime<Utc>,
#[serde(skip)]
pub path: PathBuf,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum TraceFileFormat {
Json,
Jsonl,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TraceData {
pub session_id: String,
pub agent_name: Option<String>,
pub model: Option<String>,
pub events: Vec<TraceEvent>,
pub started_at: Option<DateTime<Utc>>,
pub ended_at: Option<DateTime<Utc>>,
pub total_duration_ms: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TraceEvent {
pub timestamp: DateTime<Utc>,
pub elapsed_ms: f64,
pub event_type: String,
pub data: serde_json::Value,
}
impl TraceEvent {
pub fn new(
timestamp: DateTime<Utc>,
elapsed_ms: f64,
event_type: String,
data: serde_json::Value,
) -> Self {
Self {
timestamp,
elapsed_ms,
event_type,
data,
}
}
}
impl TraceData {
pub fn new(session_id: String) -> Self {
Self {
session_id,
agent_name: None,
model: None,
events: Vec::new(),
started_at: None,
ended_at: None,
total_duration_ms: None,
}
}
pub fn add_event(&mut self, event: TraceEvent) {
self.events.push(event);
}
pub fn sort_events(&mut self) {
self.events.sort_by(|a, b| {
a.elapsed_ms
.partial_cmp(&b.elapsed_ms)
.unwrap_or(std::cmp::Ordering::Equal)
});
}
pub fn calculate_duration(&mut self) {
if let (Some(first), Some(last)) = (self.events.first(), self.events.last()) {
self.total_duration_ms = Some(last.elapsed_ms - first.elapsed_ms);
self.started_at = Some(first.timestamp);
self.ended_at = Some(last.timestamp);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_trace_event_creation() {
let event = TraceEvent::new(
Utc::now(),
100.0,
"content".to_string(),
json!({"content": "test"}),
);
assert_eq!(event.event_type, "content");
assert_eq!(event.elapsed_ms, 100.0);
}
#[test]
fn test_trace_data_duration() {
let mut trace = TraceData::new("test-session".to_string());
let now = Utc::now();
trace.add_event(TraceEvent::new(now, 0.0, "start".to_string(), json!({})));
trace.add_event(TraceEvent::new(now, 1000.0, "end".to_string(), json!({})));
trace.calculate_duration();
assert_eq!(trace.total_duration_ms, Some(1000.0));
assert!(trace.started_at.is_some());
assert!(trace.ended_at.is_some());
}
}