ih_muse_record/file_recorder.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 96 97 98 99 100 101
// crates/ih-muse-record/src/file_recorder.rs
//! Implements the [`FileRecorder`] for recording events to a file.
//!
//! 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::fs::{File, OpenOptions};
use std::io::{BufWriter, Write};
use std::path::Path;
use async_trait::async_trait;
use super::SerializationFormat;
use crate::{RecordedEventWithTime, Recorder};
use ih_muse_core::{MuseError, MuseResult};
/// A recorder that writes events to a file.
///
/// The serialization format is determined by the file extension.
/// Supported extensions are `.bin` for Bincode and `.json` for JSON.
pub struct FileRecorder {
writer: BufWriter<File>,
format: SerializationFormat,
}
impl FileRecorder {
/// Creates a new `FileRecorder`.
///
/// # Arguments
///
/// - `path`: The file path to write recordings to.
///
/// # Errors
///
/// Returns a [`MuseError::Recording`] if the file cannot be opened.
pub fn new(path: &Path) -> MuseResult<Self> {
let ext = path.extension().and_then(|e| e.to_str());
let format = SerializationFormat::from_extension(ext)?;
let file = OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(path)
.map_err(|e| MuseError::Recording(format!("Failed to open file: {}", e)))?;
log::info!("Using {:?} format for recording.", format);
Ok(Self {
writer: BufWriter::new(file),
format,
})
}
}
#[async_trait]
impl Recorder for FileRecorder {
/// Records an event to the file.
///
/// # Arguments
///
/// - `event`: The [`RecordedEvent`] to record.
///
/// # Errors
///
/// Returns a [`MuseError::Recording`] if serialization fails.
async fn record(&mut self, event: RecordedEventWithTime) -> MuseResult<()> {
match self.format {
SerializationFormat::Bincode => bincode::serialize_into(&mut self.writer, &event)
.map_err(|e| MuseError::Recording(format!("Failed to record event: {}", e))),
SerializationFormat::Json => {
serde_json::to_writer(&mut self.writer, &event)
.map_err(|e| MuseError::Recording(format!("Failed to record event: {}", e)))?;
self.writer
.write_all(b"\n")
.map_err(|e| MuseError::Recording(format!("Failed to write newline: {}", e)))
}
}
}
/// Flushes the writer.
///
/// # Errors
///
/// Returns a [`MuseError::Recording`] if flushing fails.
async fn flush(&mut self) -> MuseResult<()> {
self.writer
.flush()
.map_err(|e| MuseError::Recording(format!("Failed to flush file: {}", e)))
}
/// Closes the recorder by flushing the writer.
///
/// # Errors
///
/// Returns a [`MuseError::Recording`] if flushing fails.
async fn close(&mut self) -> MuseResult<()> {
self.flush().await
}
}