shrs_core/builtin/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//! Builtin commands
//!
//! The main difference between builtin commands and external commands is that builtin commands
//! have access to the shell's context during execution. This may be useful if you specifically
//! need to query or mutate the shell's state. Some uses of this include switching the working
//! directory, calling hooks or accessing the state store.

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,
};
// TODO could prob just be a map, to support arbitrary (user defined even) number of builtin commands
// just provide an easy way to override the default ones
/// Store for all registered builtin commands
pub struct Builtins {
    builtins: HashMap<String, Box<dyn Builtin>>,
}

// TODO a lot of this api is silly, perhaps just expose the entire hashmap
impl Builtins {
    /// Initializes a builtin container with no registered builtins
    ///
    /// You probably want to use `Builtins::default()` instead to get some sensible default
    /// builtins to use, then override the ones you want
    pub fn new() -> Self {
        Builtins {
            builtins: HashMap::new(),
        }
    }

    /// Insert a builtin command of the given name
    ///
    /// If a builtin of the same name has been registered, it will be overwritten.
    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);
    }

    /// Get iterator of all registered builtin commands
    pub fn iter(&self) -> Iter<'_, String, Box<dyn Builtin>> {
        self.builtins.iter()
    }

    /// Find a builtin by name
    // Clippy thinks this shouldn't be a box, but it does not compile if you follow the warning
    #[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
    }
}

/// Implement this trait to define your own builtin command
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);