mirror of
https://github.com/asciinema/asciinema.git
synced 2025-12-15 19:28:00 +01:00
Make cat use first file's asciicast version
This commit is contained in:
108
src/asciicast.rs
108
src/asciicast.rs
@@ -4,6 +4,7 @@ mod v2;
|
||||
mod v3;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
use std::fs;
|
||||
use std::io::{self, BufRead};
|
||||
use std::path::Path;
|
||||
@@ -15,10 +16,18 @@ pub use v2::V2Encoder;
|
||||
pub use v3::V3Encoder;
|
||||
|
||||
pub struct Asciicast<'a> {
|
||||
pub version: Version,
|
||||
pub header: Header,
|
||||
pub events: Box<dyn Iterator<Item = Result<Event>> + 'a>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum Version {
|
||||
One,
|
||||
Two,
|
||||
Three,
|
||||
}
|
||||
|
||||
pub struct Header {
|
||||
pub term_cols: u16,
|
||||
pub term_rows: u16,
|
||||
@@ -45,6 +54,30 @@ pub enum EventData {
|
||||
Other(char, String),
|
||||
}
|
||||
|
||||
pub trait Encoder {
|
||||
fn header(&mut self, header: &Header) -> Vec<u8>;
|
||||
fn event(&mut self, event: &Event) -> Vec<u8>;
|
||||
}
|
||||
|
||||
impl PartialEq<u8> for Version {
|
||||
fn eq(&self, other: &u8) -> bool {
|
||||
matches!(
|
||||
(self, other),
|
||||
(Version::One, 1) | (Version::Two, 2) | (Version::Three, 3)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Version {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Version::One => write!(f, "1"),
|
||||
Version::Two => write!(f, "2"),
|
||||
Version::Three => write!(f, "3"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Header {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
@@ -62,6 +95,26 @@ impl Default for Header {
|
||||
}
|
||||
}
|
||||
|
||||
impl Encoder for V2Encoder {
|
||||
fn header(&mut self, header: &Header) -> Vec<u8> {
|
||||
self.header(header)
|
||||
}
|
||||
|
||||
fn event(&mut self, event: &Event) -> Vec<u8> {
|
||||
self.event(event)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encoder for V3Encoder {
|
||||
fn header(&mut self, header: &Header) -> Vec<u8> {
|
||||
self.header(header)
|
||||
}
|
||||
|
||||
fn event(&mut self, event: &Event) -> Vec<u8> {
|
||||
self.event(event)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_from_path<S: AsRef<Path>>(path: S) -> Result<Asciicast<'static>> {
|
||||
fs::File::open(path)
|
||||
.map(io::BufReader::new)
|
||||
@@ -161,6 +214,14 @@ pub fn accelerate(
|
||||
})
|
||||
}
|
||||
|
||||
pub fn encoder(version: Version) -> Option<Box<dyn Encoder>> {
|
||||
match version {
|
||||
Version::One => None,
|
||||
Version::Two => Some(Box::new(V2Encoder::new(0))),
|
||||
Version::Three => Some(Box::new(V3Encoder::new())),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Asciicast, Event, EventData, Header, V2Encoder};
|
||||
@@ -171,11 +232,15 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn open_v1_minimal() {
|
||||
let Asciicast { header, events } =
|
||||
super::open_from_path("tests/casts/minimal.json").unwrap();
|
||||
let Asciicast {
|
||||
version,
|
||||
header,
|
||||
events,
|
||||
} = super::open_from_path("tests/casts/minimal.json").unwrap();
|
||||
|
||||
let events = events.collect::<Result<Vec<Event>>>().unwrap();
|
||||
|
||||
assert_eq!(version, 1);
|
||||
assert_eq!((header.term_cols, header.term_rows), (100, 50));
|
||||
assert!(header.term_theme.is_none());
|
||||
|
||||
@@ -185,9 +250,14 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn open_v1_full() {
|
||||
let Asciicast { header, events } = super::open_from_path("tests/casts/full.json").unwrap();
|
||||
let Asciicast {
|
||||
version,
|
||||
header,
|
||||
events,
|
||||
} = super::open_from_path("tests/casts/full.json").unwrap();
|
||||
let events = events.collect::<Result<Vec<Event>>>().unwrap();
|
||||
|
||||
assert_eq!(version, 1);
|
||||
assert_eq!((header.term_cols, header.term_rows), (100, 50));
|
||||
|
||||
assert_eq!(events[0].time, 1);
|
||||
@@ -202,11 +272,15 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn open_v2_minimal() {
|
||||
let Asciicast { header, events } =
|
||||
super::open_from_path("tests/casts/minimal-v2.cast").unwrap();
|
||||
let Asciicast {
|
||||
version,
|
||||
header,
|
||||
events,
|
||||
} = super::open_from_path("tests/casts/minimal-v2.cast").unwrap();
|
||||
|
||||
let events = events.collect::<Result<Vec<Event>>>().unwrap();
|
||||
|
||||
assert_eq!(version, 2);
|
||||
assert_eq!((header.term_cols, header.term_rows), (100, 50));
|
||||
assert!(header.term_theme.is_none());
|
||||
|
||||
@@ -216,11 +290,15 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn open_v2_full() {
|
||||
let Asciicast { header, events } =
|
||||
super::open_from_path("tests/casts/full-v2.cast").unwrap();
|
||||
let Asciicast {
|
||||
version,
|
||||
header,
|
||||
events,
|
||||
} = super::open_from_path("tests/casts/full-v2.cast").unwrap();
|
||||
let events = events.take(5).collect::<Result<Vec<Event>>>().unwrap();
|
||||
let theme = header.term_theme.unwrap();
|
||||
|
||||
assert_eq!(version, 2);
|
||||
assert_eq!((header.term_cols, header.term_rows), (100, 50));
|
||||
assert_eq!(header.timestamp, Some(1509091818));
|
||||
assert_eq!(theme.fg, RGB8::new(0, 0, 0));
|
||||
@@ -248,11 +326,15 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn open_v3_minimal() {
|
||||
let Asciicast { header, events } =
|
||||
super::open_from_path("tests/casts/minimal-v3.cast").unwrap();
|
||||
let Asciicast {
|
||||
version,
|
||||
header,
|
||||
events,
|
||||
} = super::open_from_path("tests/casts/minimal-v3.cast").unwrap();
|
||||
|
||||
let events = events.collect::<Result<Vec<Event>>>().unwrap();
|
||||
|
||||
assert_eq!(version, 3);
|
||||
assert_eq!((header.term_cols, header.term_rows), (100, 50));
|
||||
assert!(header.term_theme.is_none());
|
||||
|
||||
@@ -262,11 +344,15 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn open_v3_full() {
|
||||
let Asciicast { header, events } =
|
||||
super::open_from_path("tests/casts/full-v3.cast").unwrap();
|
||||
let Asciicast {
|
||||
version,
|
||||
header,
|
||||
events,
|
||||
} = super::open_from_path("tests/casts/full-v3.cast").unwrap();
|
||||
let events = events.take(5).collect::<Result<Vec<Event>>>().unwrap();
|
||||
let theme = header.term_theme.unwrap();
|
||||
|
||||
assert_eq!(version, 3);
|
||||
assert_eq!((header.term_cols, header.term_rows), (100, 50));
|
||||
assert_eq!(header.timestamp, Some(1509091818));
|
||||
assert_eq!(theme.fg, RGB8::new(0, 0, 0));
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::collections::HashMap;
|
||||
use anyhow::{bail, Result};
|
||||
use serde::Deserialize;
|
||||
|
||||
use super::{Asciicast, Event, Header};
|
||||
use super::{Asciicast, Event, Header, Version};
|
||||
use crate::asciicast::util::deserialize_time;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@@ -57,5 +57,9 @@ pub fn load(json: String) -> Result<Asciicast<'static>> {
|
||||
Some(Ok(Event::output(time, event.data)))
|
||||
}));
|
||||
|
||||
Ok(Asciicast { header, events })
|
||||
Ok(Asciicast {
|
||||
version: Version::One,
|
||||
header,
|
||||
events,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::io;
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
|
||||
use super::{util, Asciicast, Event, EventData, Header};
|
||||
use super::{util, Asciicast, Event, EventData, Header, Version};
|
||||
use crate::tty::TtyTheme;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -87,7 +87,11 @@ impl Parser {
|
||||
|
||||
let events = Box::new(lines.filter_map(parse_line));
|
||||
|
||||
Asciicast { header, events }
|
||||
Asciicast {
|
||||
version: Version::Two,
|
||||
header,
|
||||
events,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::io;
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
|
||||
use super::{util, Asciicast, Event, EventData, Header};
|
||||
use super::{util, Asciicast, Event, EventData, Header, Version};
|
||||
use crate::tty::TtyTheme;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -103,7 +103,11 @@ impl Parser {
|
||||
|
||||
let events = Box::new(lines.filter_map(move |line| self.parse_line(line)));
|
||||
|
||||
Asciicast { header, events }
|
||||
Asciicast {
|
||||
version: Version::Three,
|
||||
header,
|
||||
events,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_line(&mut self, line: io::Result<String>) -> Option<Result<Event>> {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Result};
|
||||
|
||||
use crate::asciicast;
|
||||
use crate::cli;
|
||||
@@ -9,11 +9,21 @@ use crate::config::Config;
|
||||
|
||||
impl cli::Cat {
|
||||
pub fn run(self, _config: &Config) -> Result<()> {
|
||||
let mut encoder = asciicast::V2Encoder::new(0);
|
||||
let mut stdout = io::stdout();
|
||||
let mut time_offset: u64 = 0;
|
||||
let mut first = true;
|
||||
|
||||
let casts = self
|
||||
.filename
|
||||
.iter()
|
||||
.map(asciicast::open_from_path)
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
let version = casts[0].version;
|
||||
|
||||
let mut encoder = asciicast::encoder(version)
|
||||
.ok_or(anyhow!("asciicast v{version} files can't be concatenated"))?;
|
||||
|
||||
for path in self.filename.iter() {
|
||||
let recording = asciicast::open_from_path(path)?;
|
||||
let mut time = time_offset;
|
||||
|
||||
@@ -7,8 +7,7 @@ use std::io::Write;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::asciicast::Event;
|
||||
use crate::asciicast::Header;
|
||||
use crate::asciicast::{Event, Header};
|
||||
pub use asciicast::{AsciicastV2Encoder, AsciicastV3Encoder};
|
||||
pub use raw::RawEncoder;
|
||||
pub use txt::TextEncoder;
|
||||
|
||||
Reference in New Issue
Block a user