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
//! A crate to help you easily build a repl
use std::collections::HashMap;
use std::io::{self, Write};

use crate::Interpreter;
/// The main Repl Struct which contains pretty much everything the crate has to offer
#[derive(Debug)]
pub struct Repl {
    /// the prompt that is displayed when asking for input
    prompt: String,
    /// arguments recieved from the repl, cleaned for \r\n and \n endings
    arguments: Vec<String>,
    /// the command for exiting, exists because exit functions won't work because of loop :(
    exit: String,
    /// all the functions in HashMap<String, fn(Vec<String>)> format, specified Vec<String>
    /// because of the limitations of function pointers
    pub(crate) functions: HashMap<String, fn(Vec<String>)>,
}
/// Repl methods
impl Repl {
    /// Takes argument from stdin and mutate self.arguments to it
    /// # Example
    /// ```rust,ignore
    /// use std::collections::HashMap;
    /// use repl_framework::Repl;
    /// fn test(Vec<String>) {
    /// println!("test!");
    /// }
    /// fn main() {
    ///     let mut hashmap = HashMap::new();
    ///     hashmap.insert("test".to_string(), test as fn(Vec<()>));
    ///     let mut repl = Repl::new(">>> ", hashmap);
    ///     repl.take_arg();
    /// }
    /// ```
    fn take_arg(&mut self) {
        self.arguments = self.take_arg_return();
    }
    /// same as take_arg, but returns the argument instead of storing it in self.argument
    pub fn take_arg_return(&self) -> Vec<String> {
        print!("{}", &self.prompt);
        // flushing stdout because print! does'nt do it by default
        io::stdout()
            .flush()
            .expect("something went wrong went flushing the buffer");
        let mut buf = String::new();
        match io::stdin().read_line(&mut buf) {
            Ok(_) => (),
            Err(err) => println!("{}", err),
        };
        // trimming \r\n and \n from the input
        buf = buf
            .trim_end_matches('\n')
            .trim_end_matches('\r')
            .to_string();
        let mut args = vec![];
        for i in buf.split(' ') {
            args.push(i.to_string());
        }
        args
    }
    /// returns a customized Repl
    /// currently not much different from normal new other than the choice of exit keyword
    /// # Example
    /// ```rust,ignore
    /// use repl_framework::Repl;
    /// fn main() {
    ///     let mut repl = Repl::custom(
    ///         prompt: &str,
    ///         exit: &str,
    ///         functions: HashMap<String, fn(Vec<String>)>
    ///     );
    /// }
    /// ```
    pub fn custom(prompt: &str, exit: &str, functions: HashMap<String, fn(Vec<String>)>) -> Repl {
        Repl {
            arguments: vec![String::new()],
            prompt: prompt.to_string(),
            exit: exit.to_string(),
            functions,
        }
    }
    /// returns new repl
    /// # Example
    /// ```rust,ignore
    /// use repl_framework::Repl;
    /// fn test(_: Vec<String>) {
    ///     println!("test");
    /// }
    /// fn main() {
    ///     let mut repl = Repl::new(">>> ");
    ///     repl.add_function("test", test as fn(Vec<String>));
    ///     repl.run();
    /// }
    /// ```
    pub fn new(prompt: &str) -> Repl {
        Repl {
            arguments: vec![String::new()],
            prompt: prompt.to_string(),
            exit: "exit".to_string(),
            functions: HashMap::<String, fn(Vec<String>)>::new(),
        }
    }
    pub fn from_interpreter(interpreter: Interpreter, prompt: &str, exit: &str) -> Repl {
        Repl {
            prompt: prompt.to_string(),
            arguments: Vec::new(),
            exit: exit.to_string(),
            functions: interpreter.functions,
        }
    }
    pub fn add_function(&mut self, name: String, func: fn(Vec<String>)) {
        self.functions.insert(name, func);
    }
    /// runs the repl
    /// # Example
    /// ```rust,ignore
    /// use repl_framework::Repl;
    /// use std::collections::HashMap;
    /// fn test(_: Vec<String>) {
    /// println!("test!");
    /// }
    /// fn main() {
    ///     let mut hashmap = HashMap::new();
    ///     hashmap.insert("test".to_string(), test as fn(Vec<String>));
    ///     let mut repl = Repl::new(">>> ", hashmap);
    ///     repl.run()
    /// }
    /// ```
    pub fn run(&mut self) {
        loop {
            self.take_arg();
            if self.arguments.concat() == self.exit {
                println!("Terminated REPL");
                break;
            }
            if self.functions.contains_key(&self.arguments[0]) {
                self.functions[&self.arguments[0]](
                    self.arguments[1..self.arguments.len()].to_vec(),
                );
            } else if self.functions.contains_key("") {
                self.functions[""](self.arguments[0..self.arguments.len()].to_vec());
            }
        }
    }
    /// Runs the repl in debug mode
    pub fn run_debug(&mut self) {
        loop {
            self.take_arg();
            if self.arguments.concat() == self.exit {
                println!("Terminated REPL");
                break;
            }
            if self.functions.contains_key(&self.arguments[0]) {
                self.functions[&self.arguments[0]](
                    self.arguments[1..self.arguments.len()].to_vec(),
                );
            }
            println!("{:?}", &self);
        }
    }
}