aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2021-03-06 18:45:50 -0500
committerJesse Luehrs <doy@tozt.net>2021-03-06 18:45:50 -0500
commita4bca446049fd488af29dd5ec26f953845e8e4fe (patch)
treefa7f129e5b27c00f29b4f33edbc31f2c68d89fda
parente358c58826b1732763970848c0c6d77f12ef5e5a (diff)
downloadtextmode-a4bca446049fd488af29dd5ec26f953845e8e4fe.tar.gz
textmode-a4bca446049fd488af29dd5ec26f953845e8e4fe.zip
split into sync and async implementations
-rw-r--r--Cargo.toml10
-rw-r--r--examples/async.rs29
-rw-r--r--examples/basic.rs4
-rw-r--r--src/async.rs67
-rw-r--r--src/lib.rs91
-rw-r--r--src/sync.rs65
6 files changed, 205 insertions, 61 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 1007f0d..08b72cd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,3 +8,13 @@ edition = "2018"
itoa = "0.4"
terminal_size = "0.1"
vt100 = "0.9"
+
+blocking = { version = "1.0", optional = true }
+futures-lite = { version = "1.11", optional = true }
+
+[features]
+default = []
+async = ["blocking", "futures-lite"]
+
+[dev-dependencies]
+smol = "1.2"
diff --git a/examples/async.rs b/examples/async.rs
new file mode 100644
index 0000000..e8e4282
--- /dev/null
+++ b/examples/async.rs
@@ -0,0 +1,29 @@
+use textmode::Textmode as _;
+
+async fn run(tm: &mut textmode::r#async::Textmode) -> std::io::Result<()> {
+ tm.move_to(5, 5);
+ tm.write_str("foo");
+ smol::Timer::after(std::time::Duration::from_secs(2)).await;
+ tm.refresh().await?;
+ smol::Timer::after(std::time::Duration::from_secs(2)).await;
+
+ tm.move_to(8, 8);
+ tm.set_fgcolor(textmode::color::GREEN);
+ tm.write_str("bar");
+ tm.move_to(11, 11);
+ tm.set_fgcolor(vt100::Color::Default);
+ tm.write_str("baz");
+ smol::Timer::after(std::time::Duration::from_secs(2)).await;
+ tm.refresh().await?;
+ smol::Timer::after(std::time::Duration::from_secs(2)).await;
+ Ok(())
+}
+
+fn main() {
+ smol::block_on(async {
+ let mut tm = textmode::r#async::Textmode::new().await.unwrap();
+ let e = run(&mut tm).await;
+ tm.cleanup().await.unwrap();
+ e.unwrap();
+ });
+}
diff --git a/examples/basic.rs b/examples/basic.rs
index 1026834..150c9b0 100644
--- a/examples/basic.rs
+++ b/examples/basic.rs
@@ -1,5 +1,7 @@
+use textmode::Textmode as _;
+
fn main() {
- let mut tm = textmode::Textmode::new().unwrap();
+ let mut tm = textmode::sync::Textmode::new().unwrap();
tm.move_to(5, 5);
tm.write_str("foo");
diff --git a/src/async.rs b/src/async.rs
new file mode 100644
index 0000000..a384da8
--- /dev/null
+++ b/src/async.rs
@@ -0,0 +1,67 @@
+use futures_lite::io::AsyncWriteExt as _;
+
+use super::private::TextmodeImpl as _;
+
+pub struct Textmode {
+ cur: vt100::Parser,
+ next: vt100::Parser,
+}
+
+impl super::private::TextmodeImpl for Textmode {
+ fn cur(&self) -> &vt100::Parser {
+ &self.cur
+ }
+
+ fn cur_mut(&mut self) -> &mut vt100::Parser {
+ &mut self.cur
+ }
+
+ fn next(&self) -> &vt100::Parser {
+ &self.next
+ }
+
+ fn next_mut(&mut self) -> &mut vt100::Parser {
+ &mut self.next
+ }
+}
+
+impl super::Textmode for Textmode {}
+
+impl Textmode {
+ pub async fn new() -> std::io::Result<Self> {
+ let (rows, cols) = match terminal_size::terminal_size() {
+ Some((terminal_size::Width(w), terminal_size::Height(h))) => {
+ (h, w)
+ }
+ _ => (24, 80),
+ };
+ let cur = vt100::Parser::new(rows, cols, 0);
+ let next = vt100::Parser::new(rows, cols, 0);
+
+ let self_ = Self { cur, next };
+ self_
+ .write_stdout(b"\x1b7\x1b[?47h\x1b[2J\x1b[H\x1b[?25h")
+ .await?;
+ Ok(self_)
+ }
+
+ // TODO: without async drop or async closures, i'm not sure how to do
+ // better than this
+ pub async fn cleanup(&mut self) -> std::io::Result<()> {
+ self.write_stdout(b"\x1b[?47l\x1b8\x1b[?25h").await
+ }
+
+ pub async fn refresh(&mut self) -> std::io::Result<()> {
+ let diff = self.next().screen().contents_diff(self.cur().screen());
+ self.write_stdout(&diff).await?;
+ self.cur_mut().process(&diff);
+ Ok(())
+ }
+
+ async fn write_stdout(&self, buf: &[u8]) -> std::io::Result<()> {
+ let mut stdout = blocking::Unblock::new(std::io::stdout());
+ stdout.write_all(buf).await?;
+ stdout.flush().await?;
+ Ok(())
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 39c201b..fcee1e9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,53 +1,47 @@
-use std::io::Write as _;
-
pub mod color;
-pub struct Textmode {
- cur: vt100::Parser,
- next: vt100::Parser,
-}
+#[cfg(feature = "async")]
+pub mod r#async;
+pub mod sync;
-impl Textmode {
- pub fn new() -> std::io::Result<Self> {
- let (rows, cols) = match terminal_size::terminal_size() {
- Some((terminal_size::Width(w), terminal_size::Height(h))) => {
- (h, w)
- }
- _ => (24, 80),
- };
- let cur = vt100::Parser::new(rows, cols, 0);
- let next = vt100::Parser::new(rows, cols, 0);
+mod private {
+ pub trait TextmodeImpl {
+ fn cur(&self) -> &vt100::Parser;
+ fn cur_mut(&mut self) -> &mut vt100::Parser;
+ fn next(&self) -> &vt100::Parser;
+ fn next_mut(&mut self) -> &mut vt100::Parser;
- let self_ = Self { cur, next };
- self_.write_stdout(b"\x1b7\x1b[?47h\x1b[2J\x1b[H\x1b[?25h")?;
- Ok(self_)
- }
+ fn write_u16(&mut self, i: u16) {
+ // unwrap is fine because vt100::Parser::write can never fail
+ itoa::write(self.next_mut(), i).unwrap();
+ }
- pub fn cursor_position(&self) -> (u16, u16) {
- self.next.screen().cursor_position()
+ fn write_u8(&mut self, i: u8) {
+ // unwrap is fine because vt100::Parser::write can never fail
+ itoa::write(self.next_mut(), i).unwrap();
+ }
}
+}
- pub fn write(&mut self, buf: &[u8]) {
- self.next.process(buf);
+pub trait Textmode: private::TextmodeImpl {
+ fn cursor_position(&self) -> (u16, u16) {
+ self.next().screen().cursor_position()
}
- pub fn refresh(&mut self) -> std::io::Result<()> {
- let diff = self.next.screen().contents_diff(self.cur.screen());
- self.write_stdout(&diff)?;
- self.cur.process(&diff);
- Ok(())
+ fn write(&mut self, buf: &[u8]) {
+ self.next_mut().process(buf);
}
- pub fn set_size(&mut self, rows: u16, cols: u16) {
- self.cur.set_size(rows, cols);
- self.next.set_size(rows, cols);
+ fn set_size(&mut self, rows: u16, cols: u16) {
+ self.cur_mut().set_size(rows, cols);
+ self.next_mut().set_size(rows, cols);
}
- pub fn write_str(&mut self, text: &str) {
+ fn write_str(&mut self, text: &str) {
self.write(text.as_bytes());
}
- pub fn move_to(&mut self, row: u16, col: u16) {
+ fn move_to(&mut self, row: u16, col: u16) {
self.write(b"\x1b[");
self.write_u16(row);
self.write(b";");
@@ -55,11 +49,11 @@ impl Textmode {
self.write(b"H");
}
- pub fn clear(&mut self) {
+ fn clear(&mut self) {
self.write(b"\x1b[2J");
}
- pub fn set_fgcolor(&mut self, color: vt100::Color) {
+ fn set_fgcolor(&mut self, color: vt100::Color) {
match color {
vt100::Color::Default => {
self.write(b"\x1b[39m");
@@ -87,7 +81,7 @@ impl Textmode {
}
}
- pub fn set_bgcolor(&mut self, color: vt100::Color) {
+ fn set_bgcolor(&mut self, color: vt100::Color) {
match color {
vt100::Color::Default => {
self.write(b"\x1b[49m");
@@ -114,27 +108,4 @@ impl Textmode {
}
}
}
-
- fn write_u16(&mut self, i: u16) {
- // unwrap is fine because vt100::Parser::write can never fail
- itoa::write(&mut self.next, i).unwrap();
- }
-
- fn write_u8(&mut self, i: u8) {
- // unwrap is fine because vt100::Parser::write can never fail
- itoa::write(&mut self.next, i).unwrap();
- }
-
- fn write_stdout(&self, buf: &[u8]) -> std::io::Result<()> {
- let mut stdout = std::io::stdout();
- stdout.write_all(buf)?;
- stdout.flush()?;
- Ok(())
- }
-}
-
-impl Drop for Textmode {
- fn drop(&mut self) {
- let _ = self.write_stdout(b"\x1b[?47l\x1b8\x1b[?25h");
- }
}
diff --git a/src/sync.rs b/src/sync.rs
new file mode 100644
index 0000000..047036d
--- /dev/null
+++ b/src/sync.rs
@@ -0,0 +1,65 @@
+use std::io::Write as _;
+
+use super::private::TextmodeImpl as _;
+
+pub struct Textmode {
+ cur: vt100::Parser,
+ next: vt100::Parser,
+}
+
+impl super::private::TextmodeImpl for Textmode {
+ fn cur(&self) -> &vt100::Parser {
+ &self.cur
+ }
+
+ fn cur_mut(&mut self) -> &mut vt100::Parser {
+ &mut self.cur
+ }
+
+ fn next(&self) -> &vt100::Parser {
+ &self.next
+ }
+
+ fn next_mut(&mut self) -> &mut vt100::Parser {
+ &mut self.next
+ }
+}
+
+impl super::Textmode for Textmode {}
+
+impl Textmode {
+ pub fn new() -> std::io::Result<Self> {
+ let (rows, cols) = match terminal_size::terminal_size() {
+ Some((terminal_size::Width(w), terminal_size::Height(h))) => {
+ (h, w)
+ }
+ _ => (24, 80),
+ };
+ let cur = vt100::Parser::new(rows, cols, 0);
+ let next = vt100::Parser::new(rows, cols, 0);
+
+ let self_ = Self { cur, next };
+ self_.write_stdout(b"\x1b7\x1b[?47h\x1b[2J\x1b[H\x1b[?25h")?;
+ Ok(self_)
+ }
+
+ pub fn refresh(&mut self) -> std::io::Result<()> {
+ let diff = self.next().screen().contents_diff(self.cur().screen());
+ self.write_stdout(&diff)?;
+ self.cur_mut().process(&diff);
+ Ok(())
+ }
+
+ fn write_stdout(&self, buf: &[u8]) -> std::io::Result<()> {
+ let mut stdout = std::io::stdout();
+ stdout.write_all(buf)?;
+ stdout.flush()?;
+ Ok(())
+ }
+}
+
+impl Drop for Textmode {
+ fn drop(&mut self) {
+ let _ = self.write_stdout(b"\x1b[?47l\x1b8\x1b[?25h");
+ }
+}