mirror of
https://github.com/asciinema/asciinema.git
synced 2025-12-16 11:48:13 +01:00
Add asciicast v3 support, record and convert to v3 by default
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
mod util;
|
||||
mod v1;
|
||||
mod v2;
|
||||
mod v3;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
@@ -10,7 +11,8 @@ use std::path::Path;
|
||||
use anyhow::{anyhow, Result};
|
||||
|
||||
use crate::tty::TtyTheme;
|
||||
pub use v2::Encoder;
|
||||
pub use v2::V2Encoder;
|
||||
pub use v3::V3Encoder;
|
||||
|
||||
pub struct Asciicast<'a> {
|
||||
pub header: Header,
|
||||
@@ -18,14 +20,16 @@ pub struct Asciicast<'a> {
|
||||
}
|
||||
|
||||
pub struct Header {
|
||||
pub cols: u16,
|
||||
pub rows: u16,
|
||||
pub term_cols: u16,
|
||||
pub term_rows: u16,
|
||||
pub term_type: Option<String>,
|
||||
pub term_version: Option<String>,
|
||||
pub term_theme: Option<TtyTheme>,
|
||||
pub timestamp: Option<u64>,
|
||||
pub idle_time_limit: Option<f64>,
|
||||
pub command: Option<String>,
|
||||
pub title: Option<String>,
|
||||
pub env: Option<HashMap<String, String>>,
|
||||
pub theme: Option<TtyTheme>,
|
||||
}
|
||||
|
||||
pub struct Event {
|
||||
@@ -44,14 +48,16 @@ pub enum EventData {
|
||||
impl Default for Header {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
term_cols: 80,
|
||||
term_rows: 24,
|
||||
term_type: None,
|
||||
term_version: None,
|
||||
term_theme: None,
|
||||
timestamp: None,
|
||||
idle_time_limit: None,
|
||||
command: None,
|
||||
title: None,
|
||||
env: None,
|
||||
theme: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,14 +74,16 @@ pub fn open<'a, R: BufRead + 'a>(reader: R) -> Result<Asciicast<'a>> {
|
||||
let mut lines = reader.lines();
|
||||
let first_line = lines.next().ok_or(anyhow!("empty file"))??;
|
||||
|
||||
if let Ok(parser) = v2::open(&first_line) {
|
||||
if let Ok(parser) = v3::open(&first_line) {
|
||||
Ok(parser.parse(lines))
|
||||
} else if let Ok(parser) = v2::open(&first_line) {
|
||||
Ok(parser.parse(lines))
|
||||
} else {
|
||||
let json = std::iter::once(Ok(first_line))
|
||||
.chain(lines)
|
||||
.collect::<io::Result<String>>()?;
|
||||
|
||||
v1::load(json)
|
||||
v1::load(json).map_err(|_| anyhow!("not a v1, v2, v3 asciicast file"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +163,7 @@ pub fn accelerate(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Asciicast, Encoder, Event, EventData, Header};
|
||||
use super::{Asciicast, Event, EventData, Header, V2Encoder};
|
||||
use crate::tty::TtyTheme;
|
||||
use anyhow::Result;
|
||||
use rgb::RGB8;
|
||||
@@ -168,8 +176,8 @@ mod tests {
|
||||
|
||||
let events = events.collect::<Result<Vec<Event>>>().unwrap();
|
||||
|
||||
assert_eq!((header.cols, header.rows), (100, 50));
|
||||
assert!(header.theme.is_none());
|
||||
assert_eq!((header.term_cols, header.term_rows), (100, 50));
|
||||
assert!(header.term_theme.is_none());
|
||||
|
||||
assert_eq!(events[0].time, 1230000);
|
||||
assert!(matches!(events[0].data, EventData::Output(ref s) if s == "hello"));
|
||||
@@ -180,7 +188,7 @@ mod tests {
|
||||
let Asciicast { header, events } = super::open_from_path("tests/casts/full.json").unwrap();
|
||||
let events = events.collect::<Result<Vec<Event>>>().unwrap();
|
||||
|
||||
assert_eq!((header.cols, header.rows), (100, 50));
|
||||
assert_eq!((header.term_cols, header.term_rows), (100, 50));
|
||||
|
||||
assert_eq!(events[0].time, 1);
|
||||
assert!(matches!(events[0].data, EventData::Output(ref s) if s == "ż"));
|
||||
@@ -198,8 +206,8 @@ mod tests {
|
||||
super::open_from_path("tests/casts/minimal.cast").unwrap();
|
||||
let events = events.collect::<Result<Vec<Event>>>().unwrap();
|
||||
|
||||
assert_eq!((header.cols, header.rows), (100, 50));
|
||||
assert!(header.theme.is_none());
|
||||
assert_eq!((header.term_cols, header.term_rows), (100, 50));
|
||||
assert!(header.term_theme.is_none());
|
||||
|
||||
assert_eq!(events[0].time, 1230000);
|
||||
assert!(matches!(events[0].data, EventData::Output(ref s) if s == "hello"));
|
||||
@@ -209,9 +217,9 @@ mod tests {
|
||||
fn open_v2_full() {
|
||||
let Asciicast { header, events } = super::open_from_path("tests/casts/full.cast").unwrap();
|
||||
let events = events.take(5).collect::<Result<Vec<Event>>>().unwrap();
|
||||
let theme = header.theme.unwrap();
|
||||
let theme = header.term_theme.unwrap();
|
||||
|
||||
assert_eq!((header.cols, header.rows), (100, 50));
|
||||
assert_eq!((header.term_cols, header.term_rows), (100, 50));
|
||||
assert_eq!(theme.fg, RGB8::new(0, 0, 0));
|
||||
assert_eq!(theme.bg, RGB8::new(0xff, 0xff, 0xff));
|
||||
assert_eq!(theme.palette[0], RGB8::new(0x24, 0x1f, 0x31));
|
||||
@@ -237,23 +245,12 @@ mod tests {
|
||||
#[test]
|
||||
fn encoder() {
|
||||
let mut data = Vec::new();
|
||||
|
||||
let header = Header {
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
timestamp: None,
|
||||
idle_time_limit: None,
|
||||
command: None,
|
||||
title: None,
|
||||
env: Default::default(),
|
||||
theme: None,
|
||||
};
|
||||
|
||||
let mut enc = Encoder::new(0);
|
||||
let header = Header::default();
|
||||
let mut enc = V2Encoder::new(0);
|
||||
data.extend(enc.header(&header));
|
||||
data.extend(enc.event(&Event::output(1000000, "hello\r\n".to_owned())));
|
||||
|
||||
let mut enc = Encoder::new(1000001);
|
||||
let mut enc = V2Encoder::new(1000001);
|
||||
data.extend(enc.event(&Event::output(1000001, "world".to_owned())));
|
||||
data.extend(enc.event(&Event::input(2000002, " ".to_owned())));
|
||||
data.extend(enc.event(&Event::resize(3000003, (100, 40))));
|
||||
@@ -284,7 +281,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn header_encoding() {
|
||||
let mut enc = Encoder::new(0);
|
||||
let mut enc = V2Encoder::new(0);
|
||||
let mut env = HashMap::new();
|
||||
env.insert("SHELL".to_owned(), "/usr/bin/fish".to_owned());
|
||||
env.insert("TERM".to_owned(), "xterm256-color".to_owned());
|
||||
@@ -313,14 +310,13 @@ mod tests {
|
||||
};
|
||||
|
||||
let header = Header {
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
timestamp: Some(1704719152),
|
||||
idle_time_limit: Some(1.5),
|
||||
command: Some("/bin/bash".to_owned()),
|
||||
title: Some("Demo".to_owned()),
|
||||
env: Some(env),
|
||||
theme: Some(theme),
|
||||
term_theme: Some(theme),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let data = enc.header(&header);
|
||||
|
||||
Reference in New Issue
Block a user