mod alias;
mod cd;
mod debug;
mod exit;
mod export;
mod help;
mod history;
mod jobs;
mod source;
mod r#type;
mod unalias;
use std::{
collections::{hash_map::Iter, HashMap},
marker::PhantomData,
};
use anyhow::Result;
use unalias::unalias_builtin;
use self::{
alias::alias_builtin, cd::cd_builtin, debug::debug_builtin, exit::exit_builtin,
export::export_builtin, help::help_builtin, history::HistoryBuiltin, jobs::jobs_builtin,
r#type::type_builtin, source::source_builtin,
};
use crate::{
all_the_tuples,
prelude::{CmdOutput, States},
shell::Shell,
state::Param,
};
pub struct Builtins {
builtins: HashMap<String, Box<dyn Builtin>>,
}
impl Builtins {
pub fn new() -> Self {
Builtins {
builtins: HashMap::new(),
}
}
pub fn insert<I, B: Builtin + 'static>(
&mut self,
name: impl ToString,
builtin: impl IntoBuiltin<I, Builtin = B>,
) {
let item = Box::new(builtin.into_builtin());
self.builtins.insert(name.to_string(), item);
}
pub fn iter(&self) -> Iter<'_, String, Box<dyn Builtin>> {
self.builtins.iter()
}
#[allow(clippy::borrowed_box)]
pub fn get(&self, name: &'static str) -> Option<&Box<dyn Builtin>> {
self.builtins.get(name)
}
}
impl Default for Builtins {
fn default() -> Self {
let mut builtins = Builtins::new();
builtins.insert("exit", exit_builtin);
builtins.insert("help", help_builtin);
builtins.insert("alias", alias_builtin);
builtins.insert("cd", cd_builtin);
builtins.insert("type", type_builtin);
builtins.insert("export", export_builtin);
builtins.insert("history", HistoryBuiltin {});
builtins.insert("jobs", jobs_builtin);
builtins.insert("source", source_builtin);
builtins.insert("debug", debug_builtin);
builtins.insert("unalias", unalias_builtin);
builtins
}
}
pub trait Builtin {
fn run(&self, sh: &Shell, states: &States, args: &Vec<String>) -> Result<CmdOutput>;
}
pub trait IntoBuiltin<Input> {
type Builtin: Builtin;
fn into_builtin(self) -> Self::Builtin;
}
pub struct FunctionBuiltin<Input, F> {
f: F,
marker: PhantomData<fn() -> Input>,
}
impl<F> Builtin for FunctionBuiltin<Vec<String>, F>
where
for<'a, 'b> &'a F: Fn(&Vec<String>) -> Result<CmdOutput>,
{
fn run(&self, _sh: &Shell, _ctx: &States, args: &Vec<String>) -> Result<CmdOutput> {
fn call_inner(
f: impl Fn(&Vec<String>) -> Result<CmdOutput>,
args: &Vec<String>,
) -> Result<CmdOutput> {
f(&args)
}
call_inner(&self.f, &args)
}
}
macro_rules! impl_builtin {
(
$($params:ident),*
) => {
#[allow(non_snake_case)]
#[allow(unused)]
impl<F, $($params: Param),+> Builtin for FunctionBuiltin<($($params,)+), F>
where
for<'a, 'b> &'a F:
Fn( $($params),+,&Vec<String>)->Result<CmdOutput> +
Fn( $(<$params as Param>::Item<'b>),+,&Vec<String> )->Result<CmdOutput>
{
fn run(&self, sh: &Shell,states: &States, args: &Vec<String>)->Result<CmdOutput> {
fn call_inner<$($params),+>(
f: impl Fn($($params),+,&Vec<String>)->Result<CmdOutput>,
$($params: $params),*
,args:&Vec<String>
) -> Result<CmdOutput>{
f($($params),*,args)
}
$(
let $params = $params::retrieve(sh,states).unwrap();
)+
call_inner(&self.f, $($params),+,&args)
}
}
}
}
impl<F> IntoBuiltin<()> for F
where
for<'a, 'b> &'a F: Fn(&Vec<String>) -> Result<CmdOutput>,
{
type Builtin = FunctionBuiltin<Vec<String>, Self>;
fn into_builtin(self) -> Self::Builtin {
FunctionBuiltin {
f: self,
marker: Default::default(),
}
}
}
impl<B: Builtin> IntoBuiltin<B> for B {
type Builtin = B;
fn into_builtin(self) -> Self::Builtin {
self
}
}
macro_rules! impl_into_builtin {
(
$($params:ident),+
) => {
impl<F, $($params: Param),+> IntoBuiltin<($($params,)*)> for F
where
for<'a, 'b> &'a F:
Fn( $($params),+,&Vec<String> ) ->Result<CmdOutput>+
Fn( $(<$params as Param>::Item<'b>),+,&Vec<String> )->Result<CmdOutput>
{
type Builtin = FunctionBuiltin<($($params,)+), Self>;
fn into_builtin(self) -> Self::Builtin {
FunctionBuiltin {
f: self,
marker: Default::default(),
}
}
}
}
}
all_the_tuples!(impl_builtin, impl_into_builtin);