New implementation of play command (basic version)

This commit is contained in:
Marcin Kulik
2024-01-02 16:06:43 +01:00
parent 58b3d567c3
commit 6e380cf245
4 changed files with 55 additions and 7 deletions

View File

@@ -1,5 +1,7 @@
use crate::player;
use anyhow::Result; use anyhow::Result;
use clap::Args; use clap::Args;
use std::fs;
#[derive(Debug, Args)] #[derive(Debug, Args)]
pub struct Cli { pub struct Cli {
@@ -24,6 +26,8 @@ pub struct Cli {
impl Cli { impl Cli {
pub fn run(self) -> Result<()> { pub fn run(self) -> Result<()> {
todo!(); let file = fs::File::open(self.filename)?;
player::play(file)
} }
} }

View File

@@ -1,4 +1,4 @@
use serde::de::Error; use anyhow::Result;
use serde::{Deserialize, Deserializer}; use serde::{Deserialize, Deserializer};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{self, Display}; use std::fmt::{self, Display};
@@ -84,7 +84,7 @@ where
} }
} }
pub fn get_duration<S: AsRef<Path>>(path: S) -> anyhow::Result<u64> { pub fn get_duration<S: AsRef<Path>>(path: S) -> Result<u64> {
let file = fs::File::open(path)?; let file = fs::File::open(path)?;
let reader = io::BufReader::new(file); let reader = io::BufReader::new(file);
let (_header, events) = open(reader)?; let (_header, events) = open(reader)?;
@@ -93,9 +93,7 @@ pub fn get_duration<S: AsRef<Path>>(path: S) -> anyhow::Result<u64> {
Ok(time) Ok(time)
} }
pub fn open<R: BufRead>( pub fn open<R: BufRead>(reader: R) -> Result<(Header, impl Iterator<Item = Result<Event>>)> {
reader: R,
) -> anyhow::Result<(Header, impl Iterator<Item = anyhow::Result<Event>>)> {
let mut lines = reader.lines(); let mut lines = reader.lines();
let first_line = lines.next().ok_or(anyhow::anyhow!("empty file"))??; let first_line = lines.next().ok_or(anyhow::anyhow!("empty file"))??;
let header: Header = serde_json::from_str(&first_line)?; let header: Header = serde_json::from_str(&first_line)?;
@@ -104,7 +102,7 @@ pub fn open<R: BufRead>(
Ok((header, events)) Ok((header, events))
} }
fn parse_event(line: io::Result<String>) -> Option<anyhow::Result<Event>> { fn parse_event(line: io::Result<String>) -> Option<Result<Event>> {
match line { match line {
Ok(line) => { Ok(line) => {
if line.is_empty() { if line.is_empty() {
@@ -122,6 +120,8 @@ fn deserialize_time<'de, D>(deserializer: D) -> Result<u64, D::Error>
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
use serde::de::Error;
let value: serde_json::Value = Deserialize::deserialize(deserializer)?; let value: serde_json::Value = Deserialize::deserialize(deserializer)?;
let string = value.to_string(); let string = value.to_string();
let parts: Vec<&str> = string.split('.').collect(); let parts: Vec<&str> = string.split('.').collect();
@@ -146,6 +146,7 @@ fn deserialize_code<'de, D>(deserializer: D) -> Result<EventCode, D::Error>
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
use serde::de::Error;
use EventCode::*; use EventCode::*;
let value: &str = Deserialize::deserialize(deserializer)?; let value: &str = Deserialize::deserialize(deserializer)?;
@@ -292,6 +293,21 @@ impl From<&super::Header> for Header {
} }
} }
pub fn output(
events: impl Iterator<Item = Result<Event>>,
) -> impl Iterator<Item = Result<(u64, String)>> {
events.filter_map(|e| match e {
Ok(Event {
code: EventCode::Output,
time,
data,
}) => Some(Ok((time, data))),
Ok(_) => None,
Err(e) => Some(Err(e)),
})
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{Event, EventCode, Header, Writer}; use super::{Event, EventCode, Header, Writer};

View File

@@ -2,6 +2,7 @@ mod cmd;
mod config; mod config;
mod format; mod format;
mod locale; mod locale;
mod player;
mod pty; mod pty;
mod recorder; mod recorder;
use crate::config::Config; use crate::config::Config;

27
src/player.rs Normal file
View File

@@ -0,0 +1,27 @@
use crate::format::asciicast;
use anyhow::Result;
use std::io::{self, Write};
use std::thread;
use std::time::{Duration, Instant};
pub fn play(input: impl io::Read) -> Result<()> {
let reader = io::BufReader::new(input);
let (_, events) = asciicast::open(reader)?;
let output = asciicast::output(events);
let mut stdout = io::stdout();
let epoch = Instant::now();
for o in output {
let (time, data) = o?;
let diff = time as i64 - epoch.elapsed().as_micros() as i64;
if diff > 0 {
stdout.flush().unwrap();
thread::sleep(Duration::from_micros(diff as u64));
}
stdout.write(data.as_bytes())?;
}
Ok(())
}