mirror of
https://github.com/asciinema/asciinema.git
synced 2025-12-16 03:38:03 +01:00
Make Encoder return bytes instead of directly writing to io::Write impl
This commit is contained in:
5
Cargo.lock
generated
5
Cargo.lock
generated
@@ -179,12 +179,11 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "avt"
|
name = "avt"
|
||||||
version = "0.11.0"
|
version = "0.14.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0cb3c38905502a1f46c37ae2d3373c36a3aaf3cd9f7fde871984ef870f039bd6"
|
checksum = "b485f400d02970694eed10e7080f994ad82eaf56a867d6671af5d5e184ed8ee6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rgb",
|
"rgb",
|
||||||
"serde",
|
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ config = { version = "0.14.0", default-features = false, features = ["toml", "in
|
|||||||
which = "6.0.0"
|
which = "6.0.0"
|
||||||
tempfile = "3.9.0"
|
tempfile = "3.9.0"
|
||||||
scraper = { version = "0.19.0", default-features = false }
|
scraper = { version = "0.19.0", default-features = false }
|
||||||
avt = "0.11.0"
|
avt = "0.14.0"
|
||||||
axum = { version = "0.7.4", default-features = false, features = ["http1", "ws"] }
|
axum = { version = "0.7.4", default-features = false, features = ["http1", "ws"] }
|
||||||
tokio = { version = "1.35.1", features = ["full"] }
|
tokio = { version = "1.35.1", features = ["full"] }
|
||||||
futures-util = "0.3.30"
|
futures-util = "0.3.30"
|
||||||
|
|||||||
142
src/asciicast.rs
142
src/asciicast.rs
@@ -7,7 +7,7 @@ use std::collections::HashMap;
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::{self, BufRead};
|
use std::io::{self, BufRead};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
pub use v2::Writer;
|
pub use v2::Encoder;
|
||||||
|
|
||||||
pub struct Asciicast<'a> {
|
pub struct Asciicast<'a> {
|
||||||
pub header: Header,
|
pub header: Header,
|
||||||
@@ -138,12 +138,11 @@ pub fn accelerate(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{Asciicast, Event, EventData, Header, Writer};
|
use super::{Asciicast, Encoder, Event, EventData, Header};
|
||||||
use crate::tty;
|
use crate::tty;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use rgb::RGB8;
|
use rgb::RGB8;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn open_v1_minimal() {
|
fn open_v1_minimal() {
|
||||||
@@ -221,44 +220,30 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn writer() {
|
fn encoder() {
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
|
|
||||||
{
|
let header = Header {
|
||||||
let mut fw = Writer::new(&mut data, 0);
|
version: 2,
|
||||||
|
cols: 80,
|
||||||
|
rows: 24,
|
||||||
|
timestamp: None,
|
||||||
|
idle_time_limit: None,
|
||||||
|
command: None,
|
||||||
|
title: None,
|
||||||
|
env: Default::default(),
|
||||||
|
theme: None,
|
||||||
|
};
|
||||||
|
|
||||||
let header = Header {
|
let mut enc = Encoder::new(0);
|
||||||
version: 2,
|
data.extend(enc.header(&header));
|
||||||
cols: 80,
|
data.extend(enc.event(&Event::output(1000001, "hello\r\n".to_owned())));
|
||||||
rows: 24,
|
|
||||||
timestamp: None,
|
|
||||||
idle_time_limit: None,
|
|
||||||
command: None,
|
|
||||||
title: None,
|
|
||||||
env: Default::default(),
|
|
||||||
theme: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
fw.write_header(&header).unwrap();
|
let mut enc = Encoder::new(1000001);
|
||||||
|
data.extend(enc.event(&Event::output(1000001, "world".to_owned())));
|
||||||
fw.write_event(&Event::output(1000001, "hello\r\n".to_owned()))
|
data.extend(enc.event(&Event::input(2000002, " ".to_owned())));
|
||||||
.unwrap();
|
data.extend(enc.event(&Event::resize(3000003, (100, 40))));
|
||||||
}
|
data.extend(enc.event(&Event::output(4000004, "żółć".to_owned())));
|
||||||
|
|
||||||
{
|
|
||||||
let mut fw = Writer::new(&mut data, 1000001);
|
|
||||||
|
|
||||||
fw.write_event(&Event::output(1000001, "world".to_owned()))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
fw.write_event(&Event::input(2000002, " ".to_owned()))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
fw.write_event(&Event::resize(3000003, (100, 40))).unwrap();
|
|
||||||
|
|
||||||
fw.write_event(&Event::output(4000004, "żółć".to_owned()))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let lines = parse(data);
|
let lines = parse(data);
|
||||||
|
|
||||||
@@ -284,53 +269,48 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn write_header() {
|
fn header_encoding() {
|
||||||
let mut data = Vec::new();
|
let mut enc = Encoder::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());
|
||||||
|
|
||||||
{
|
let theme = tty::Theme {
|
||||||
let mut fw = Writer::new(io::Cursor::new(&mut data), 0);
|
fg: RGB8::new(0, 1, 2),
|
||||||
let mut env = HashMap::new();
|
bg: RGB8::new(0, 100, 200),
|
||||||
env.insert("SHELL".to_owned(), "/usr/bin/fish".to_owned());
|
palette: vec![
|
||||||
env.insert("TERM".to_owned(), "xterm256-color".to_owned());
|
RGB8::new(0, 0, 0),
|
||||||
|
RGB8::new(10, 11, 12),
|
||||||
|
RGB8::new(20, 21, 22),
|
||||||
|
RGB8::new(30, 31, 32),
|
||||||
|
RGB8::new(40, 41, 42),
|
||||||
|
RGB8::new(50, 51, 52),
|
||||||
|
RGB8::new(60, 61, 62),
|
||||||
|
RGB8::new(70, 71, 72),
|
||||||
|
RGB8::new(80, 81, 82),
|
||||||
|
RGB8::new(90, 91, 92),
|
||||||
|
RGB8::new(100, 101, 102),
|
||||||
|
RGB8::new(110, 111, 112),
|
||||||
|
RGB8::new(120, 121, 122),
|
||||||
|
RGB8::new(130, 131, 132),
|
||||||
|
RGB8::new(140, 141, 142),
|
||||||
|
RGB8::new(150, 151, 152),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
let theme = tty::Theme {
|
let header = Header {
|
||||||
fg: RGB8::new(0, 1, 2),
|
version: 2,
|
||||||
bg: RGB8::new(0, 100, 200),
|
cols: 80,
|
||||||
palette: vec![
|
rows: 24,
|
||||||
RGB8::new(0, 0, 0),
|
timestamp: Some(1704719152),
|
||||||
RGB8::new(10, 11, 12),
|
idle_time_limit: Some(1.5),
|
||||||
RGB8::new(20, 21, 22),
|
command: Some("/bin/bash".to_owned()),
|
||||||
RGB8::new(30, 31, 32),
|
title: Some("Demo".to_owned()),
|
||||||
RGB8::new(40, 41, 42),
|
env: Some(env),
|
||||||
RGB8::new(50, 51, 52),
|
theme: Some(theme),
|
||||||
RGB8::new(60, 61, 62),
|
};
|
||||||
RGB8::new(70, 71, 72),
|
|
||||||
RGB8::new(80, 81, 82),
|
|
||||||
RGB8::new(90, 91, 92),
|
|
||||||
RGB8::new(100, 101, 102),
|
|
||||||
RGB8::new(110, 111, 112),
|
|
||||||
RGB8::new(120, 121, 122),
|
|
||||||
RGB8::new(130, 131, 132),
|
|
||||||
RGB8::new(140, 141, 142),
|
|
||||||
RGB8::new(150, 151, 152),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
let header = Header {
|
|
||||||
version: 2,
|
|
||||||
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),
|
|
||||||
};
|
|
||||||
|
|
||||||
fw.write_header(&header).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
let data = enc.header(&header);
|
||||||
let lines = parse(data);
|
let lines = parse(data);
|
||||||
|
|
||||||
assert_eq!(lines[0]["version"], 2);
|
assert_eq!(lines[0]["version"], 2);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use anyhow::{anyhow, bail, Result};
|
|||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::{self, Write};
|
use std::io;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct V2Header {
|
struct V2Header {
|
||||||
@@ -156,30 +156,28 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Writer<W: Write> {
|
pub struct Encoder {
|
||||||
writer: io::LineWriter<W>,
|
|
||||||
time_offset: u64,
|
time_offset: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> Writer<W>
|
impl Encoder {
|
||||||
where
|
pub fn new(time_offset: u64) -> Self {
|
||||||
W: Write,
|
Self { time_offset }
|
||||||
{
|
|
||||||
pub fn new(writer: W, time_offset: u64) -> Self {
|
|
||||||
Self {
|
|
||||||
writer: io::LineWriter::new(writer),
|
|
||||||
time_offset,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_header(&mut self, header: &Header) -> io::Result<()> {
|
pub fn header(&mut self, header: &Header) -> Vec<u8> {
|
||||||
let header: V2Header = header.into();
|
let header: V2Header = header.into();
|
||||||
|
let mut data = serde_json::to_string(&header).unwrap().into_bytes();
|
||||||
|
data.push(b'\n');
|
||||||
|
|
||||||
writeln!(self.writer, "{}", serde_json::to_string(&header)?)
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_event(&mut self, event: &Event) -> io::Result<()> {
|
pub fn event(&mut self, event: &Event) -> Vec<u8> {
|
||||||
writeln!(self.writer, "{}", self.serialize_event(event)?)
|
let mut data = self.serialize_event(event).unwrap().into_bytes();
|
||||||
|
data.push(b'\n');
|
||||||
|
|
||||||
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_event(&self, event: &Event) -> Result<String, serde_json::Error> {
|
fn serialize_event(&self, event: &Event) -> Result<String, serde_json::Error> {
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ use crate::cli;
|
|||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
impl Command for cli::Cat {
|
impl Command for cli::Cat {
|
||||||
fn run(self, _config: &Config) -> Result<()> {
|
fn run(self, _config: &Config) -> Result<()> {
|
||||||
let mut writer = asciicast::Writer::new(io::stdout(), 0);
|
let mut encoder = asciicast::Encoder::new(0);
|
||||||
|
let mut stdout = io::stdout();
|
||||||
let mut time_offset: u64 = 0;
|
let mut time_offset: u64 = 0;
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
|
|
||||||
@@ -16,7 +18,7 @@ impl Command for cli::Cat {
|
|||||||
let mut time = time_offset;
|
let mut time = time_offset;
|
||||||
|
|
||||||
if first {
|
if first {
|
||||||
writer.write_header(&recording.header)?;
|
stdout.write_all(&encoder.header(&recording.header))?;
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +26,7 @@ impl Command for cli::Cat {
|
|||||||
let mut event = event?;
|
let mut event = event?;
|
||||||
time = time_offset + event.time;
|
time = time_offset + event.time;
|
||||||
event.time = time;
|
event.time = time;
|
||||||
writer.write_event(&event)?;
|
stdout.write_all(&encoder.event(&event))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
time_offset = time;
|
time_offset = time;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use super::Command;
|
|||||||
use crate::asciicast::{self, Header};
|
use crate::asciicast::{self, Header};
|
||||||
use crate::cli::{self, Format};
|
use crate::cli::{self, Format};
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::encoder::{self, EncoderExt};
|
use crate::encoder::{self, AsciicastEncoder, EncoderExt, RawEncoder, TextEncoder};
|
||||||
use crate::util;
|
use crate::util;
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
@@ -11,17 +11,16 @@ use std::path::Path;
|
|||||||
impl Command for cli::Convert {
|
impl Command for cli::Convert {
|
||||||
fn run(self, _config: &Config) -> Result<()> {
|
fn run(self, _config: &Config) -> Result<()> {
|
||||||
let path = util::get_local_path(&self.input_filename)?;
|
let path = util::get_local_path(&self.input_filename)?;
|
||||||
let input = asciicast::open_from_path(&*path)?;
|
let cast = asciicast::open_from_path(&*path)?;
|
||||||
let mut output = self.get_output(&input.header)?;
|
let mut encoder = self.get_encoder(&cast.header);
|
||||||
|
let mut file = self.open_file()?;
|
||||||
|
|
||||||
output.encode(input)
|
encoder.encode_to_file(cast, &mut file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl cli::Convert {
|
impl cli::Convert {
|
||||||
fn get_output(&self, header: &Header) -> Result<Box<dyn encoder::Encoder>> {
|
fn get_encoder(&self, header: &Header) -> Box<dyn encoder::Encoder> {
|
||||||
let file = self.open_file()?;
|
|
||||||
|
|
||||||
let format = self.format.unwrap_or_else(|| {
|
let format = self.format.unwrap_or_else(|| {
|
||||||
if self.output_filename.to_lowercase().ends_with(".txt") {
|
if self.output_filename.to_lowercase().ends_with(".txt") {
|
||||||
Format::Txt
|
Format::Txt
|
||||||
@@ -31,15 +30,9 @@ impl cli::Convert {
|
|||||||
});
|
});
|
||||||
|
|
||||||
match format {
|
match format {
|
||||||
Format::Asciicast => Ok(Box::new(encoder::AsciicastEncoder::new(
|
Format::Asciicast => Box::new(AsciicastEncoder::new(false, 0, header.into())),
|
||||||
file,
|
Format::Raw => Box::new(RawEncoder::new(false)),
|
||||||
false,
|
Format::Txt => Box::new(TextEncoder::new()),
|
||||||
0,
|
|
||||||
header.into(),
|
|
||||||
))),
|
|
||||||
|
|
||||||
Format::Raw => Ok(Box::new(encoder::RawEncoder::new(file, false))),
|
|
||||||
Format::Txt => Ok(Box::new(encoder::TextEncoder::new(file))),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ use super::Command;
|
|||||||
use crate::asciicast;
|
use crate::asciicast;
|
||||||
use crate::cli;
|
use crate::cli;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::encoder;
|
use crate::encoder::{AsciicastEncoder, Encoder, Metadata, RawEncoder, TextEncoder};
|
||||||
use crate::locale;
|
use crate::locale;
|
||||||
use crate::logger;
|
use crate::logger;
|
||||||
use crate::pty;
|
use crate::pty;
|
||||||
|
use crate::recorder::Output;
|
||||||
use crate::recorder::{self, KeyBindings};
|
use crate::recorder::{self, KeyBindings};
|
||||||
use crate::tty::{self, FixedSizeTty, Tty};
|
use crate::tty::{self, FixedSizeTty, Tty};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
@@ -13,8 +14,10 @@ use cli::Format;
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::io::{self, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process;
|
use std::process;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
impl Command for cli::Record {
|
impl Command for cli::Record {
|
||||||
fn run(mut self, config: &Config) -> Result<()> {
|
fn run(mut self, config: &Config) -> Result<()> {
|
||||||
@@ -173,17 +176,14 @@ impl cli::Record {
|
|||||||
match format {
|
match format {
|
||||||
Format::Asciicast => {
|
Format::Asciicast => {
|
||||||
let metadata = self.build_asciicast_metadata(theme, config);
|
let metadata = self.build_asciicast_metadata(theme, config);
|
||||||
|
let file = io::LineWriter::new(file);
|
||||||
|
let encoder = AsciicastEncoder::new(append, time_offset, metadata);
|
||||||
|
|
||||||
Box::new(encoder::AsciicastEncoder::new(
|
Box::new(FileOutput(file, encoder))
|
||||||
file,
|
|
||||||
append,
|
|
||||||
time_offset,
|
|
||||||
metadata,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Format::Raw => Box::new(encoder::RawEncoder::new(file, append)),
|
Format::Raw => Box::new(FileOutput(file, RawEncoder::new(append))),
|
||||||
Format::Txt => Box::new(encoder::TextEncoder::new(file)),
|
Format::Txt => Box::new(FileOutput(file, TextEncoder::new())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,11 +191,7 @@ impl cli::Record {
|
|||||||
self.command.as_ref().cloned().or(config.cmd_rec_command())
|
self.command.as_ref().cloned().or(config.cmd_rec_command())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_asciicast_metadata(
|
fn build_asciicast_metadata(&self, theme: Option<tty::Theme>, config: &Config) -> Metadata {
|
||||||
&self,
|
|
||||||
theme: Option<tty::Theme>,
|
|
||||||
config: &Config,
|
|
||||||
) -> encoder::Metadata {
|
|
||||||
let idle_time_limit = self.idle_time_limit.or(config.cmd_rec_idle_time_limit());
|
let idle_time_limit = self.idle_time_limit.or(config.cmd_rec_idle_time_limit());
|
||||||
let command = self.get_command(config);
|
let command = self.get_command(config);
|
||||||
|
|
||||||
@@ -206,7 +202,7 @@ impl cli::Record {
|
|||||||
.or(config.cmd_rec_env())
|
.or(config.cmd_rec_env())
|
||||||
.unwrap_or(String::from("TERM,SHELL"));
|
.unwrap_or(String::from("TERM,SHELL"));
|
||||||
|
|
||||||
encoder::Metadata {
|
Metadata {
|
||||||
idle_time_limit,
|
idle_time_limit,
|
||||||
command,
|
command,
|
||||||
title: self.title.clone(),
|
title: self.title.clone(),
|
||||||
@@ -216,6 +212,23 @@ impl cli::Record {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FileOutput<W: Write, E: Encoder>(W, E);
|
||||||
|
|
||||||
|
impl<W: Write, E: Encoder> Output for FileOutput<W, E> {
|
||||||
|
fn header(&mut self, time: SystemTime, tty_size: tty::TtySize) -> io::Result<()> {
|
||||||
|
let timestamp = time.duration_since(UNIX_EPOCH).unwrap().as_secs();
|
||||||
|
self.0.write_all(&self.1.start(Some(timestamp), tty_size))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, event: asciicast::Event) -> io::Result<()> {
|
||||||
|
self.0.write_all(&self.1.event(event))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.0.write_all(&self.1.finish())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_key_bindings(config: &Config) -> Result<KeyBindings> {
|
fn get_key_bindings(config: &Config) -> Result<KeyBindings> {
|
||||||
let mut keys = KeyBindings::default();
|
let mut keys = KeyBindings::default();
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
use crate::asciicast::{Event, Header, Writer};
|
use crate::asciicast::{Encoder, Event, Header};
|
||||||
use crate::tty;
|
use crate::tty;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::{self, Write};
|
|
||||||
|
|
||||||
pub struct AsciicastEncoder<W: Write> {
|
pub struct AsciicastEncoder {
|
||||||
writer: Writer<W>,
|
inner: Encoder,
|
||||||
append: bool,
|
append: bool,
|
||||||
metadata: Metadata,
|
metadata: Metadata,
|
||||||
}
|
}
|
||||||
@@ -17,13 +16,12 @@ pub struct Metadata {
|
|||||||
pub theme: Option<tty::Theme>,
|
pub theme: Option<tty::Theme>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> AsciicastEncoder<W>
|
impl AsciicastEncoder {
|
||||||
where
|
pub fn new(append: bool, time_offset: u64, metadata: Metadata) -> Self {
|
||||||
W: Write,
|
let inner = Encoder::new(time_offset);
|
||||||
{
|
|
||||||
pub fn new(writer: W, append: bool, time_offset: u64, metadata: Metadata) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
writer: Writer::new(writer, time_offset),
|
inner,
|
||||||
append,
|
append,
|
||||||
metadata,
|
metadata,
|
||||||
}
|
}
|
||||||
@@ -44,22 +42,21 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> super::Encoder for AsciicastEncoder<W>
|
impl super::Encoder for AsciicastEncoder {
|
||||||
where
|
fn start(&mut self, timestamp: Option<u64>, tty_size: tty::TtySize) -> Vec<u8> {
|
||||||
W: Write,
|
|
||||||
{
|
|
||||||
fn start(&mut self, timestamp: Option<u64>, tty_size: &tty::TtySize) -> io::Result<()> {
|
|
||||||
if self.append {
|
if self.append {
|
||||||
Ok(())
|
Vec::new()
|
||||||
} else {
|
} else {
|
||||||
let header = self.build_header(timestamp, tty_size);
|
self.inner.header(&self.build_header(timestamp, &tty_size))
|
||||||
|
|
||||||
self.writer.write_header(&header)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event(&mut self, event: &Event) -> io::Result<()> {
|
fn event(&mut self, event: Event) -> Vec<u8> {
|
||||||
self.writer.write_event(event)
|
self.inner.event(&event)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(&mut self) -> Vec<u8> {
|
||||||
|
Vec::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,67 +8,32 @@ pub use raw::RawEncoder;
|
|||||||
pub use txt::TextEncoder;
|
pub use txt::TextEncoder;
|
||||||
|
|
||||||
use crate::asciicast::Event;
|
use crate::asciicast::Event;
|
||||||
use crate::recorder;
|
|
||||||
use crate::tty;
|
use crate::tty;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::io;
|
use std::fs::File;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::io::Write;
|
||||||
|
|
||||||
pub trait Encoder {
|
pub trait Encoder {
|
||||||
fn start(&mut self, timestamp: Option<u64>, tty_size: &tty::TtySize) -> io::Result<()>;
|
fn start(&mut self, timestamp: Option<u64>, tty_size: tty::TtySize) -> Vec<u8>;
|
||||||
fn event(&mut self, event: &Event) -> io::Result<()>;
|
fn event(&mut self, event: Event) -> Vec<u8>;
|
||||||
|
fn finish(&mut self) -> Vec<u8>;
|
||||||
fn finish(&mut self) -> io::Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait EncoderExt {
|
pub trait EncoderExt {
|
||||||
fn encode(&mut self, recording: crate::asciicast::Asciicast) -> Result<()>;
|
fn encode_to_file(&mut self, cast: crate::asciicast::Asciicast, file: &mut File) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Encoder + ?Sized> EncoderExt for E {
|
impl<E: Encoder + ?Sized> EncoderExt for E {
|
||||||
fn encode(&mut self, recording: crate::asciicast::Asciicast) -> Result<()> {
|
fn encode_to_file(&mut self, cast: crate::asciicast::Asciicast, file: &mut File) -> Result<()> {
|
||||||
let tty_size = tty::TtySize(recording.header.cols, recording.header.rows);
|
let tty_size = tty::TtySize(cast.header.cols, cast.header.rows);
|
||||||
self.start(recording.header.timestamp, &tty_size)?;
|
file.write_all(&self.start(cast.header.timestamp, tty_size))?;
|
||||||
|
|
||||||
for event in recording.events {
|
for event in cast.events {
|
||||||
self.event(&event?)?;
|
file.write_all(&self.event(event?))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.finish()?;
|
file.write_all(&self.finish())?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Encoder> recorder::Output for E {
|
|
||||||
fn start(&mut self, tty_size: &tty::TtySize) -> io::Result<()> {
|
|
||||||
let timestamp = SystemTime::now()
|
|
||||||
.duration_since(UNIX_EPOCH)
|
|
||||||
.unwrap()
|
|
||||||
.as_secs();
|
|
||||||
|
|
||||||
self.start(Some(timestamp), tty_size)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn output(&mut self, time: u64, text: String) -> io::Result<()> {
|
|
||||||
self.event(&Event::output(time, text))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn input(&mut self, time: u64, text: String) -> io::Result<()> {
|
|
||||||
self.event(&Event::input(time, text))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resize(&mut self, time: u64, size: (u16, u16)) -> io::Result<()> {
|
|
||||||
self.event(&Event::resize(time, size))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn marker(&mut self, time: u64) -> io::Result<()> {
|
|
||||||
self.event(&Event::marker(time, "".to_owned()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(&mut self) -> io::Result<()> {
|
|
||||||
self.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,34 +1,36 @@
|
|||||||
use crate::asciicast::{Event, EventData};
|
use crate::asciicast::{Event, EventData};
|
||||||
use crate::tty;
|
use crate::tty;
|
||||||
use std::io::{self, Write};
|
|
||||||
|
|
||||||
pub struct RawEncoder<W> {
|
pub struct RawEncoder {
|
||||||
writer: W,
|
|
||||||
append: bool,
|
append: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> RawEncoder<W> {
|
impl RawEncoder {
|
||||||
pub fn new(writer: W, append: bool) -> Self {
|
pub fn new(append: bool) -> Self {
|
||||||
RawEncoder { writer, append }
|
RawEncoder { append }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> super::Encoder for RawEncoder<W> {
|
impl super::Encoder for RawEncoder {
|
||||||
fn start(&mut self, _timestamp: Option<u64>, tty_size: &tty::TtySize) -> io::Result<()> {
|
fn start(&mut self, _timestamp: Option<u64>, tty_size: tty::TtySize) -> Vec<u8> {
|
||||||
if self.append {
|
if self.append {
|
||||||
Ok(())
|
Vec::new()
|
||||||
} else {
|
} else {
|
||||||
write!(self.writer, "\x1b[8;{};{}t", tty_size.1, tty_size.0)
|
format!("\x1b[8;{};{}t", tty_size.1, tty_size.0).into_bytes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event(&mut self, event: &Event) -> io::Result<()> {
|
fn event(&mut self, event: Event) -> Vec<u8> {
|
||||||
if let EventData::Output(data) = &event.data {
|
if let EventData::Output(data) = event.data {
|
||||||
self.writer.write_all(data.as_bytes())
|
data.into_bytes()
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Vec::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn finish(&mut self) -> Vec<u8> {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -39,20 +41,27 @@ mod tests {
|
|||||||
use crate::tty::TtySize;
|
use crate::tty::TtySize;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encoder_impl() -> anyhow::Result<()> {
|
fn encoder() {
|
||||||
let mut out: Vec<u8> = Vec::new();
|
let mut enc = RawEncoder::new(false);
|
||||||
let mut enc = RawEncoder::new(&mut out, false);
|
|
||||||
|
|
||||||
enc.start(None, &TtySize(100, 50))?;
|
assert_eq!(
|
||||||
enc.event(&Event::output(0, "he\x1b[1mllo\r\n".to_owned()))?;
|
enc.start(None, TtySize(100, 50)),
|
||||||
enc.event(&Event::output(1, "world\r\n".to_owned()))?;
|
"\x1b[8;50;100t".as_bytes()
|
||||||
enc.event(&Event::input(2, ".".to_owned()))?;
|
);
|
||||||
enc.event(&Event::resize(3, (80, 24)))?;
|
|
||||||
enc.event(&Event::marker(4, ".".to_owned()))?;
|
|
||||||
enc.finish()?;
|
|
||||||
|
|
||||||
assert_eq!(out, b"\x1b[8;50;100the\x1b[1mllo\r\nworld\r\n");
|
assert_eq!(
|
||||||
|
enc.event(Event::output(0, "he\x1b[1mllo\r\n".to_owned())),
|
||||||
|
"he\x1b[1mllo\r\n".as_bytes()
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
assert_eq!(
|
||||||
|
enc.event(Event::output(1, "world\r\n".to_owned())),
|
||||||
|
"world\r\n".as_bytes()
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(enc.event(Event::input(2, ".".to_owned())).is_empty());
|
||||||
|
assert!(enc.event(Event::resize(3, (80, 24))).is_empty());
|
||||||
|
assert!(enc.event(Event::marker(4, ".".to_owned())).is_empty());
|
||||||
|
assert!(enc.finish().is_empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,62 +1,56 @@
|
|||||||
use crate::asciicast::{Event, EventData};
|
use crate::asciicast::{Event, EventData};
|
||||||
use crate::tty;
|
use crate::tty;
|
||||||
use avt::util::{TextCollector, TextCollectorOutput};
|
use avt::util::TextCollector;
|
||||||
use std::io::{self, Write};
|
|
||||||
|
|
||||||
pub struct TextEncoder<W: Write> {
|
pub struct TextEncoder {
|
||||||
writer: Option<W>,
|
collector: Option<TextCollector>,
|
||||||
collector: Option<TextCollector<TextWriter<W>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> TextEncoder<W> {
|
impl TextEncoder {
|
||||||
pub fn new(writer: W) -> Self {
|
pub fn new() -> Self {
|
||||||
TextEncoder {
|
TextEncoder { collector: None }
|
||||||
writer: Some(writer),
|
|
||||||
collector: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> super::Encoder for TextEncoder<W> {
|
impl super::Encoder for TextEncoder {
|
||||||
fn start(&mut self, _timestamp: Option<u64>, tty_size: &tty::TtySize) -> io::Result<()> {
|
fn start(&mut self, _timestamp: Option<u64>, tty_size: tty::TtySize) -> Vec<u8> {
|
||||||
let vt = avt::Vt::builder()
|
let vt = avt::Vt::builder()
|
||||||
.size(tty_size.0 as usize, tty_size.1 as usize)
|
.size(tty_size.0 as usize, tty_size.1 as usize)
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
.scrollback_limit(100)
|
.scrollback_limit(100)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
self.collector = Some(TextCollector::new(
|
self.collector = Some(TextCollector::new(vt));
|
||||||
vt,
|
|
||||||
TextWriter(self.writer.take().unwrap()),
|
|
||||||
));
|
|
||||||
|
|
||||||
Ok(())
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event(&mut self, event: &Event) -> io::Result<()> {
|
fn event(&mut self, event: Event) -> Vec<u8> {
|
||||||
use EventData::*;
|
use EventData::*;
|
||||||
|
|
||||||
match &event.data {
|
match &event.data {
|
||||||
Output(data) => self.collector.as_mut().unwrap().feed_str(data),
|
Output(data) => text_lines_to_bytes(self.collector.as_mut().unwrap().feed_str(data)),
|
||||||
Resize(cols, rows) => self.collector.as_mut().unwrap().resize(*cols, *rows),
|
|
||||||
_ => Ok(()),
|
Resize(cols, rows) => {
|
||||||
|
text_lines_to_bytes(self.collector.as_mut().unwrap().resize(*cols, *rows))
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(&mut self) -> io::Result<()> {
|
fn finish(&mut self) -> Vec<u8> {
|
||||||
self.collector.as_mut().unwrap().flush()
|
text_lines_to_bytes(self.collector.take().unwrap().flush().iter())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TextWriter<W: Write>(W);
|
fn text_lines_to_bytes<S: AsRef<str>>(lines: impl Iterator<Item = S>) -> Vec<u8> {
|
||||||
|
lines.fold(Vec::new(), |mut bytes, line| {
|
||||||
|
bytes.extend_from_slice(line.as_ref().as_bytes());
|
||||||
|
bytes.push(b'\n');
|
||||||
|
|
||||||
impl<W: Write> TextCollectorOutput for TextWriter<W> {
|
bytes
|
||||||
type Error = io::Error;
|
})
|
||||||
|
|
||||||
fn push(&mut self, line: String) -> Result<(), Self::Error> {
|
|
||||||
self.0.write_all(line.as_bytes())?;
|
|
||||||
self.0.write_all(b"\n")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -67,17 +61,19 @@ mod tests {
|
|||||||
use crate::tty::TtySize;
|
use crate::tty::TtySize;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encoder_impl() -> anyhow::Result<()> {
|
fn encoder() {
|
||||||
let mut out: Vec<u8> = Vec::new();
|
let mut enc = TextEncoder::new();
|
||||||
let mut enc = TextEncoder::new(&mut out);
|
|
||||||
|
|
||||||
enc.start(None, &TtySize(3, 1))?;
|
assert!(enc.start(None, TtySize(3, 1)).is_empty());
|
||||||
enc.event(&Event::output(0, "he\x1b[1mllo\r\n".to_owned()))?;
|
|
||||||
enc.event(&Event::output(1, "world\r\n".to_owned()))?;
|
|
||||||
enc.finish()?;
|
|
||||||
|
|
||||||
assert_eq!(out, b"hello\nworld\n");
|
assert!(enc
|
||||||
|
.event(Event::output(0, "he\x1b[1mllo\r\n".to_owned()))
|
||||||
|
.is_empty());
|
||||||
|
|
||||||
Ok(())
|
assert!(enc
|
||||||
|
.event(Event::output(1, "world\r\n".to_owned()))
|
||||||
|
.is_empty());
|
||||||
|
|
||||||
|
assert_eq!(enc.finish(), "hello\nworld\n".as_bytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ use std::time::{Duration, Instant};
|
|||||||
type ExtraEnv = HashMap<String, String>;
|
type ExtraEnv = HashMap<String, String>;
|
||||||
|
|
||||||
pub trait Handler {
|
pub trait Handler {
|
||||||
fn start(&mut self, epoch: Instant, tty_size: TtySize);
|
fn start(&mut self, tty_size: TtySize);
|
||||||
fn output(&mut self, time: Duration, data: &[u8]) -> bool;
|
fn output(&mut self, time: Duration, data: &[u8]) -> bool;
|
||||||
fn input(&mut self, time: Duration, data: &[u8]) -> bool;
|
fn input(&mut self, time: Duration, data: &[u8]) -> bool;
|
||||||
fn resize(&mut self, time: Duration, tty_size: TtySize) -> bool;
|
fn resize(&mut self, time: Duration, tty_size: TtySize) -> bool;
|
||||||
@@ -37,7 +37,7 @@ pub fn exec<S: AsRef<str>, T: Tty + ?Sized, H: Handler>(
|
|||||||
) -> Result<i32> {
|
) -> Result<i32> {
|
||||||
let winsize = tty.get_size();
|
let winsize = tty.get_size();
|
||||||
let epoch = Instant::now();
|
let epoch = Instant::now();
|
||||||
handler.start(epoch, winsize.into());
|
handler.start(winsize.into());
|
||||||
let result = unsafe { pty::forkpty(Some(&winsize), None) }?;
|
let result = unsafe { pty::forkpty(Some(&winsize), None) }?;
|
||||||
|
|
||||||
match result.fork_result {
|
match result.fork_result {
|
||||||
@@ -378,7 +378,7 @@ mod tests {
|
|||||||
use super::Handler;
|
use super::Handler;
|
||||||
use crate::pty::ExtraEnv;
|
use crate::pty::ExtraEnv;
|
||||||
use crate::tty::{FixedSizeTty, NullTty, TtySize};
|
use crate::tty::{FixedSizeTty, NullTty, TtySize};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct TestHandler {
|
struct TestHandler {
|
||||||
@@ -387,7 +387,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Handler for TestHandler {
|
impl Handler for TestHandler {
|
||||||
fn start(&mut self, _epoch: Instant, tty_size: TtySize) {
|
fn start(&mut self, tty_size: TtySize) {
|
||||||
self.tty_size = Some(tty_size);
|
self.tty_size = Some(tty_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::asciicast::Event;
|
||||||
use crate::config::Key;
|
use crate::config::Key;
|
||||||
use crate::notifier::Notifier;
|
use crate::notifier::Notifier;
|
||||||
use crate::pty;
|
use crate::pty;
|
||||||
@@ -6,7 +7,7 @@ use crate::util;
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
pub struct Recorder {
|
pub struct Recorder {
|
||||||
output: Option<Box<dyn Output + Send>>,
|
output: Option<Box<dyn Output + Send>>,
|
||||||
@@ -22,15 +23,9 @@ pub struct Recorder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait Output {
|
pub trait Output {
|
||||||
fn start(&mut self, tty_size: &tty::TtySize) -> io::Result<()>;
|
fn header(&mut self, time: SystemTime, tty_size: tty::TtySize) -> io::Result<()>;
|
||||||
fn output(&mut self, time: u64, text: String) -> io::Result<()>;
|
fn event(&mut self, event: Event) -> io::Result<()>;
|
||||||
fn input(&mut self, time: u64, text: String) -> io::Result<()>;
|
fn flush(&mut self) -> io::Result<()>;
|
||||||
fn resize(&mut self, time: u64, size: (u16, u16)) -> io::Result<()>;
|
|
||||||
fn marker(&mut self, time: u64) -> io::Result<()>;
|
|
||||||
|
|
||||||
fn finish(&mut self) -> io::Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Message {
|
enum Message {
|
||||||
@@ -82,9 +77,9 @@ impl Recorder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl pty::Handler for Recorder {
|
impl pty::Handler for Recorder {
|
||||||
fn start(&mut self, _epoch: Instant, tty_size: tty::TtySize) {
|
fn start(&mut self, tty_size: tty::TtySize) {
|
||||||
let mut output = self.output.take().unwrap();
|
let mut output = self.output.take().unwrap();
|
||||||
let _ = output.start(&tty_size);
|
let _ = output.header(SystemTime::now(), tty_size);
|
||||||
let receiver = self.receiver.take().unwrap();
|
let receiver = self.receiver.take().unwrap();
|
||||||
let mut notifier = self.notifier.take().unwrap();
|
let mut notifier = self.notifier.take().unwrap();
|
||||||
|
|
||||||
@@ -100,7 +95,7 @@ impl pty::Handler for Recorder {
|
|||||||
let text = output_decoder.feed(&data);
|
let text = output_decoder.feed(&data);
|
||||||
|
|
||||||
if !text.is_empty() {
|
if !text.is_empty() {
|
||||||
let _ = output.output(time, text);
|
let _ = output.event(Event::output(time, text));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,19 +103,19 @@ impl pty::Handler for Recorder {
|
|||||||
let text = input_decoder.feed(&data);
|
let text = input_decoder.feed(&data);
|
||||||
|
|
||||||
if !text.is_empty() {
|
if !text.is_empty() {
|
||||||
let _ = output.input(time, text);
|
let _ = output.event(Event::input(time, text));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Resize(time, new_tty_size) => {
|
Resize(time, new_tty_size) => {
|
||||||
if new_tty_size != last_tty_size {
|
if new_tty_size != last_tty_size {
|
||||||
let _ = output.resize(time, new_tty_size.into());
|
let _ = output.event(Event::resize(time, new_tty_size.into()));
|
||||||
last_tty_size = new_tty_size;
|
last_tty_size = new_tty_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Marker(time) => {
|
Marker(time) => {
|
||||||
let _ = output.marker(time);
|
let _ = output.event(Event::marker(time, String::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Notification(text) => {
|
Notification(text) => {
|
||||||
@@ -129,7 +124,7 @@ impl pty::Handler for Recorder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = output.finish();
|
let _ = output.flush();
|
||||||
});
|
});
|
||||||
|
|
||||||
self.handle = Some(util::JoinHandle::new(handle));
|
self.handle = Some(util::JoinHandle::new(handle));
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ use crate::util;
|
|||||||
use std::net;
|
use std::net;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::time::Instant;
|
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
@@ -83,7 +82,7 @@ impl Streamer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl pty::Handler for Streamer {
|
impl pty::Handler for Streamer {
|
||||||
fn start(&mut self, _epoch: Instant, tty_size: tty::TtySize) {
|
fn start(&mut self, tty_size: tty::TtySize) {
|
||||||
let pty_rx = self.pty_rx.take().unwrap();
|
let pty_rx = self.pty_rx.take().unwrap();
|
||||||
let (clients_tx, mut clients_rx) = mpsc::channel(1);
|
let (clients_tx, mut clients_rx) = mpsc::channel(1);
|
||||||
let shutdown_token = tokio_util::sync::CancellationToken::new();
|
let shutdown_token = tokio_util::sync::CancellationToken::new();
|
||||||
|
|||||||
Reference in New Issue
Block a user