aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2019-07-09 01:01:55 -0400
committerJesse Luehrs <doy@tozt.net>2019-07-09 01:07:29 -0400
commitb6709ebadff904ddefec07625d198c8fa33a4555 (patch)
tree834a3530810f78484b5efcaea07352f825bfe698
parent6983acfaff676d527b0239be8ed6306672968c2b (diff)
downloadnbsh-old-b6709ebadff904ddefec07625d198c8fa33a4555.tar.gz
nbsh-old-b6709ebadff904ddefec07625d198c8fa33a4555.zip
split into a repl and a tui
the repl uses readline and is line-based, the tui will eventually be a full alternate screen app
-rw-r--r--src/bin/repl.rs5
-rw-r--r--src/bin/tui.rs5
-rw-r--r--src/lib.rs (renamed from src/main.rs)6
-rw-r--r--src/repl.rs101
-rw-r--r--src/tui.rs92
5 files changed, 149 insertions, 60 deletions
diff --git a/src/bin/repl.rs b/src/bin/repl.rs
new file mode 100644
index 0000000..ed483ec
--- /dev/null
+++ b/src/bin/repl.rs
@@ -0,0 +1,5 @@
+extern crate nbsh;
+
+fn main() {
+ nbsh::repl::repl();
+}
diff --git a/src/bin/tui.rs b/src/bin/tui.rs
new file mode 100644
index 0000000..68be8a8
--- /dev/null
+++ b/src/bin/tui.rs
@@ -0,0 +1,5 @@
+extern crate nbsh;
+
+fn main() {
+ nbsh::tui::tui();
+}
diff --git a/src/main.rs b/src/lib.rs
index cd3dc54..ebe3785 100644
--- a/src/main.rs
+++ b/src/lib.rs
@@ -16,9 +16,7 @@ mod eval;
mod parser;
mod process;
mod readline;
-mod repl;
mod state;
-fn main() {
- repl::repl();
-}
+pub mod repl;
+pub mod tui;
diff --git a/src/repl.rs b/src/repl.rs
index 874a22e..aa80132 100644
--- a/src/repl.rs
+++ b/src/repl.rs
@@ -1,5 +1,6 @@
use futures::future::{Future as _, IntoFuture as _};
use futures::stream::Stream as _;
+use snafu::ResultExt as _;
use std::io::Write as _;
#[derive(Debug, snafu::Snafu)]
@@ -11,50 +12,41 @@ pub enum Error {
Eval { source: crate::eval::Error },
#[snafu(display("error during print: {}", source))]
- Print { source: crate::state::Error },
+ Print { source: std::io::Error },
}
-pub fn repl() {
- tokio::run(futures::lazy(|| {
- let (w, r) = futures::sync::mpsc::channel(0);
-
- tokio::spawn(crate::state::State::new(r).map_err(|e| {
- error(&Error::Print { source: e });
- }));
+pub type Result<T> = std::result::Result<T, Error>;
- futures::future::loop_fn(0, move |idx| {
- let w = w.clone();
- read()
- .and_then(move |line| {
- let w = w.clone();
- eval(&line).for_each(move |event| {
- let w = w.clone();
- print(w, idx, &event)
- })
- })
- .then(move |res| match res {
- // successful run or empty input means prompt again
- Ok(_)
- | Err(Error::Eval {
- source:
- crate::eval::Error::Parser {
- source: crate::parser::Error::CommandRequired,
- ..
- },
- }) => Ok(futures::future::Loop::Continue(idx + 1)),
- // eof means we're done
- Err(Error::Read {
- source: crate::readline::Error::EOF,
- }) => Ok(futures::future::Loop::Break(())),
- // any other errors should be displayed, then we
- // prompt again
- Err(e) => {
- error(&e);
- Ok(futures::future::Loop::Continue(idx + 1))
- }
- })
- })
- }));
+pub fn repl() {
+ tokio::run(futures::future::loop_fn((), |_| {
+ read()
+ .and_then(|line| eval(&line).for_each(|event| print(&event)))
+ .then(|res| match res {
+ // successful run or empty input means prompt again
+ Ok(_)
+ | Err(Error::Eval {
+ source:
+ crate::eval::Error::Parser {
+ source: crate::parser::Error::CommandRequired,
+ ..
+ },
+ }) => Ok(futures::future::Loop::Continue(())),
+ // eof means we're done
+ Err(Error::Read {
+ source: crate::readline::Error::EOF,
+ }) => Ok(futures::future::Loop::Break(())),
+ // any other errors should be displayed, then we
+ // prompt again
+ Err(e) => {
+ let stderr = std::io::stderr();
+ let mut stderr = stderr.lock();
+ // panics seem fine for errors during error handling
+ write!(stderr, "{}\r\n", e).unwrap();
+ stderr.flush().unwrap();
+ Ok(futures::future::Loop::Continue(()))
+ }
+ })
+ }))
}
fn read() -> impl futures::future::Future<Item = String, Error = Error> {
@@ -74,19 +66,16 @@ fn eval(
.map_err(|e| Error::Eval { source: e })
}
-fn print(
- w: futures::sync::mpsc::Sender<crate::state::StateEvent>,
- idx: usize,
- event: &crate::eval::CommandEvent,
-) -> impl futures::future::Future<Item = (), Error = Error> {
- crate::state::update(w, idx, event)
- .map_err(|e| Error::Print { source: e })
-}
-
-fn error(e: &Error) {
- let stderr = std::io::stderr();
- let mut stderr = stderr.lock();
- // panics seem fine for errors during error handling
- write!(stderr, "{}\r\n", e).unwrap();
- stderr.flush().unwrap();
+fn print(event: &crate::eval::CommandEvent) -> Result<()> {
+ match event {
+ crate::eval::CommandEvent::CommandStart(_, _) => {}
+ crate::eval::CommandEvent::Output(out) => {
+ let stdout = std::io::stdout();
+ let mut stdout = stdout.lock();
+ stdout.write(out).context(Print)?;
+ stdout.flush().context(Print)?;
+ }
+ crate::eval::CommandEvent::CommandExit(_) => {}
+ }
+ Ok(())
}
diff --git a/src/tui.rs b/src/tui.rs
new file mode 100644
index 0000000..8d84a7f
--- /dev/null
+++ b/src/tui.rs
@@ -0,0 +1,92 @@
+use futures::future::{Future as _, IntoFuture as _};
+use futures::stream::Stream as _;
+use std::io::Write as _;
+
+#[derive(Debug, snafu::Snafu)]
+pub enum Error {
+ #[snafu(display("error during read: {}", source))]
+ Read { source: crate::readline::Error },
+
+ #[snafu(display("error during eval: {}", source))]
+ Eval { source: crate::eval::Error },
+
+ #[snafu(display("error during print: {}", source))]
+ Print { source: crate::state::Error },
+}
+
+pub fn tui() {
+ tokio::run(futures::lazy(|| {
+ let (w, r) = futures::sync::mpsc::channel(0);
+
+ tokio::spawn(crate::state::State::new(r).map_err(|e| {
+ error(&Error::Print { source: e });
+ }));
+
+ futures::future::loop_fn(0, move |idx| {
+ let w = w.clone();
+ read()
+ .and_then(move |line| {
+ let w = w.clone();
+ eval(&line).for_each(move |event| {
+ let w = w.clone();
+ print(w, idx, &event)
+ })
+ })
+ .then(move |res| match res {
+ // successful run or empty input means prompt again
+ Ok(_)
+ | Err(Error::Eval {
+ source:
+ crate::eval::Error::Parser {
+ source: crate::parser::Error::CommandRequired,
+ ..
+ },
+ }) => Ok(futures::future::Loop::Continue(idx + 1)),
+ // eof means we're done
+ Err(Error::Read {
+ source: crate::readline::Error::EOF,
+ }) => Ok(futures::future::Loop::Break(())),
+ // any other errors should be displayed, then we
+ // prompt again
+ Err(e) => {
+ error(&e);
+ Ok(futures::future::Loop::Continue(idx + 1))
+ }
+ })
+ })
+ }));
+}
+
+fn read() -> impl futures::future::Future<Item = String, Error = Error> {
+ crate::readline::readline("$ ", true)
+ .into_future()
+ .flatten()
+ .map_err(|e| Error::Read { source: e })
+}
+
+fn eval(
+ line: &str,
+) -> impl futures::stream::Stream<Item = crate::eval::CommandEvent, Error = Error>
+{
+ crate::eval::eval(line)
+ .into_future()
+ .flatten_stream()
+ .map_err(|e| Error::Eval { source: e })
+}
+
+fn print(
+ w: futures::sync::mpsc::Sender<crate::state::StateEvent>,
+ idx: usize,
+ event: &crate::eval::CommandEvent,
+) -> impl futures::future::Future<Item = (), Error = Error> {
+ crate::state::update(w, idx, event)
+ .map_err(|e| Error::Print { source: e })
+}
+
+fn error(e: &Error) {
+ let stderr = std::io::stderr();
+ let mut stderr = stderr.lock();
+ // panics seem fine for errors during error handling
+ write!(stderr, "{}\r\n", e).unwrap();
+ stderr.flush().unwrap();
+}