diff options
Diffstat (limited to 'src/state.rs')
-rw-r--r-- | src/state.rs | 231 |
1 files changed, 0 insertions, 231 deletions
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, - } - } -} |