aboutsummaryrefslogtreecommitdiffstats
path: root/src/state.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/state.rs')
-rw-r--r--src/state.rs125
1 files changed, 67 insertions, 58 deletions
diff --git a/src/state.rs b/src/state.rs
index d291e15..197ff0e 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -1,3 +1,4 @@
+use futures::future::Future as _;
use futures::stream::Stream as _;
use snafu::{OptionExt as _, ResultExt as _};
use std::io::Write as _;
@@ -7,59 +8,59 @@ pub enum Error {
#[snafu(display("invalid command index: {}", idx))]
InvalidCommandIndex { idx: usize },
- #[snafu(display("error sending message"))]
- Sending,
-
- #[snafu(display("error printing output: {}", source))]
- PrintOutput { source: std::io::Error },
+ #[snafu(display("error during read: {}", source))]
+ Read { source: crate::readline::Error },
#[snafu(display("error during eval: {}", source))]
Eval { source: crate::eval::Error },
-}
-pub type Result<T> = std::result::Result<T, Error>;
+ #[snafu(display("error during print: {}", source))]
+ Print { source: std::io::Error },
-#[derive(Debug)]
-pub enum StateEvent {
- Line(usize, String, futures::sync::oneshot::Sender<Result<()>>),
+ #[snafu(display("eof"))]
+ EOF,
}
+pub type Result<T> = std::result::Result<T, Error>;
+
pub struct State {
- r: futures::sync::mpsc::Receiver<StateEvent>,
+ idx: usize,
+ readline: Option<crate::readline::Readline>,
commands: std::collections::HashMap<usize, Command>,
}
impl State {
- pub fn new(r: futures::sync::mpsc::Receiver<StateEvent>) -> Self {
- Self {
- r,
+ 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)
}
- pub fn eval(
+ fn eval(
&mut self,
idx: usize,
line: &str,
- res: futures::sync::oneshot::Sender<Result<()>>,
- ) -> std::result::Result<
- (),
- (futures::sync::oneshot::Sender<Result<()>>, Error),
- > {
+ ) -> std::result::Result<(), Error> {
if self.commands.contains_key(&idx) {
- return Err((res, Error::InvalidCommandIndex { 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, res));
+ self.commands.insert(idx, Command::new(eval));
}
- Err(e) => return Err((res, e)),
+ Err(e) => return Err(e),
}
Ok(())
}
- pub fn command_start(
+ fn command_start(
&mut self,
idx: usize,
cmd: &str,
@@ -77,11 +78,7 @@ impl State {
Ok(())
}
- pub fn command_output(
- &mut self,
- idx: usize,
- output: &[u8],
- ) -> Result<()> {
+ fn command_output(&mut self, idx: usize, output: &[u8]) -> Result<()> {
let command = self
.commands
.get_mut(&idx)
@@ -90,13 +87,13 @@ impl State {
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
- stdout.write(output).context(PrintOutput)?;
- stdout.flush().context(PrintOutput)?;
+ stdout.write(output).context(Print)?;
+ stdout.flush().context(Print)?;
Ok(())
}
- pub fn command_exit(
+ fn command_exit(
&mut self,
idx: usize,
status: std::process::ExitStatus,
@@ -114,24 +111,43 @@ impl State {
loop {
let mut did_work = false;
- match self.r.poll().map_err(|()| unreachable!())? {
- futures::Async::Ready(Some(StateEvent::Line(
- idx,
- line,
- res,
- ))) => {
- match self.eval(idx, &line, res) {
- Ok(()) => {}
- Err((res, e)) => {
- res.send(Err(e)).map_err(|_| Error::Sending)?;
+ 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;
}
- did_work = true;
- }
- futures::Async::Ready(None) => {
- return Ok(futures::Async::Ready(()));
+ Ok(futures::Async::NotReady) => {
+ self.readline.replace(r);
+ }
+ Err(crate::readline::Error::EOF) => {
+ return Err(Error::EOF)
+ }
+ Err(e) => return Err(e).context(Read),
}
- futures::Async::NotReady => {}
}
for idx in self.commands.keys().cloned().collect::<Vec<usize>>() {
@@ -163,10 +179,7 @@ impl State {
futures::Async::Ready(None) => {
self.commands
.remove(&idx)
- .context(InvalidCommandIndex { idx })?
- .res
- .send(Ok(()))
- .map_err(|_| Error::Sending)?;
+ .context(InvalidCommandIndex { idx })?;
did_work = true;
}
futures::Async::NotReady => {}
@@ -188,6 +201,7 @@ impl futures::future::Future for State {
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);
}
@@ -198,7 +212,6 @@ impl futures::future::Future for State {
struct Command {
future: crate::eval::Eval,
- res: futures::sync::oneshot::Sender<Result<()>>,
cmd: Option<String>,
args: Option<Vec<String>>,
output: Vec<u8>,
@@ -206,13 +219,9 @@ struct Command {
}
impl Command {
- fn new(
- future: crate::eval::Eval,
- res: futures::sync::oneshot::Sender<Result<()>>,
- ) -> Self {
+ fn new(future: crate::eval::Eval) -> Self {
Self {
future,
- res,
cmd: None,
args: None,
output: vec![],