aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2019-10-25 06:34:48 -0400
committerJesse Luehrs <doy@tozt.net>2019-10-25 06:59:12 -0400
commit05cefcdf32b2d3dc9a2cf3f9b391bfda785bd801 (patch)
tree6fe31dc6961c4ef71da455d572a6663b816879a2
parentc8c0dbaba82bd53b7d311a7ba1fe97474e2f398e (diff)
downloadttyrec-05cefcdf32b2d3dc9a2cf3f9b391bfda785bd801.tar.gz
ttyrec-05cefcdf32b2d3dc9a2cf3f9b391bfda785bd801.zip
break it up into files
-rw-r--r--src/creator.rs31
-rw-r--r--src/error.rs17
-rw-r--r--src/frame.rs28
-rw-r--r--src/lib.rs256
-rw-r--r--src/parser.rs88
-rw-r--r--src/reader.rs41
-rw-r--r--src/writer.rs40
7 files changed, 257 insertions, 244 deletions
diff --git a/src/creator.rs b/src/creator.rs
new file mode 100644
index 0000000..bda3719
--- /dev/null
+++ b/src/creator.rs
@@ -0,0 +1,31 @@
+use std::convert::TryFrom as _;
+
+pub struct Creator {
+ base_time: Option<std::time::Instant>,
+}
+
+impl Creator {
+ pub fn new() -> Self {
+ Default::default()
+ }
+
+ pub fn frame(&mut self, data: &[u8]) -> crate::error::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(crate::frame::Frame {
+ time: cur_time - *base_time,
+ data: data.to_vec(),
+ })
+ }
+}
+
+impl Default for Creator {
+ fn default() -> Self {
+ Self { base_time: None }
+ }
+}
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..923ad4f
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,17 @@
+#[derive(Debug, snafu::Snafu)]
+#[snafu(visibility(pub))]
+pub enum Error {
+ #[snafu(display("failed to create ttyrec frame: got {} bytes of data, but ttyrec frames can be at most {} bytes", input, u32::max_value()))]
+ FrameTooBig { input: usize },
+
+ #[snafu(display("failed to create ttyrec frame: got {} seconds, but ttyrecs can be at most {} seconds", input, u32::max_value()))]
+ FrameTooLong { input: u64 },
+
+ #[snafu(display("failed to read from file: {}", source))]
+ ReadFile { source: tokio::io::Error },
+
+ #[snafu(display("failed to write to file: {}", source))]
+ WriteFile { source: tokio::io::Error },
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
diff --git a/src/frame.rs b/src/frame.rs
new file mode 100644
index 0000000..f4e8099
--- /dev/null
+++ b/src/frame.rs
@@ -0,0 +1,28 @@
+pub struct Frame {
+ pub time: std::time::Duration,
+ pub data: Vec<u8>,
+}
+
+impl std::convert::TryFrom<Frame> for Vec<u8> {
+ type Error = crate::error::Error;
+
+ fn try_from(frame: Frame) -> crate::error::Result<Self> {
+ let secs = u32::try_from(frame.time.as_secs()).map_err(|_| {
+ crate::error::Error::FrameTooLong {
+ input: frame.time.as_secs(),
+ }
+ })?;
+ let micros = frame.time.subsec_micros();
+ let len = u32::try_from(frame.data.len()).map_err(|_| {
+ crate::error::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)
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 9e7c2da..7324edd 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,244 +1,12 @@
-use snafu::ResultExt as _;
-use std::convert::TryFrom as _;
-
-#[derive(Debug, snafu::Snafu)]
-pub enum Error {
- #[snafu(display("failed to create ttyrec frame: got {} bytes of data, but ttyrec frames can be at most {} bytes", input, u32::max_value()))]
- FrameTooBig { input: usize },
-
- #[snafu(display("failed to create ttyrec frame: got {} seconds, but ttyrecs can be at most {} seconds", input, u32::max_value()))]
- FrameTooLong { input: u64 },
-
- #[snafu(display("failed to read from file: {}", source))]
- ReadFile { source: tokio::io::Error },
-
- #[snafu(display("failed to write to file: {}", source))]
- WriteFile { source: tokio::io::Error },
-}
-
-pub type Result<T> = std::result::Result<T, Error>;
-
-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 {
- Default::default()
- }
-
- 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 })
- }
-}
-
-impl Default for Parser {
- fn default() -> Self {
- Self {
- reading: std::collections::VecDeque::new(),
- read_state: None,
- }
- }
-}
-
-pub struct Creator {
- base_time: Option<std::time::Instant>,
-}
-
-impl Creator {
- pub fn new() -> Self {
- Default::default()
- }
-
- 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(),
- })
- }
-}
-
-impl Default for Creator {
- fn default() -> Self {
- Self { base_time: None }
- }
-}
-
-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(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(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()
- }
-}
+mod creator;
+pub use creator::Creator;
+mod error;
+pub use error::Error;
+mod frame;
+pub use frame::Frame;
+mod parser;
+pub use parser::Parser;
+mod reader;
+pub use reader::Reader;
+mod writer;
+pub use writer::Writer;
diff --git a/src/parser.rs b/src/parser.rs
new file mode 100644
index 0000000..89c88ab
--- /dev/null
+++ b/src/parser.rs
@@ -0,0 +1,88 @@
+#[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 {
+ Default::default()
+ }
+
+ pub fn add_bytes(&mut self, bytes: &[u8]) {
+ self.reading.extend(bytes.iter());
+ }
+
+ pub fn next_frame(&mut self) -> Option<crate::frame::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(crate::frame::Frame { time, data })
+ }
+}
+
+impl Default for Parser {
+ fn default() -> Self {
+ Self {
+ reading: std::collections::VecDeque::new(),
+ read_state: None,
+ }
+ }
+}
diff --git a/src/reader.rs b/src/reader.rs
new file mode 100644
index 0000000..4d122f9
--- /dev/null
+++ b/src/reader.rs
@@ -0,0 +1,41 @@
+use snafu::ResultExt as _;
+
+pub struct Reader<R> {
+ reader: R,
+ parser: crate::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: crate::parser::Parser::new(),
+ read_buf: [0; 4096],
+ done_reading: false,
+ }
+ }
+
+ pub fn poll_read(
+ &mut self,
+ ) -> futures::Poll<Option<crate::frame::Frame>, crate::error::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;
+ }
+ }
+ }
+}
diff --git a/src/writer.rs b/src/writer.rs
new file mode 100644
index 0000000..7d2ecb4
--- /dev/null
+++ b/src/writer.rs
@@ -0,0 +1,40 @@
+use snafu::ResultExt as _;
+
+pub struct Writer<W> {
+ writer: W,
+ creator: crate::creator::Creator,
+ to_write: std::collections::VecDeque<u8>,
+}
+
+impl<W: tokio::io::AsyncWrite> Writer<W> {
+ pub fn new(writer: W) -> Self {
+ Self {
+ writer,
+ creator: crate::creator::Creator::new(),
+ to_write: std::collections::VecDeque::new(),
+ }
+ }
+
+ pub fn frame(&mut self, data: &[u8]) -> crate::error::Result<()> {
+ let bytes = self.creator.frame(data)?;
+ self.to_write.extend(bytes.iter());
+ Ok(())
+ }
+
+ pub fn poll_write(&mut self) -> futures::Poll<(), crate::error::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()
+ }
+}