ih_muse_record/
file_replayer.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// crates/ih-muse-record/src/file_replayer.rs

//! Provides the [`FileReplayer`] for replaying recorded events.
//!
//! The recording files should have a specific extension to determine
//! the encoding/serialization format. Supported extensions are:
//!
//! - `.bin` for Bincode serialization
//! - `.json` for JSON serialization

use std::path::Path;

use async_trait::async_trait;
use tokio::fs::File;
use tokio::io::{AsyncBufReadExt, AsyncReadExt, BufReader};

use super::SerializationFormat;
use crate::{RecordedEventWithTime, Replayer};
use ih_muse_core::{MuseError, MuseResult};

/// A replayer that reads events from a recording file.
///
/// The serialization format is determined by the file extension.
/// Supported extensions are `.bin` for Bincode and `.json` for JSON.
pub struct FileReplayer {
    reader: BufReader<File>,
    format: SerializationFormat,
}

impl FileReplayer {
    /// Creates a new `FileReplayer`.
    ///
    /// # Arguments
    ///
    /// - `path`: The file path to read recordings from.
    ///
    /// # Errors
    ///
    /// Returns a [`MuseError::Replaying`] if the file cannot be opened.
    pub async fn new(path: &Path) -> MuseResult<Self> {
        let ext = path.extension().and_then(|e| e.to_str());
        let format = SerializationFormat::from_extension(ext)?;

        let file = File::open(path)
            .await
            .map_err(|e| MuseError::Replaying(format!("Failed to open file: {}", e)))?;
        log::info!("Using {:?} format for replaying.", format);
        Ok(Self {
            reader: BufReader::new(file),
            format,
        })
    }
}

#[async_trait]
impl Replayer for FileReplayer {
    async fn next_event(&mut self) -> MuseResult<Option<RecordedEventWithTime>> {
        match self.format {
            SerializationFormat::Bincode => {
                // Bincode doesn't support async read directly; read the entire file into memory
                let mut buffer = Vec::new();
                let bytes_read = self
                    .reader
                    .read_to_end(&mut buffer)
                    .await
                    .map_err(|e| MuseError::Replaying(e.to_string()))?;
                if bytes_read == 0 {
                    return Ok(None); // EOF
                }
                let mut cursor = std::io::Cursor::new(buffer);
                match bincode::deserialize_from::<_, RecordedEventWithTime>(&mut cursor) {
                    Ok(timed_event) => Ok(Some(timed_event)),
                    Err(e) => Err(MuseError::Replaying(format!(
                        "Failed to deserialize event: {}",
                        e
                    ))),
                }
            }
            SerializationFormat::Json => {
                let mut line = String::new();
                match self.reader.read_line(&mut line).await {
                    Ok(0) => Ok(None), // EOF
                    Ok(_) => {
                        let timed_event: RecordedEventWithTime = serde_json::from_str(&line)
                            .map_err(|e| {
                                MuseError::Replaying(format!("Failed to deserialize event: {}", e))
                            })?;
                        Ok(Some(timed_event))
                    }
                    Err(e) => Err(MuseError::Replaying(format!("Failed to read line: {}", e))),
                }
            }
        }
    }
}