mirror of
https://github.com/asciinema/asciinema.git
synced 2025-12-16 19:58:03 +01:00
New implementation of play command (basic version)
This commit is contained in:
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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};
|
||||||
|
|||||||
@@ -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
27
src/player.rs
Normal 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(())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user