1use crate::protocol::Message;
7use serde::{Deserialize, Serialize};
8use std::sync::{Arc, RwLock};
9use std::time::{Duration, Instant, SystemTime};
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct RecordedSession {
14 pub name: String,
16 pub started_at: SystemTime,
18 #[serde(with = "duration_serde")]
20 pub duration: Duration,
21 pub events: Vec<SessionEvent>,
23 pub metadata: SessionMetadata,
25}
26
27impl RecordedSession {
28 #[must_use]
30 pub fn new(name: impl Into<String>) -> Self {
31 Self {
32 name: name.into(),
33 started_at: SystemTime::now(),
34 duration: Duration::ZERO,
35 events: Vec::new(),
36 metadata: SessionMetadata::default(),
37 }
38 }
39
40 #[must_use]
42 pub fn len(&self) -> usize {
43 self.events.len()
44 }
45
46 #[must_use]
48 pub fn is_empty(&self) -> bool {
49 self.events.is_empty()
50 }
51
52 #[must_use]
54 pub fn messages(&self) -> Vec<&Message> {
55 self.events
56 .iter()
57 .filter_map(|e| match e {
58 SessionEvent::MessageSent { message, .. }
59 | SessionEvent::MessageReceived { message, .. } => Some(message),
60 _ => None,
61 })
62 .collect()
63 }
64
65 #[must_use]
67 pub fn events_in_range(&self, start: Duration, end: Duration) -> Vec<&SessionEvent> {
68 self.events
69 .iter()
70 .filter(|e| {
71 let offset = e.offset();
72 offset >= start && offset <= end
73 })
74 .collect()
75 }
76
77 pub fn to_json(&self) -> Result<String, serde_json::Error> {
83 serde_json::to_string_pretty(self)
84 }
85
86 pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
92 serde_json::from_str(json)
93 }
94}
95
96#[derive(Debug, Clone, Default, Serialize, Deserialize)]
98pub struct SessionMetadata {
99 pub client_name: Option<String>,
101 pub client_version: Option<String>,
103 pub server_name: Option<String>,
105 pub server_version: Option<String>,
107 pub transport: Option<String>,
109 pub protocol_version: Option<String>,
111 pub tags: Vec<String>,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
117#[serde(tag = "type")]
118pub enum SessionEvent {
119 SessionStarted {
121 #[serde(with = "duration_serde")]
123 offset: Duration,
124 },
125 MessageSent {
127 #[serde(with = "duration_serde")]
129 offset: Duration,
130 message: Message,
132 },
133 MessageReceived {
135 #[serde(with = "duration_serde")]
137 offset: Duration,
138 message: Message,
140 },
141 Error {
143 #[serde(with = "duration_serde")]
145 offset: Duration,
146 error: String,
148 },
149 SessionEnded {
151 #[serde(with = "duration_serde")]
153 offset: Duration,
154 reason: Option<String>,
156 },
157 Custom {
159 #[serde(with = "duration_serde")]
161 offset: Duration,
162 name: String,
164 data: serde_json::Value,
166 },
167}
168
169impl SessionEvent {
170 #[must_use]
172 pub fn offset(&self) -> Duration {
173 match self {
174 Self::SessionStarted { offset }
175 | Self::MessageSent { offset, .. }
176 | Self::MessageReceived { offset, .. }
177 | Self::Error { offset, .. }
178 | Self::SessionEnded { offset, .. }
179 | Self::Custom { offset, .. } => *offset,
180 }
181 }
182}
183
184pub struct SessionRecorder {
186 name: String,
188 started: Instant,
190 state: Arc<RwLock<RecorderState>>,
192}
193
194struct RecorderState {
195 events: Vec<SessionEvent>,
197 recording: bool,
199 metadata: SessionMetadata,
201}
202
203impl SessionRecorder {
204 #[must_use]
206 pub fn new(name: impl Into<String>) -> Self {
207 Self {
208 name: name.into(),
209 started: Instant::now(),
210 state: Arc::new(RwLock::new(RecorderState {
211 events: vec![SessionEvent::SessionStarted {
212 offset: Duration::ZERO,
213 }],
214 recording: true,
215 metadata: SessionMetadata::default(),
216 })),
217 }
218 }
219
220 pub fn set_metadata(&self, metadata: SessionMetadata) {
222 if let Ok(mut state) = self.state.write() {
223 state.metadata = metadata;
224 }
225 }
226
227 #[must_use]
229 pub fn is_recording(&self) -> bool {
230 self.state.read().map(|s| s.recording).unwrap_or(false)
231 }
232
233 pub fn stop(&self, reason: Option<String>) {
235 if let Ok(mut state) = self.state.write() {
236 if state.recording {
237 state.events.push(SessionEvent::SessionEnded {
238 offset: self.started.elapsed(),
239 reason,
240 });
241 state.recording = false;
242 }
243 }
244 }
245
246 pub fn record_sent(&self, message: Message) {
248 if let Ok(mut state) = self.state.write() {
249 if state.recording {
250 state.events.push(SessionEvent::MessageSent {
251 offset: self.started.elapsed(),
252 message,
253 });
254 }
255 }
256 }
257
258 pub fn record_received(&self, message: Message) {
260 if let Ok(mut state) = self.state.write() {
261 if state.recording {
262 state.events.push(SessionEvent::MessageReceived {
263 offset: self.started.elapsed(),
264 message,
265 });
266 }
267 }
268 }
269
270 pub fn record_error(&self, error: impl Into<String>) {
272 if let Ok(mut state) = self.state.write() {
273 if state.recording {
274 state.events.push(SessionEvent::Error {
275 offset: self.started.elapsed(),
276 error: error.into(),
277 });
278 }
279 }
280 }
281
282 pub fn record_custom(&self, name: impl Into<String>, data: serde_json::Value) {
284 if let Ok(mut state) = self.state.write() {
285 if state.recording {
286 state.events.push(SessionEvent::Custom {
287 offset: self.started.elapsed(),
288 name: name.into(),
289 data,
290 });
291 }
292 }
293 }
294
295 #[must_use]
297 pub fn event_count(&self) -> usize {
298 self.state.read().map(|s| s.events.len()).unwrap_or(0)
299 }
300
301 #[must_use]
303 pub fn finalize(self) -> RecordedSession {
304 self.stop(Some("finalized".to_string()));
305
306 let (events, metadata) = self
307 .state
308 .read()
309 .map(|s| (s.events.clone(), s.metadata.clone()))
310 .unwrap_or_default();
311
312 RecordedSession {
313 name: self.name,
314 started_at: SystemTime::now() - self.started.elapsed(),
315 duration: self.started.elapsed(),
316 events,
317 metadata,
318 }
319 }
320}
321
322impl Clone for SessionRecorder {
323 fn clone(&self) -> Self {
324 Self {
325 name: self.name.clone(),
326 started: self.started,
327 state: Arc::clone(&self.state),
328 }
329 }
330}
331
332mod duration_serde {
334 use serde::{Deserialize, Deserializer, Serialize, Serializer};
335 use std::time::Duration;
336
337 pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
338 where
339 S: Serializer,
340 {
341 duration.as_millis().serialize(serializer)
342 }
343
344 pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
345 where
346 D: Deserializer<'de>,
347 {
348 let ms = u64::deserialize(deserializer)?;
349 Ok(Duration::from_millis(ms))
350 }
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356 use crate::protocol::{Request, RequestId, Response};
357
358 #[test]
359 fn test_session_recorder() {
360 let recorder = SessionRecorder::new("test-session");
361
362 recorder.record_sent(Message::Request(Request::new("test/method", 1)));
363 recorder.record_received(Message::Response(Response::success(
364 RequestId::from(1),
365 serde_json::json!({}),
366 )));
367
368 assert_eq!(recorder.event_count(), 3); assert!(recorder.is_recording());
370
371 let session = recorder.finalize();
372 assert_eq!(session.name, "test-session");
373 assert_eq!(session.events.len(), 4); }
375
376 #[test]
377 fn test_session_serialization() {
378 let recorder = SessionRecorder::new("test");
379 recorder.record_sent(Message::Request(Request::new("ping", 1)));
380 let session = recorder.finalize();
381
382 let json = session.to_json().expect("Failed to serialize");
383 let restored = RecordedSession::from_json(&json).expect("Failed to deserialize");
384
385 assert_eq!(restored.name, "test");
386 assert_eq!(restored.events.len(), session.events.len());
387 }
388
389 #[test]
390 fn test_session_metadata() {
391 let recorder = SessionRecorder::new("test");
392
393 let metadata = SessionMetadata {
394 client_name: Some("test-client".to_string()),
395 client_version: Some("1.0.0".to_string()),
396 ..Default::default()
397 };
398
399 recorder.set_metadata(metadata);
400
401 let session = recorder.finalize();
402 assert_eq!(
403 session.metadata.client_name,
404 Some("test-client".to_string())
405 );
406 }
407}