mirror of
https://github.com/asciinema/asciinema.git
synced 2025-12-15 19:28:00 +01:00
Implement asciicast v2 writer
This commit is contained in:
142
Cargo.lock
generated
142
Cargo.lock
generated
@@ -15,6 +15,9 @@ dependencies = [
|
||||
"anyhow",
|
||||
"mio",
|
||||
"nix",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"termion",
|
||||
]
|
||||
|
||||
@@ -36,12 +39,40 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
@@ -77,6 +108,24 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
@@ -86,13 +135,96 @@ dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f"
|
||||
dependencies = [
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.189"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.189"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"redox_syscall 0.3.5",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -103,10 +235,16 @@ checksum = "659c1f379f3408c7e5e84c7d0da6d93404e3800b6b9d063ba24436419302ec90"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"numtoa",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.2.16",
|
||||
"redox_termios",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
|
||||
@@ -15,3 +15,8 @@ anyhow = "1.0.75"
|
||||
nix = { version = "0.27", features = [ "fs", "term", "process", "signal" ] }
|
||||
mio = { version ="0.8", features = ["os-poll", "os-ext"] }
|
||||
termion = "2.0.1"
|
||||
serde = { version = "1.0.189", features = ["derive"] }
|
||||
serde_json = "1.0.107"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.8.0"
|
||||
|
||||
283
src/asciicast.rs
Normal file
283
src/asciicast.rs
Normal file
@@ -0,0 +1,283 @@
|
||||
use anyhow::bail;
|
||||
use serde::Deserialize;
|
||||
use std::fmt::{self, Display};
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::BufRead;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
pub struct Writer {
|
||||
file: fs::File,
|
||||
time_offset: f64,
|
||||
}
|
||||
|
||||
pub struct Header {
|
||||
pub terminal_size: (usize, usize),
|
||||
pub idle_time_limit: Option<f64>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct V2Header {
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
pub idle_time_limit: Option<f64>,
|
||||
}
|
||||
|
||||
pub struct Event {
|
||||
pub time: f64,
|
||||
pub code: EventCode,
|
||||
pub data: String,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub enum EventCode {
|
||||
Output,
|
||||
Input,
|
||||
Resize,
|
||||
Marker,
|
||||
Other(char),
|
||||
}
|
||||
|
||||
pub fn open<R: BufRead>(
|
||||
reader: R,
|
||||
) -> anyhow::Result<(Header, impl Iterator<Item = anyhow::Result<Event>>)> {
|
||||
let mut lines = reader.lines();
|
||||
let first_line = lines.next().ok_or(anyhow::anyhow!("empty"))??;
|
||||
let v2_header: V2Header = serde_json::from_str(&first_line)?;
|
||||
let header: Header = v2_header.into();
|
||||
|
||||
let events = lines
|
||||
.filter(|l| l.as_ref().map_or(true, |l| !l.is_empty()))
|
||||
.enumerate()
|
||||
.map(|(i, l)| l.map(|l| parse_event(l, i + 2))?);
|
||||
|
||||
Ok((header, events))
|
||||
}
|
||||
|
||||
fn parse_event(line: String, i: usize) -> anyhow::Result<Event> {
|
||||
use EventCode::*;
|
||||
|
||||
let value: serde_json::Value = serde_json::from_str(&line)?;
|
||||
|
||||
let time = value[0]
|
||||
.as_f64()
|
||||
.ok_or(anyhow::anyhow!("line {}: invalid event time", i))?;
|
||||
|
||||
let code = match value[1].as_str() {
|
||||
Some("o") => Output,
|
||||
Some("i") => Input,
|
||||
Some("r") => Resize,
|
||||
Some("m") => Marker,
|
||||
Some(s) if !s.is_empty() => Other(s.chars().next().unwrap()),
|
||||
Some(_) => bail!("line {}: missing event code", i),
|
||||
None => bail!("line {}: event code must be a string", i),
|
||||
};
|
||||
|
||||
let data = match value[2].as_str() {
|
||||
Some(data) => data.to_owned(),
|
||||
None => bail!("line {}: event data must be a string", i),
|
||||
};
|
||||
|
||||
Ok(Event { time, code, data })
|
||||
}
|
||||
|
||||
pub fn write_header<W: Write>(sink: &mut W, header: &Header) -> io::Result<()> {
|
||||
writeln!(sink, "{}", serde_json::to_string(header)?)
|
||||
}
|
||||
|
||||
pub fn write_event<W: Write>(sink: &mut W, event: &Event) -> io::Result<()> {
|
||||
writeln!(sink, "{}", serde_json::to_string(event)?)
|
||||
}
|
||||
|
||||
pub fn get_duration<S: AsRef<Path>>(path: S) -> anyhow::Result<f64> {
|
||||
let file = fs::File::open(path)?;
|
||||
let reader = io::BufReader::new(file);
|
||||
let (_header, events) = open(reader)?;
|
||||
let time = events.last().map_or(Ok(0.0), |e| e.map(|e| e.time))?;
|
||||
|
||||
Ok(time)
|
||||
}
|
||||
|
||||
impl Writer {
|
||||
pub fn new<S: AsRef<Path>>(path: S, append: bool) -> anyhow::Result<Self> {
|
||||
if append {
|
||||
Self::append(path)
|
||||
} else {
|
||||
Self::create(path)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create<S: AsRef<Path>>(path: S) -> anyhow::Result<Self> {
|
||||
let file = fs::OpenOptions::new()
|
||||
.create_new(true)
|
||||
.write(true)
|
||||
.open(path)?;
|
||||
|
||||
Ok(Self {
|
||||
file,
|
||||
time_offset: 0.0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn append<S: AsRef<Path>>(path: S) -> anyhow::Result<Self> {
|
||||
let time_offset = get_duration(&path)?;
|
||||
let file = fs::OpenOptions::new().append(true).open(path)?;
|
||||
|
||||
Ok(Self { file, time_offset })
|
||||
}
|
||||
|
||||
pub fn write_header(&mut self, header: &Header) -> io::Result<()> {
|
||||
if self.time_offset == 0.0 {
|
||||
write_header(&mut self.file, header)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_event(&mut self, mut event: Event) -> io::Result<()> {
|
||||
event.time += self.time_offset;
|
||||
|
||||
write_event(&mut self.file, &event)
|
||||
}
|
||||
}
|
||||
|
||||
impl Event {
|
||||
pub fn output(time: f64, data: &[u8]) -> Self {
|
||||
Event {
|
||||
time,
|
||||
code: EventCode::Output,
|
||||
data: String::from_utf8_lossy(data).to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input(time: f64, data: &[u8]) -> Self {
|
||||
Event {
|
||||
time,
|
||||
code: EventCode::Input,
|
||||
data: String::from_utf8_lossy(data).to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for EventCode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
|
||||
use EventCode::*;
|
||||
|
||||
match self {
|
||||
Output => f.write_str("o"),
|
||||
Input => f.write_str("i"),
|
||||
Resize => f.write_str("r"),
|
||||
Marker => f.write_str("m"),
|
||||
Other(t) => f.write_str(&t.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for Header {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
use serde::ser::SerializeMap;
|
||||
let mut map = serializer.serialize_map(Some(3))?;
|
||||
map.serialize_entry("version", &2)?;
|
||||
map.serialize_entry("width", &self.terminal_size.0)?;
|
||||
map.serialize_entry("height", &self.terminal_size.1)?;
|
||||
// TODO idle_time_limit
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for Event {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
use serde::ser::SerializeTuple;
|
||||
let mut tup = serializer.serialize_tuple(3)?;
|
||||
tup.serialize_element(&self.time)?;
|
||||
tup.serialize_element(&self.code.to_string())?;
|
||||
tup.serialize_element(&self.data)?;
|
||||
tup.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<V2Header> for Header {
|
||||
fn from(v2: V2Header) -> Self {
|
||||
Self {
|
||||
terminal_size: (v2.width, v2.height),
|
||||
idle_time_limit: v2.idle_time_limit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Event, EventCode, Header, Writer};
|
||||
use std::fs::{self, File};
|
||||
use std::io;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[test]
|
||||
fn open() {
|
||||
let file = File::open("tests/demo.cast").unwrap();
|
||||
let (header, events) = super::open(io::BufReader::new(file)).unwrap();
|
||||
|
||||
let events = events
|
||||
.take(7)
|
||||
.collect::<anyhow::Result<Vec<Event>>>()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(header.terminal_size, (75, 18));
|
||||
|
||||
assert_eq!(events[1].time, 0.100989);
|
||||
assert_eq!(events[1].code, EventCode::Output);
|
||||
assert_eq!(events[1].data, "\u{1b}[?2004h");
|
||||
|
||||
assert_eq!(events[5].time, 1.511526);
|
||||
assert_eq!(events[5].code, EventCode::Input);
|
||||
assert_eq!(events[5].data, "v");
|
||||
|
||||
assert_eq!(events[6].time, 1.511937);
|
||||
assert_eq!(events[6].code, EventCode::Output);
|
||||
assert_eq!(events[6].data, "v");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn writer() {
|
||||
let tmp_dir = tempdir().unwrap();
|
||||
let tmp_path = tmp_dir.path().join("test.cast");
|
||||
|
||||
{
|
||||
let header = Header {
|
||||
terminal_size: (80, 24),
|
||||
idle_time_limit: None,
|
||||
};
|
||||
|
||||
let mut fw = Writer::create(&tmp_path).unwrap();
|
||||
|
||||
fw.write_header(&header).unwrap();
|
||||
|
||||
fw.write_event(Event {
|
||||
time: 1.0,
|
||||
code: EventCode::Output,
|
||||
data: "hello\r\n".to_owned(),
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
{
|
||||
let mut fw = Writer::append(&tmp_path).unwrap();
|
||||
|
||||
fw.write_event(Event {
|
||||
time: 1.0,
|
||||
code: EventCode::Output,
|
||||
data: "world".to_owned(),
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(fs::read_to_string(tmp_path).unwrap(), "{\"version\":2,\"width\":80,\"height\":24}\n[1.0,\"o\",\"hello\\r\\n\"]\n[2.0,\"o\",\"world\"]\n");
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
mod asciicast;
|
||||
mod pty;
|
||||
mod recorder;
|
||||
use anyhow::{anyhow, Result};
|
||||
@@ -8,7 +9,7 @@ fn main() -> Result<()> {
|
||||
.nth(1)
|
||||
.ok_or(anyhow!("output filename missing"))?;
|
||||
|
||||
let mut recorder = recorder::new(path, recorder::Format::Raw, false, true)?;
|
||||
let mut recorder = recorder::new(path, recorder::Format::Asciicast, false, true)?;
|
||||
pty::exec(&["/bin/bash"], &mut recorder)?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
use crate::asciicast;
|
||||
use crate::pty;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{self, Write};
|
||||
use std::time;
|
||||
|
||||
pub struct Recorder {
|
||||
writer: Box<dyn FileWriter>,
|
||||
record_input: bool,
|
||||
start_time: time::Instant,
|
||||
}
|
||||
|
||||
trait FileWriter {
|
||||
@@ -18,11 +21,6 @@ pub enum Format {
|
||||
Raw,
|
||||
}
|
||||
|
||||
struct AsciicastWriter {
|
||||
file: File,
|
||||
append: bool,
|
||||
}
|
||||
|
||||
struct RawWriter {
|
||||
file: File,
|
||||
append: bool,
|
||||
@@ -33,25 +31,62 @@ pub fn new<S: Into<String>>(
|
||||
format: Format,
|
||||
append: bool,
|
||||
record_input: bool,
|
||||
) -> io::Result<Recorder> {
|
||||
) -> anyhow::Result<Recorder> {
|
||||
let path = path.into();
|
||||
|
||||
let writer: Box<dyn FileWriter> = match format {
|
||||
Format::Asciicast => Box::new(AsciicastWriter::new(path, append)?),
|
||||
Format::Asciicast => Box::new(asciicast::Writer::new(path, append)?),
|
||||
Format::Raw => Box::new(RawWriter::new(path, append)?),
|
||||
};
|
||||
|
||||
Ok(Recorder {
|
||||
writer,
|
||||
record_input,
|
||||
start_time: time::Instant::now(),
|
||||
})
|
||||
}
|
||||
|
||||
impl AsciicastWriter {
|
||||
fn new(path: String, append: bool) -> io::Result<Self> {
|
||||
let file = File::create(path)?;
|
||||
impl Recorder {
|
||||
fn elapsed_time(&self) -> f64 {
|
||||
self.start_time.elapsed().as_secs_f64()
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self { file, append })
|
||||
impl pty::Recorder for Recorder {
|
||||
fn start(&mut self, size: (u16, u16)) -> io::Result<()> {
|
||||
self.start_time = time::Instant::now();
|
||||
self.writer.header(size)
|
||||
}
|
||||
|
||||
fn output(&mut self, data: &[u8]) {
|
||||
let _ = self.writer.output(self.elapsed_time(), data);
|
||||
// TODO use notifier for error reporting
|
||||
}
|
||||
|
||||
fn input(&mut self, data: &[u8]) {
|
||||
if self.record_input {
|
||||
let _ = self.writer.input(self.elapsed_time(), data);
|
||||
// TODO use notifier for error reporting
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FileWriter for asciicast::Writer {
|
||||
fn header(&mut self, size: (u16, u16)) -> io::Result<()> {
|
||||
let header = asciicast::Header {
|
||||
terminal_size: (size.0 as usize, size.1 as usize),
|
||||
idle_time_limit: None,
|
||||
};
|
||||
|
||||
self.write_header(&header)
|
||||
}
|
||||
|
||||
fn output(&mut self, time: f64, data: &[u8]) -> io::Result<()> {
|
||||
self.write_event(asciicast::Event::output(time, data))
|
||||
}
|
||||
|
||||
fn input(&mut self, time: f64, data: &[u8]) -> io::Result<()> {
|
||||
self.write_event(asciicast::Event::input(time, data))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,20 +106,6 @@ impl RawWriter {
|
||||
}
|
||||
}
|
||||
|
||||
impl FileWriter for AsciicastWriter {
|
||||
fn header(&mut self, size: (u16, u16)) -> io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn output(&mut self, time: f64, data: &[u8]) -> io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn input(&mut self, time: f64, data: &[u8]) -> io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl FileWriter for RawWriter {
|
||||
fn header(&mut self, size: (u16, u16)) -> io::Result<()> {
|
||||
if self.append {
|
||||
@@ -102,19 +123,3 @@ impl FileWriter for RawWriter {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl pty::Recorder for Recorder {
|
||||
fn start(&mut self, size: (u16, u16)) -> io::Result<()> {
|
||||
self.writer.header(size)
|
||||
}
|
||||
|
||||
fn output(&mut self, data: &[u8]) {
|
||||
let _ = self.writer.output(0.0, data);
|
||||
}
|
||||
|
||||
fn input(&mut self, data: &[u8]) {
|
||||
if self.record_input {
|
||||
let _ = self.writer.input(0.0, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user