aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2019-10-27 10:45:37 -0400
committerJesse Luehrs <doy@tozt.net>2019-10-27 10:45:37 -0400
commitd0d5bfdfd2f6c0efbd9e5ddbb0e00e3a281d78c2 (patch)
tree43f5f323416922a387ce51df6f7f4e553e124bb0
parent8f13228756dea88f1b8bdeaba1b46db400bab68c (diff)
downloadteleterm-d0d5bfdfd2f6c0efbd9e5ddbb0e00e3a281d78c2.tar.gz
teleterm-d0d5bfdfd2f6c0efbd9e5ddbb0e00e3a281d78c2.zip
move ttyrec to a separate crate
-rw-r--r--Cargo.lock12
-rw-r--r--Cargo.toml1
-rw-r--r--src/cmd/play.rs9
-rw-r--r--src/cmd/record.rs22
-rw-r--r--src/error.rs6
-rw-r--r--src/main.rs1
-rw-r--r--src/ttyrec.rs215
7 files changed, 38 insertions, 228 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 5fe861c..0f4d7a3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1649,6 +1649,7 @@ dependencies = [
"tokio-pty-process-stream",
"tokio-signal",
"tokio-tls",
+ "ttyrec",
"twoway",
"url 2.1.0",
"users",
@@ -1994,6 +1995,17 @@ dependencies = [
]
[[package]]
+name = "ttyrec"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "034c0e4a24b738a650ea69bcb829388a2e72baa5e7aa1e9aa85fcd03e9f5cac6"
+dependencies = [
+ "futures",
+ "snafu",
+ "tokio",
+]
+
+[[package]]
name = "twoway"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 1e93dc7..9f44de0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -36,6 +36,7 @@ tokio = { version = "0.1.22", features = ["codec", "fs", "io", "reactor", "rt-fu
tokio-pty-process-stream = "0.1"
tokio-signal = "0.2"
tokio-tls = "0.2"
+ttyrec = "0.1"
twoway = "0.2"
url = "2"
users = "0.9"
diff --git a/src/cmd/play.rs b/src/cmd/play.rs
index d73dcb4..28d6985 100644
--- a/src/cmd/play.rs
+++ b/src/cmd/play.rs
@@ -50,7 +50,7 @@ enum FileState {
fut: tokio::fs::file::OpenFuture<String>,
},
Open {
- reader: crate::ttyrec::Reader<tokio::fs::File>,
+ reader: ttyrec::Reader<tokio::fs::File>,
},
Eof,
}
@@ -106,7 +106,7 @@ impl PlaySession {
filename: filename.to_string(),
}
}));
- let reader = crate::ttyrec::Reader::new(file);
+ let reader = ttyrec::Reader::new(file);
self.file = FileState::Open { reader };
Ok(component_future::Async::DidWork)
}
@@ -116,8 +116,9 @@ impl PlaySession {
fn poll_read_file(&mut self) -> component_future::Poll<(), Error> {
if let FileState::Open { reader } = &mut self.file {
- if let Some(frame) =
- component_future::try_ready!(reader.poll_read())
+ if let Some(frame) = component_future::try_ready!(reader
+ .poll_read()
+ .context(crate::error::ReadTtyrec))
{
self.to_write
.insert_at(frame.data, self.base_time + frame.time);
diff --git a/src/cmd/record.rs b/src/cmd/record.rs
index 46e76e1..2d9116c 100644
--- a/src/cmd/record.rs
+++ b/src/cmd/record.rs
@@ -62,7 +62,7 @@ enum FileState {
fut: tokio::fs::file::CreateFuture<String>,
},
Open {
- writer: crate::ttyrec::Writer<tokio::fs::File>,
+ writer: ttyrec::Writer<tokio::fs::File>,
},
}
@@ -141,9 +141,11 @@ impl RecordSession {
filename: filename.clone(),
}
}));
- let mut writer = crate::ttyrec::Writer::new(file);
+ let mut writer = ttyrec::Writer::new(file);
if !self.buffer.contents().is_empty() {
- writer.frame(self.buffer.contents())?;
+ writer
+ .frame(self.buffer.contents())
+ .context(crate::error::WriteTtyrec)?;
}
self.file = FileState::Open { writer };
Ok(component_future::Async::DidWork)
@@ -176,7 +178,9 @@ impl RecordSession {
tokio_pty_process_stream::Event::Output { data } => {
self.record_bytes(&data);
if let FileState::Open { writer } = &mut self.file {
- writer.frame(&data)?;
+ writer
+ .frame(&data)
+ .context(crate::error::WriteTtyrec)?;
}
}
}
@@ -231,16 +235,18 @@ impl RecordSession {
}
};
- if writer.is_empty() {
+ if writer.needs_write() {
+ component_future::try_ready!(writer
+ .poll_write()
+ .context(crate::error::WriteTtyrec));
+ Ok(component_future::Async::DidWork)
+ } else {
// finish writing to the file before actually ending
if self.done {
Ok(component_future::Async::Ready(()))
} else {
Ok(component_future::Async::NothingToDo)
}
- } else {
- component_future::try_ready!(writer.poll_write());
- Ok(component_future::Async::DidWork)
}
}
}
diff --git a/src/error.rs b/src/error.rs
index ee160ce..5fa2f4d 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -303,6 +303,9 @@ pub enum Error {
#[snafu(display("failed to read from terminal: {}", source))]
ReadTerminal { source: std::io::Error },
+ #[snafu(display("failed to read ttyrec: {}", source))]
+ ReadTtyrec { source: ttyrec::Error },
+
#[snafu(display("failed to resize pty: {}", source))]
ResizePty { source: std::io::Error },
@@ -464,6 +467,9 @@ pub enum Error {
#[snafu(display("failed to write to terminal: {}", source))]
WriteTerminalSync { source: std::io::Error },
+
+ #[snafu(display("failed to write ttyrec: {}", source))]
+ WriteTtyrec { source: ttyrec::Error },
}
pub type Result<T> = std::result::Result<T, Error>;
diff --git a/src/main.rs b/src/main.rs
index bc57f00..e0db244 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -28,7 +28,6 @@ mod resize;
mod server;
mod session_list;
mod term;
-mod ttyrec;
fn main() {
dirs::Dirs::new().create_all().unwrap();
diff --git a/src/ttyrec.rs b/src/ttyrec.rs
deleted file mode 100644
index 2ccefdc..0000000
--- a/src/ttyrec.rs
+++ /dev/null
@@ -1,215 +0,0 @@
-use crate::prelude::*;
-use std::convert::TryFrom as _;
-
-pub struct Frame {
- pub time: std::time::Duration,
- pub data: Vec<u8>,
-}
-
-impl std::convert::TryFrom<Frame> for Vec<u8> {
- type Error = Error;
-
- fn try_from(frame: Frame) -> Result<Self> {
- let secs = u32::try_from(frame.time.as_secs()).map_err(|_| {
- Error::FrameTooLong {
- input: frame.time.as_secs(),
- }
- })?;
- let micros = frame.time.subsec_micros();
- let len = u32::try_from(frame.data.len()).map_err(|_| {
- Error::FrameTooBig {
- input: frame.data.len(),
- }
- })?;
- let mut bytes = vec![];
- bytes.extend(secs.to_le_bytes().iter());
- bytes.extend(micros.to_le_bytes().iter());
- bytes.extend(len.to_le_bytes().iter());
- bytes.extend(frame.data.iter());
- Ok(bytes)
- }
-}
-
-#[repr(packed)]
-struct Header {
- secs: u32,
- micros: u32,
- len: u32,
-}
-
-impl Header {
- fn time(&self) -> std::time::Duration {
- std::time::Duration::from_micros(u64::from(
- self.secs * 1_000_000 + self.micros,
- ))
- }
-
- fn len(&self) -> usize {
- self.len as usize
- }
-}
-
-pub struct Parser {
- reading: std::collections::VecDeque<u8>,
- read_state: Option<Header>,
-}
-
-impl Parser {
- pub fn new() -> Self {
- Self {
- reading: std::collections::VecDeque::new(),
- read_state: None,
- }
- }
-
- pub fn add_bytes(&mut self, bytes: &[u8]) {
- self.reading.extend(bytes.iter());
- }
-
- pub fn next_frame(&mut self) -> Option<Frame> {
- let header = if let Some(header) = &self.read_state {
- header
- } else {
- if self.reading.len() < std::mem::size_of::<Header>() {
- return None;
- }
-
- let secs1 = self.reading.pop_front().unwrap();
- let secs2 = self.reading.pop_front().unwrap();
- let secs3 = self.reading.pop_front().unwrap();
- let secs4 = self.reading.pop_front().unwrap();
- let secs = u32::from_le_bytes([secs1, secs2, secs3, secs4]);
-
- let usecs1 = self.reading.pop_front().unwrap();
- let usecs2 = self.reading.pop_front().unwrap();
- let usecs3 = self.reading.pop_front().unwrap();
- let usecs4 = self.reading.pop_front().unwrap();
- let micros = u32::from_le_bytes([usecs1, usecs2, usecs3, usecs4]);
-
- let len1 = self.reading.pop_front().unwrap();
- let len2 = self.reading.pop_front().unwrap();
- let len3 = self.reading.pop_front().unwrap();
- let len4 = self.reading.pop_front().unwrap();
- let len = u32::from_le_bytes([len1, len2, len3, len4]);
-
- let header = Header { secs, micros, len };
- self.read_state = Some(header);
- self.read_state.as_ref().unwrap()
- };
-
- if self.reading.len() < header.len() {
- return None;
- }
-
- let mut data = vec![];
- for _ in 0..header.len() {
- data.push(self.reading.pop_front().unwrap());
- }
-
- let time = header.time();
-
- self.read_state = None;
- Some(Frame { time, data })
- }
-}
-
-pub struct Creator {
- base_time: Option<std::time::Instant>,
-}
-
-impl Creator {
- pub fn new() -> Self {
- Self { base_time: None }
- }
-
- pub fn frame(&mut self, data: &[u8]) -> Result<Vec<u8>> {
- let cur_time = std::time::Instant::now();
- let base_time = if let Some(base_time) = &self.base_time {
- base_time
- } else {
- self.base_time = Some(cur_time);
- self.base_time.as_ref().unwrap()
- };
- Vec::<u8>::try_from(Frame {
- time: cur_time - *base_time,
- data: data.to_vec(),
- })
- }
-}
-
-pub struct Reader<R> {
- reader: R,
- parser: Parser,
- read_buf: [u8; 4096],
- done_reading: bool,
-}
-
-impl<R: tokio::io::AsyncRead> Reader<R> {
- pub fn new(reader: R) -> Self {
- Self {
- reader,
- parser: Parser::new(),
- read_buf: [0; 4096],
- done_reading: false,
- }
- }
-
- pub fn poll_read(&mut self) -> futures::Poll<Option<Frame>, Error> {
- loop {
- if let Some(frame) = self.parser.next_frame() {
- return Ok(futures::Async::Ready(Some(frame)));
- } else if self.done_reading {
- return Ok(futures::Async::Ready(None));
- }
-
- let n = futures::try_ready!(self
- .reader
- .poll_read(&mut self.read_buf)
- .context(crate::error::ReadFile));
- if n > 0 {
- self.parser.add_bytes(&self.read_buf[..n]);
- } else {
- self.done_reading = true;
- }
- }
- }
-}
-
-pub struct Writer<W> {
- writer: W,
- creator: Creator,
- to_write: std::collections::VecDeque<u8>,
-}
-
-impl<W: tokio::io::AsyncWrite> Writer<W> {
- pub fn new(writer: W) -> Self {
- Self {
- writer,
- creator: Creator::new(),
- to_write: std::collections::VecDeque::new(),
- }
- }
-
- pub fn frame(&mut self, data: &[u8]) -> Result<()> {
- let bytes = self.creator.frame(data)?;
- self.to_write.extend(bytes.iter());
- Ok(())
- }
-
- pub fn poll_write(&mut self) -> futures::Poll<(), Error> {
- let (a, b) = self.to_write.as_slices();
- let buf = if a.is_empty() { b } else { a };
- let n = futures::try_ready!(self
- .writer
- .poll_write(buf)
- .context(crate::error::WriteFile));
- for _ in 0..n {
- self.to_write.pop_front();
- }
- Ok(futures::Async::Ready(()))
- }
-
- pub fn is_empty(&self) -> bool {
- self.to_write.is_empty()
- }
-}