use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use super::format::{Transcript, TranscriptEvent, TranscriptMetadata};
#[derive(Debug)]
pub struct Recorder {
start: Instant,
transcript: Arc<Mutex<Transcript>>,
recording: bool,
max_duration: Option<Duration>,
max_events: Option<usize>,
}
impl Recorder {
#[must_use]
pub fn new(width: u16, height: u16) -> Self {
Self {
start: Instant::now(),
transcript: Arc::new(Mutex::new(Transcript::new(TranscriptMetadata::new(
width, height,
)))),
recording: true,
max_duration: None,
max_events: None,
}
}
#[must_use]
pub const fn with_max_duration(mut self, duration: Duration) -> Self {
self.max_duration = Some(duration);
self
}
#[must_use]
pub const fn with_max_events(mut self, count: usize) -> Self {
self.max_events = Some(count);
self
}
#[must_use]
pub fn elapsed(&self) -> Duration {
self.start.elapsed()
}
#[must_use]
pub const fn is_recording(&self) -> bool {
self.recording
}
pub fn stop(&mut self) {
self.recording = false;
if let Ok(mut t) = self.transcript.lock() {
t.metadata.duration = Some(self.elapsed());
}
}
pub fn record_output(&self, data: &[u8]) {
if !self.should_record() {
return;
}
self.push_event(TranscriptEvent::output(self.elapsed(), data));
}
pub fn record_input(&self, data: &[u8]) {
if !self.should_record() {
return;
}
self.push_event(TranscriptEvent::input(self.elapsed(), data));
}
pub fn record_resize(&self, cols: u16, rows: u16) {
if !self.should_record() {
return;
}
self.push_event(TranscriptEvent::resize(self.elapsed(), cols, rows));
}
pub fn add_marker(&self, label: &str) {
if !self.should_record() {
return;
}
self.push_event(TranscriptEvent::marker(self.elapsed(), label));
}
fn should_record(&self) -> bool {
if !self.recording {
return false;
}
if let Some(max_dur) = self.max_duration
&& self.elapsed() > max_dur
{
return false;
}
if let Some(max_events) = self.max_events
&& let Ok(t) = self.transcript.lock()
&& t.events.len() >= max_events
{
return false;
}
true
}
fn push_event(&self, event: TranscriptEvent) {
if let Ok(mut t) = self.transcript.lock() {
t.push(event);
}
}
#[must_use]
pub fn transcript(&self) -> Arc<Mutex<Transcript>> {
Arc::clone(&self.transcript)
}
#[must_use]
pub fn into_transcript(self) -> Transcript {
Arc::try_unwrap(self.transcript)
.ok()
.and_then(|m| m.into_inner().ok())
.unwrap_or_else(|| Transcript::new(TranscriptMetadata::new(80, 24)))
}
#[must_use]
pub fn event_count(&self) -> usize {
self.transcript.lock().map(|t| t.events.len()).unwrap_or(0)
}
}
#[derive(Debug, Default)]
pub struct RecorderBuilder {
width: u16,
height: u16,
command: Option<String>,
title: Option<String>,
max_duration: Option<Duration>,
max_events: Option<usize>,
}
impl RecorderBuilder {
#[must_use]
pub fn new() -> Self {
Self {
width: 80,
height: 24,
..Default::default()
}
}
#[must_use]
pub const fn size(mut self, width: u16, height: u16) -> Self {
self.width = width;
self.height = height;
self
}
#[must_use]
pub fn command(mut self, cmd: impl Into<String>) -> Self {
self.command = Some(cmd.into());
self
}
#[must_use]
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = Some(title.into());
self
}
#[must_use]
pub const fn max_duration(mut self, duration: Duration) -> Self {
self.max_duration = Some(duration);
self
}
#[must_use]
pub const fn max_events(mut self, count: usize) -> Self {
self.max_events = Some(count);
self
}
#[must_use]
pub fn build(self) -> Recorder {
let mut recorder = Recorder::new(self.width, self.height);
if let Some(duration) = self.max_duration {
recorder.max_duration = Some(duration);
}
if let Some(events) = self.max_events {
recorder.max_events = Some(events);
}
if let Ok(mut t) = recorder.transcript.lock() {
t.metadata.command = self.command;
t.metadata.title = self.title;
}
recorder
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn recorder_basic() {
let recorder = Recorder::new(80, 24);
recorder.record_output(b"hello");
recorder.record_input(b"world");
assert_eq!(recorder.event_count(), 2);
}
#[test]
fn recorder_stop() {
let mut recorder = Recorder::new(80, 24);
recorder.record_output(b"before");
recorder.stop();
recorder.record_output(b"after");
assert_eq!(recorder.event_count(), 1);
}
#[test]
fn recorder_builder() {
let recorder = RecorderBuilder::new()
.size(120, 40)
.title("Test")
.max_events(10)
.build();
assert!(recorder.is_recording());
}
}