use std::future::Future;
use std::io;
use std::path::Path;
use std::sync::Arc;
use std::sync::atomic::AtomicU64;
use futures::future::Either;
use serde::Serialize;
use crate::rhythm::Rhythm;
use super::frame::RhythmMeta;
use super::orchestrator::ReplayOrchestrator;
use super::recording::RecordingRhythm;
use super::replay::{ReplayMode, ReplayRhythm, ReplayTiming};
use super::rlog::{FrameWriter, RlogReader};
pub struct SessionRecorder {
writer: Arc<FrameWriter>,
global_seq: Arc<AtomicU64>,
}
impl SessionRecorder {
pub fn create(path: impl AsRef<Path>, rhythms: &[RhythmMeta]) -> io::Result<Self> {
let writer = FrameWriter::create(path, rhythms)?;
let global_seq = Arc::new(AtomicU64::new(0));
Ok(Self { writer, global_seq })
}
pub fn from_env(rhythms: &[RhythmMeta]) -> Option<Self> {
let path = std::env::var("ROPLAT_RECORD").ok()?;
Some(Self::create(path, rhythms).expect("SessionRecorder: 无法创建录制文件"))
}
pub fn wrap<R>(&self, rhythm: R, rhythm_id: u16) -> RecordingRhythm<R> {
RecordingRhythm::new(
rhythm,
rhythm_id,
self.writer.clone(),
self.global_seq.clone(),
)
}
pub fn maybe_wrap<R>(
recorder: Option<&Self>,
rhythm: R,
rhythm_id: u16,
) -> MaybeRecordingRhythm<R> {
match recorder {
Some(rec) => MaybeRecordingRhythm::Recording(rec.wrap(rhythm, rhythm_id)),
None => MaybeRecordingRhythm::Passthrough(rhythm),
}
}
pub fn flush(&self) {
self.writer.flush();
}
}
pub struct SessionPlayer {
rlog: RlogReader,
}
impl SessionPlayer {
pub fn open(path: impl AsRef<Path>) -> io::Result<Self> {
let rlog = RlogReader::open(path)?;
Ok(Self { rlog })
}
pub fn from_env() -> Option<Self> {
let path = std::env::var("ROPLAT_REPLAY").ok()?;
Some(Self::open(path).expect("SessionPlayer: 无法打开 .rlog 文件"))
}
pub fn replay_rhythm<Y, D>(
&self,
rhythm_id: u16,
mode: ReplayMode,
timing: ReplayTiming,
) -> ReplayRhythm<Y, D> {
let frames = self.rlog.frames_for(rhythm_id);
ReplayRhythm::new(frames, mode, timing)
}
pub fn orchestrator(&self) -> ReplayOrchestrator {
ReplayOrchestrator::from_rlog(&self.rlog)
}
pub fn rhythm_ids(&self) -> Vec<u16> {
let mut ids: Vec<u16> = self.rlog.frames.iter().map(|f| f.rhythm_id).collect();
ids.sort_unstable();
ids.dedup();
ids
}
pub fn rhythm_meta(&self, rhythm_id: u16) -> Option<&RhythmMeta> {
self.rlog.rhythm_meta(rhythm_id)
}
pub fn frame_count(&self) -> usize {
self.rlog.frame_count()
}
pub fn rlog(&self) -> &RlogReader {
&self.rlog
}
}
pub enum MaybeRecordingRhythm<R> {
Passthrough(R),
Recording(RecordingRhythm<R>),
}
impl<R> Rhythm for MaybeRecordingRhythm<R>
where
R: Rhythm + Send,
R::Yield: Serialize + Clone + Send,
R::Feed: Serialize + Send,
R::Output: Send,
R::Error: Send,
{
type Yield = R::Yield;
type Feed = R::Feed;
type Output = R::Output;
type Error = R::Error;
fn drive<N, F, Fut>(
&mut self,
nodes: N,
op_domain: F,
) -> impl Future<Output = (Self::Output, N)> + Send
where
N: Send,
F: FnMut(N, Self::Yield) -> Fut + Send,
Fut: Future<Output = (Self::Feed, N)> + Send,
{
match self {
Self::Passthrough(r) => Either::Left(r.drive(nodes, op_domain)),
Self::Recording(r) => Either::Right(r.drive(nodes, op_domain)),
}
}
}