aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2019-07-10 22:49:36 -0400
committerJesse Luehrs <doy@tozt.net>2019-07-10 22:49:36 -0400
commitecd958bca79914f387b9766e5f35edc544f0a66c (patch)
tree1ff9c1ae640319852dd56261868755854efcdad7
parent8fc62f921ebe7cfed93c7a53403cc6b93a022b51 (diff)
downloadnbsh-old-ecd958bca79914f387b9766e5f35edc544f0a66c.tar.gz
nbsh-old-ecd958bca79914f387b9766e5f35edc544f0a66c.zip
don't need multiple files here
-rw-r--r--src/lib.rs1
-rw-r--r--src/state.rs231
-rw-r--r--src/tui.rs233
3 files changed, 228 insertions, 237 deletions
diff --git a/src/lib.rs b/src/lib.rs
index df7b61e..4f3e3df 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -17,7 +17,6 @@ mod eval;
mod parser;
mod process;
mod readline;
-mod state;
pub mod repl;
pub mod tui;
diff --git a/src/state.rs b/src/state.rs
deleted file mode 100644
index 197ff0e..0000000
--- a/src/state.rs
+++ /dev/null
@@ -1,231 +0,0 @@
-use futures::future::Future as _;
-use futures::stream::Stream as _;
-use snafu::{OptionExt as _, ResultExt as _};
-use std::io::Write as _;
-
-#[derive(Debug, snafu::Snafu)]
-pub enum Error {
- #[snafu(display("invalid command index: {}", idx))]
- InvalidCommandIndex { idx: usize },
-
- #[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: std::io::Error },
-
- #[snafu(display("eof"))]
- EOF,
-}
-
-pub type Result<T> = std::result::Result<T, Error>;
-
-pub struct State {
- idx: usize,
- readline: Option<crate::readline::Readline>,
- commands: std::collections::HashMap<usize, Command>,
-}
-
-impl State {
- pub fn new() -> Result<Self> {
- Ok(Self {
- idx: 0,
- readline: Some(Self::read()?),
- commands: std::collections::HashMap::new(),
- })
- }
-
- fn read() -> Result<crate::readline::Readline> {
- crate::readline::readline("$ ", true).context(Read)
- }
-
- fn eval(
- &mut self,
- idx: usize,
- line: &str,
- ) -> std::result::Result<(), Error> {
- if self.commands.contains_key(&idx) {
- return Err(Error::InvalidCommandIndex { idx });
- }
- let eval = crate::eval::eval(line).context(Eval);
- match eval {
- Ok(eval) => {
- self.commands.insert(idx, Command::new(eval));
- }
- Err(e) => return Err(e),
- }
- Ok(())
- }
-
- fn command_start(
- &mut self,
- idx: usize,
- cmd: &str,
- args: &[String],
- ) -> Result<()> {
- let command = self
- .commands
- .get_mut(&idx)
- .context(InvalidCommandIndex { idx })?;
- let cmd = cmd.to_string();
- let args = args.to_vec();
- eprint!("running '{} {:?}'\r\n", cmd, args);
- command.cmd = Some(cmd);
- command.args = Some(args);
- Ok(())
- }
-
- fn command_output(&mut self, idx: usize, output: &[u8]) -> Result<()> {
- let command = self
- .commands
- .get_mut(&idx)
- .context(InvalidCommandIndex { idx })?;
- command.output.append(&mut output.to_vec());
-
- let stdout = std::io::stdout();
- let mut stdout = stdout.lock();
- stdout.write(output).context(Print)?;
- stdout.flush().context(Print)?;
-
- Ok(())
- }
-
- fn command_exit(
- &mut self,
- idx: usize,
- status: std::process::ExitStatus,
- ) -> Result<()> {
- let command = self
- .commands
- .get_mut(&idx)
- .context(InvalidCommandIndex { idx })?;
- command.status = Some(status);
- eprint!("command exited: {}\r\n", status);
- Ok(())
- }
-
- fn poll_with_errors(&mut self) -> futures::Poll<(), Error> {
- loop {
- let mut did_work = false;
-
- if self.readline.is_none() && self.commands.is_empty() {
- self.idx += 1;
- self.readline = Some(Self::read()?)
- }
-
- if let Some(mut r) = self.readline.take() {
- match r.poll() {
- Ok(futures::Async::Ready(line)) => {
- // overlapping RawScreen lifespans don't work properly
- // - if readline creates a RawScreen, then eval
- // creates a separate one, then readline drops it, the
- // screen will go back to cooked even though a
- // RawScreen instance is still live.
- drop(r);
-
- match self.eval(self.idx, &line) {
- Ok(())
- | Err(Error::Eval {
- source:
- crate::eval::Error::Parser {
- source:
- crate::parser::Error::CommandRequired,
- ..
- },
- }) => {}
- Err(e) => return Err(e),
- }
- did_work = true;
- }
- Ok(futures::Async::NotReady) => {
- self.readline.replace(r);
- }
- Err(crate::readline::Error::EOF) => {
- return Err(Error::EOF)
- }
- Err(e) => return Err(e).context(Read),
- }
- }
-
- for idx in self.commands.keys().cloned().collect::<Vec<usize>>() {
- match self
- .commands
- .get_mut(&idx)
- .unwrap()
- .future
- .poll()
- .context(Eval)?
- {
- futures::Async::Ready(Some(event)) => match event {
- crate::eval::CommandEvent::CommandStart(
- cmd,
- args,
- ) => {
- self.command_start(idx, &cmd, &args)?;
- did_work = true;
- }
- crate::eval::CommandEvent::Output(out) => {
- self.command_output(idx, &out)?;
- did_work = true;
- }
- crate::eval::CommandEvent::CommandExit(status) => {
- self.command_exit(idx, status)?;
- did_work = true;
- }
- },
- futures::Async::Ready(None) => {
- self.commands
- .remove(&idx)
- .context(InvalidCommandIndex { idx })?;
- did_work = true;
- }
- futures::Async::NotReady => {}
- }
- }
-
- if !did_work {
- return Ok(futures::Async::NotReady);
- }
- }
- }
-}
-
-impl futures::future::Future for State {
- type Item = ();
- type Error = ();
-
- fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
- loop {
- match self.poll_with_errors() {
- Ok(a) => return Ok(a),
- Err(Error::EOF) => return Ok(futures::Async::Ready(())),
- Err(e) => {
- eprint!("error polling state: {}\r\n", e);
- }
- }
- }
- }
-}
-
-struct Command {
- future: crate::eval::Eval,
- cmd: Option<String>,
- args: Option<Vec<String>>,
- output: Vec<u8>,
- status: Option<std::process::ExitStatus>,
-}
-
-impl Command {
- fn new(future: crate::eval::Eval) -> Self {
- Self {
- future,
- cmd: None,
- args: None,
- output: vec![],
- status: None,
- }
- }
-}
diff --git a/src/tui.rs b/src/tui.rs
index 296f1bf..6a19a86 100644
--- a/src/tui.rs
+++ b/src/tui.rs
@@ -1,15 +1,238 @@
-use snafu::ResultExt as _;
+use futures::future::Future as _;
+use futures::stream::Stream as _;
+use snafu::{OptionExt as _, ResultExt as _};
+use std::io::Write as _;
#[derive(Debug, snafu::Snafu)]
pub enum Error {
- #[snafu(display("error from state: {}", source))]
- State { source: crate::state::Error },
+ #[snafu(display("invalid command index: {}", idx))]
+ InvalidCommandIndex { idx: usize },
+
+ #[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: std::io::Error },
+
+ #[snafu(display("eof"))]
+ EOF,
}
+pub type Result<T> = std::result::Result<T, Error>;
+
pub fn tui() {
- let state = crate::state::State::new().context(State);
- match state {
+ match Tui::new() {
Ok(state) => tokio::run(state),
Err(e) => eprintln!("failed to create state: {}", e),
}
}
+
+pub struct Tui {
+ idx: usize,
+ readline: Option<crate::readline::Readline>,
+ commands: std::collections::HashMap<usize, Command>,
+}
+
+impl Tui {
+ pub fn new() -> Result<Self> {
+ Ok(Self {
+ idx: 0,
+ readline: Some(Self::read()?),
+ commands: std::collections::HashMap::new(),
+ })
+ }
+
+ fn read() -> Result<crate::readline::Readline> {
+ crate::readline::readline("$ ", true).context(Read)
+ }
+
+ fn eval(
+ &mut self,
+ idx: usize,
+ line: &str,
+ ) -> std::result::Result<(), Error> {
+ if self.commands.contains_key(&idx) {
+ return Err(Error::InvalidCommandIndex { idx });
+ }
+ let eval = crate::eval::eval(line).context(Eval);
+ match eval {
+ Ok(eval) => {
+ self.commands.insert(idx, Command::new(eval));
+ }
+ Err(e) => return Err(e),
+ }
+ Ok(())
+ }
+
+ fn command_start(
+ &mut self,
+ idx: usize,
+ cmd: &str,
+ args: &[String],
+ ) -> Result<()> {
+ let command = self
+ .commands
+ .get_mut(&idx)
+ .context(InvalidCommandIndex { idx })?;
+ let cmd = cmd.to_string();
+ let args = args.to_vec();
+ eprint!("running '{} {:?}'\r\n", cmd, args);
+ command.cmd = Some(cmd);
+ command.args = Some(args);
+ Ok(())
+ }
+
+ fn command_output(&mut self, idx: usize, output: &[u8]) -> Result<()> {
+ let command = self
+ .commands
+ .get_mut(&idx)
+ .context(InvalidCommandIndex { idx })?;
+ command.output.append(&mut output.to_vec());
+
+ let stdout = std::io::stdout();
+ let mut stdout = stdout.lock();
+ stdout.write(output).context(Print)?;
+ stdout.flush().context(Print)?;
+
+ Ok(())
+ }
+
+ fn command_exit(
+ &mut self,
+ idx: usize,
+ status: std::process::ExitStatus,
+ ) -> Result<()> {
+ let command = self
+ .commands
+ .get_mut(&idx)
+ .context(InvalidCommandIndex { idx })?;
+ command.status = Some(status);
+ eprint!("command exited: {}\r\n", status);
+ Ok(())
+ }
+
+ fn poll_with_errors(&mut self) -> futures::Poll<(), Error> {
+ loop {
+ let mut did_work = false;
+
+ if self.readline.is_none() && self.commands.is_empty() {
+ self.idx += 1;
+ self.readline = Some(Self::read()?)
+ }
+
+ if let Some(mut r) = self.readline.take() {
+ match r.poll() {
+ Ok(futures::Async::Ready(line)) => {
+ // overlapping RawScreen lifespans don't work properly
+ // - if readline creates a RawScreen, then eval
+ // creates a separate one, then readline drops it, the
+ // screen will go back to cooked even though a
+ // RawScreen instance is still live.
+ drop(r);
+
+ match self.eval(self.idx, &line) {
+ Ok(())
+ | Err(Error::Eval {
+ source:
+ crate::eval::Error::Parser {
+ source:
+ crate::parser::Error::CommandRequired,
+ ..
+ },
+ }) => {}
+ Err(e) => return Err(e),
+ }
+ did_work = true;
+ }
+ Ok(futures::Async::NotReady) => {
+ self.readline.replace(r);
+ }
+ Err(crate::readline::Error::EOF) => {
+ return Err(Error::EOF)
+ }
+ Err(e) => return Err(e).context(Read),
+ }
+ }
+
+ for idx in self.commands.keys().cloned().collect::<Vec<usize>>() {
+ match self
+ .commands
+ .get_mut(&idx)
+ .unwrap()
+ .future
+ .poll()
+ .context(Eval)?
+ {
+ futures::Async::Ready(Some(event)) => match event {
+ crate::eval::CommandEvent::CommandStart(
+ cmd,
+ args,
+ ) => {
+ self.command_start(idx, &cmd, &args)?;
+ did_work = true;
+ }
+ crate::eval::CommandEvent::Output(out) => {
+ self.command_output(idx, &out)?;
+ did_work = true;
+ }
+ crate::eval::CommandEvent::CommandExit(status) => {
+ self.command_exit(idx, status)?;
+ did_work = true;
+ }
+ },
+ futures::Async::Ready(None) => {
+ self.commands
+ .remove(&idx)
+ .context(InvalidCommandIndex { idx })?;
+ did_work = true;
+ }
+ futures::Async::NotReady => {}
+ }
+ }
+
+ if !did_work {
+ return Ok(futures::Async::NotReady);
+ }
+ }
+ }
+}
+
+impl futures::future::Future for Tui {
+ type Item = ();
+ type Error = ();
+
+ fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
+ loop {
+ match self.poll_with_errors() {
+ Ok(a) => return Ok(a),
+ Err(Error::EOF) => return Ok(futures::Async::Ready(())),
+ Err(e) => {
+ eprint!("error polling state: {}\r\n", e);
+ }
+ }
+ }
+ }
+}
+
+struct Command {
+ future: crate::eval::Eval,
+ cmd: Option<String>,
+ args: Option<Vec<String>>,
+ output: Vec<u8>,
+ status: Option<std::process::ExitStatus>,
+}
+
+impl Command {
+ fn new(future: crate::eval::Eval) -> Self {
+ Self {
+ future,
+ cmd: None,
+ args: None,
+ output: vec![],
+ status: None,
+ }
+ }
+}