mssh 0.0.0

Mssh Simple SHell. Bash interpreter/compiler. Will not support all the functionalities.
use std::collections::HashMap;
use std::io::Error;

use crate::shell::repl::tty;

type Func = fn(Vec<&str>, &mut State) -> Result<(), std::io::Error>;

type State = super::ShellState;

pub type NameToFunc = HashMap<String, Func>;

macro_rules! add_builtin {
    ($a:expr,$b:ident) => {
        $a.insert(stringify!($b).to_owned(), $b)
    };
}

pub fn init() -> NameToFunc {
    let mut res = NameToFunc::new();
    //not possible to do a loop as the loop index
    //is stringified leading to a single value in the map
    add_builtin!(res, cd);
    add_builtin!(res, echo);
    add_builtin!(res, pwd);
    add_builtin!(res, reset);

    res
}

fn cd(args: Vec<&str>, state: &mut super::ShellState) -> Result<(), Error> {
    let mut _path = if args.len() < 2 { &state.home } else { args[1] };

    let mut path = String::new();
    if !_path.starts_with("/") {
        path.push_str(&state.pwd);
        path.push_str("/");
    }
    path.push_str(_path);
    // path is now absolute

    let sub_paths: Vec<&str> = path.split("/").collect();

    let mut ignore_next = 0;
    let mut idx = sub_paths.len() - 1;
    let mut sub_result = Vec::new();
    while sub_paths[idx] != "" {
        if sub_paths[idx] == ".." {
            ignore_next += 1;
        } else {
            if ignore_next > 0 {
                ignore_next -= 1;
            } else {
                sub_result.push(sub_paths[idx]);
            }
        }
        idx -= 1;
    }
    let mut res = String::new();
    let len = sub_result.len();
    for i in 0..len {
        res.push_str("/");
        res.push_str(sub_result[len - i - 1]);
    }
    std::env::set_current_dir(&res)?;
    state.pwd = res;
    Ok(())
}

fn echo(args: Vec<&str>, _state: &mut super::ShellState) -> Result<(), Error> {
    let mut res = String::new();
    for arg in args.iter() {
        res.push_str(arg);
        res.push_str(" ");
    }
    res.pop();
    println!("{}", res);
    Ok(())
}

fn pwd(_args: Vec<&str>, state: &mut super::ShellState) -> Result<(), Error> {
    println!("{}", &state.pwd);
    Ok(())
}

fn reset(_args: Vec<&str>, _state: &mut super::ShellState) -> Result<(), Error> {
    // TODO should fully clear the state and reset it
    let size = tty::get_terminal_size();
    println!("{} lines and {} columns", size.height, size.width);
    Ok(())
}