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();
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);
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> {
let size = tty::get_terminal_size();
println!("{} lines and {} columns", size.height, size.width);
Ok(())
}