aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2021-12-03 21:28:44 -0500
committerJesse Luehrs <doy@tozt.net>2021-12-04 01:58:12 -0500
commit9d07ac10cf7ec1278dd90ae8c4fe73cbd80c3fd5 (patch)
treef13aa19d200087b392e6481da97fdeb30dfd3bae /src
parent8a98d4fee2172d5ac53e74bcc95cd39aa68492a3 (diff)
downloadttyrec-9d07ac10cf7ec1278dd90ae8c4fe73cbd80c3fd5.tar.gz
ttyrec-9d07ac10cf7ec1278dd90ae8c4fe73cbd80c3fd5.zip
reintroduce readers and writers with a new api
Diffstat (limited to 'src')
-rw-r--r--src/blocking/mod.rs4
-rw-r--r--src/blocking/reader.rs46
-rw-r--r--src/blocking/writer.rs43
-rw-r--r--src/error.rs12
-rw-r--r--src/lib.rs11
-rw-r--r--src/reader.rs49
-rw-r--r--src/writer.rs46
7 files changed, 210 insertions, 1 deletions
diff --git a/src/blocking/mod.rs b/src/blocking/mod.rs
new file mode 100644
index 0000000..1ffaccd
--- /dev/null
+++ b/src/blocking/mod.rs
@@ -0,0 +1,4 @@
+pub mod reader;
+pub use reader::Reader;
+pub mod writer;
+pub use writer::Writer;
diff --git a/src/blocking/reader.rs b/src/blocking/reader.rs
new file mode 100644
index 0000000..86c348d
--- /dev/null
+++ b/src/blocking/reader.rs
@@ -0,0 +1,46 @@
+/// Reads ttyrec frames from a `std::io::Read` instance.
+pub struct Reader<T: std::io::Read> {
+ input: T,
+ parser: crate::Parser,
+ buf: [u8; 4096],
+}
+
+impl<T: std::io::Read> Reader<T> {
+ /// Creates a new `Reader` from a `std::io::Read` instance.
+ pub fn new(input: T) -> Self {
+ Self {
+ input,
+ parser: crate::Parser::new(),
+ buf: [0; 4096],
+ }
+ }
+
+ /// Returns the next parsed frame from the input stream.
+ ///
+ /// # Errors
+ /// * `crate::Error::EOF`: The input stream has been closed.
+ /// * `crate::Error::Read`: There was an error reading from the input
+ /// stream.
+ pub fn read_frame(&mut self) -> crate::Result<crate::Frame> {
+ loop {
+ if let Some(frame) = self.parser.next_frame() {
+ return Ok(frame);
+ }
+ let bytes = self
+ .input
+ .read(&mut self.buf)
+ .map_err(|source| crate::Error::Read { source })?;
+ if bytes == 0 {
+ return Err(crate::Error::EOF);
+ }
+ self.parser.add_bytes(&self.buf[..bytes]);
+ }
+ }
+
+ /// How much the timestamps in this file should be offset by.
+ ///
+ /// See `Parser::offset`.
+ pub fn offset(&self) -> Option<std::time::Duration> {
+ self.parser.offset()
+ }
+}
diff --git a/src/blocking/writer.rs b/src/blocking/writer.rs
new file mode 100644
index 0000000..5dade25
--- /dev/null
+++ b/src/blocking/writer.rs
@@ -0,0 +1,43 @@
+/// Writes ttyrec frames to a `std::io::Write` instance.
+pub struct Writer<T: std::io::Write> {
+ output: T,
+ creator: crate::Creator,
+}
+
+impl<T: std::io::Write> Writer<T> {
+ /// Creates a new `Writer` from a `std::io::Write` instance.
+ pub fn new(output: T) -> Self {
+ Self {
+ output,
+ creator: crate::Creator::new(),
+ }
+ }
+
+ /// Writes a new frame to the output stream, using the current time and
+ /// given data.
+ ///
+ /// # Errors
+ /// * `crate::Error::Write`: There was an error writing to the input
+ /// stream.
+ pub fn frame(&mut self, data: &[u8]) -> crate::Result<()> {
+ self.frame_at(std::time::Instant::now(), data)
+ }
+
+ /// Writes a new frame to the output stream, using the given time and
+ /// data.
+ ///
+ /// # Errors
+ /// * `crate::Error::Write`: There was an error writing to the input
+ /// stream.
+ pub fn frame_at(
+ &mut self,
+ cur_time: std::time::Instant,
+ data: &[u8],
+ ) -> crate::Result<()> {
+ let frame = self.creator.frame_at(cur_time, data);
+ let bytes: Vec<u8> = frame.try_into()?;
+ self.output
+ .write_all(&bytes)
+ .map_err(|source| crate::Error::Write { source })
+ }
+}
diff --git a/src/error.rs b/src/error.rs
index 9ca35a8..6978990 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,6 +1,9 @@
/// Errors potentially returned by this crate.
#[derive(Debug)]
pub enum Error {
+ /// eof
+ EOF,
+
/// failed to create ttyrec frame: got N bytes of data but ttyrec frames
/// can be at most M bytes
FrameTooBig { input: usize },
@@ -8,13 +11,22 @@ pub enum Error {
/// failed to create ttyrec frame: got N seconds but ttyrec frames can be
/// at most M seconds
FrameTooLong { input: u64 },
+
+ /// failed to read from input
+ Read { source: std::io::Error },
+
+ /// failed to write to output
+ Write { source: std::io::Error },
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
+ Self::EOF => write!(f, "eof"),
Self::FrameTooBig { input } => write!(f, "failed to create ttyrec frame: got {} bytes of data, but ttyrec frames can be at most {} bytes", input, u32::max_value()),
Self::FrameTooLong { input } => write!(f, "failed to create ttyrec frame: got {} seconds, but ttyrecs can be at most {} seconds", input, u32::max_value()),
+ Self::Read { source } => write!(f, "failed to read from input: {}", source),
+ Self::Write { source } => write!(f, "failed to write to output: {}", source),
}
}
}
diff --git a/src/lib.rs b/src/lib.rs
index d774aa3..5efd824 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,8 +19,17 @@
mod creator;
pub use creator::Creator;
mod error;
-pub use error::Error;
+pub use error::{Error, Result};
mod frame;
pub use frame::Frame;
mod parser;
pub use parser::Parser;
+pub mod blocking;
+#[cfg(feature = "async")]
+mod reader;
+#[cfg(feature = "async")]
+pub use reader::Reader;
+#[cfg(feature = "async")]
+mod writer;
+#[cfg(feature = "async")]
+pub use writer::Writer;
diff --git a/src/reader.rs b/src/reader.rs
new file mode 100644
index 0000000..6641bb9
--- /dev/null
+++ b/src/reader.rs
@@ -0,0 +1,49 @@
+use futures::io::AsyncReadExt as _;
+
+/// Reads ttyrec frames from a `futures::io::AsyncRead` instance.
+pub struct Reader<T: futures::io::AsyncRead> {
+ input: T,
+ parser: crate::Parser,
+ buf: [u8; 4096],
+}
+
+impl<T: futures::io::AsyncRead + std::marker::Unpin + Send> Reader<T> {
+ /// Creates a new `Reader` from a `futures::io::AsyncRead` instance.
+ pub fn new(input: T) -> Self {
+ Self {
+ input,
+ parser: crate::Parser::new(),
+ buf: [0; 4096],
+ }
+ }
+
+ /// Returns the next parsed frame from the input stream.
+ ///
+ /// # Errors
+ /// * `crate::Error::EOF`: The input stream has been closed.
+ /// * `crate::Error::Read`: There was an error reading from the input
+ /// stream.
+ pub async fn read_frame(&mut self) -> crate::Result<crate::Frame> {
+ loop {
+ if let Some(frame) = self.parser.next_frame() {
+ return Ok(frame);
+ }
+ let bytes = self
+ .input
+ .read(&mut self.buf)
+ .await
+ .map_err(|source| crate::Error::Read { source })?;
+ if bytes == 0 {
+ return Err(crate::Error::EOF);
+ }
+ self.parser.add_bytes(&self.buf[..bytes]);
+ }
+ }
+
+ /// How much the timestamps in this file should be offset by.
+ ///
+ /// See `Parser::offset`.
+ pub fn offset(&self) -> Option<std::time::Duration> {
+ self.parser.offset()
+ }
+}
diff --git a/src/writer.rs b/src/writer.rs
new file mode 100644
index 0000000..6975ad1
--- /dev/null
+++ b/src/writer.rs
@@ -0,0 +1,46 @@
+use futures::io::AsyncWriteExt as _;
+
+/// Writes ttyrec frames to a `futures::io::AsyncWrite` instance.
+pub struct Writer<T: futures::io::AsyncWrite> {
+ output: T,
+ creator: crate::Creator,
+}
+
+impl<T: futures::io::AsyncWrite + std::marker::Unpin + Send> Writer<T> {
+ /// Creates a new `Writer` from a `futures::io::AsyncWrite` instance.
+ pub fn new(output: T) -> Self {
+ Self {
+ output,
+ creator: crate::Creator::new(),
+ }
+ }
+
+ /// Writes a new frame to the output stream, using the current time and
+ /// given data.
+ ///
+ /// # Errors
+ /// * `crate::Error::Write`: There was an error writing to the input
+ /// stream.
+ pub async fn frame(&mut self, data: &[u8]) -> crate::Result<()> {
+ self.frame_at(std::time::Instant::now(), data).await
+ }
+
+ /// Writes a new frame to the output stream, using the given time and
+ /// data.
+ ///
+ /// # Errors
+ /// * `crate::Error::Write`: There was an error writing to the input
+ /// stream.
+ pub async fn frame_at(
+ &mut self,
+ cur_time: std::time::Instant,
+ data: &[u8],
+ ) -> crate::Result<()> {
+ let frame = self.creator.frame_at(cur_time, data);
+ let bytes: Vec<u8> = frame.try_into()?;
+ self.output
+ .write_all(&bytes)
+ .await
+ .map_err(|source| crate::Error::Write { source })
+ }
+}