msbash 0.0.0

Bash interpreter/compiler. Will not support all the functionalities.
use std::io::{self, Write};
use std::time::Instant;

use super::cmd::run_cmd_line;
use tty::{Key, RawTTy};

pub mod colors;
pub mod history;
pub mod tty;

mod prompt;

pub fn run() {
    let repl = Repl::new();
    repl.main_loop();
    tty::reset_terminal_mode();
}

impl Repl {
    fn main_loop(mut self) {
        let prompt = prompt::Prompt::new();

        let mut stdout = io::stdout().lock();
        let mut elapsed: u64 = 0;
        loop {
            //infinite loop
            prompt.show(&self.state.pwd, elapsed);
            loop {
                // loop to read one command
                match self.tty.read_one_key() {
                    Key::Enter => {
                        let start = Instant::now();
                        self.run_cmd();
                        elapsed = start.elapsed().as_secs();
                        break;
                    }
                    Key::Tab => self.handle_completion(),
                    Key::Up => {
                        let line = self.state.hist.get_previous();
                        self.display_line(line);
                    }
                    Key::Down => {
                        let line = self.state.hist.get_next();
                        self.display_line(line);
                    }
                    Key::Right => self.move_right(),
                    Key::Left => self.move_left(),
                    Key::Ctrl_C => return,
                    Key::Ctrl_Z => return,

                    Key::Backspace => {
                        if self.position_on_line > 0 {
                            self.delete_one_char()
                        }
                    }
                    Key::Char(c) => {
                        //implement smart completion here
                        self.add_one_char(c);
                    }
                    k => unimplemented!("{:?} not handled", k),
                }
                stdout.flush().expect("unable to flush stdout");
            }
        }
    }
    fn run_cmd(&mut self) {
        eprintln!("");
        if self.buffer.len() > 0 {
            let line = &self.buffer.to_owned();
            let line = std::str::from_utf8(line).unwrap();
            if let Err(err) = self._run_cmd(line) {
                eprintln!("io error: {}", err);
            } else {
                self.state.hist.add(line.to_owned());
            }
        }

        self.buffer.clear();
        self.position_on_line = 0;
    }

    fn _run_cmd(&mut self, line: &str) -> Result<(), std::io::Error> {
        let args: Vec<&str> = line.split(' ').filter(|s| !s.is_empty()).collect();
        if let Some(func) = self.state.builtins.get(args[0]) {
            func(args, &mut self.state)?;
        } else {
            let output = run_cmd_line(&line)?;
            let stdout = std::str::from_utf8(&output.stdout).unwrap();
            let stderr = std::str::from_utf8(&output.stderr).unwrap();
            print!("{}{}", stderr, stdout);
        }
        Ok(())
    }

    fn display_line(&mut self, line: String) {
        self.clear_line();
        for c in line.bytes() {
            self.add_one_char(c);
        }
    }

    fn clear_line(&mut self) {
        loop {
            if self.position_on_line < 1 {
                break;
            }
            self.delete_one_char();
        }
    }

    fn handle_completion(&mut self) {
        eprintln!("todo! handle autocompletion")
    }

    fn delete_one_char(&mut self) {
        //move left then print whitespace then go back again
        print!("\x1b[1D \x1b[1D");
        self.position_on_line -= 1;
        self.buffer.pop();
    }
    fn add_one_char(&mut self, c: u8) {
        self.buffer.push(c);
        self.position_on_line += 1;
        print!("{}", c as char);
    }

    fn new() -> Self {
        let position_on_line = 0;
        let buffer: Vec<u8> = Vec::new();
        let state = super::ShellState::new();
        let raw_tty = tty::RawTTy::new();
        Self {
            state,
            position_on_line,
            tty: raw_tty,
            buffer,
        }
    }
    fn move_left(&mut self) {
        if self.position_on_line > 0 {
            self.position_on_line -= 1;
            eprint!("\x1B[1D");
        }
    }
    fn move_right(&mut self) {
        if self.position_on_line + 1 < self.buffer.len() {
            eprint!("\x1B[1C");
            self.position_on_line += 1;
        }
    }
}

struct Repl {
    state: super::ShellState,
    position_on_line: usize,
    // repl is a singleton with exclusive access to stdin
    tty: RawTTy<'static>,

    buffer: Vec<u8>, // characters read so far
}