parent
dca88dc1db
commit
8416e48ab2
30 changed files with 528 additions and 1088 deletions
151
engine/src/core/repl/handler.rs
Normal file
151
engine/src/core/repl/handler.rs
Normal file
|
@ -0,0 +1,151 @@
|
|||
use std::collections::HashMap;
|
||||
use colored::Colorize;
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::RwLock;
|
||||
lazy_static! {
|
||||
pub static ref COMMAND_MANAGER: RwLock<CommandManager> = RwLock::new(CommandManager::init());
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! commands {
|
||||
[$($command:ty),*] => [
|
||||
$(
|
||||
{
|
||||
let mut manager = $crate::core::repl::handler::COMMAND_MANAGER.write();
|
||||
manager.add_command(Box::new(<$command>::default()));
|
||||
}
|
||||
)*
|
||||
];
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! alias {
|
||||
($($alias:expr => $command:expr),*) => {
|
||||
$(
|
||||
{
|
||||
let mut manager = $crate::COMMAND_MANAGER.write();
|
||||
manager.add_alias($alias, $command);
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
fn hamming_distance(a: &str, b: &str) -> Option<usize> {
|
||||
if a.len() != b.len() {
|
||||
return None;
|
||||
}
|
||||
Some(
|
||||
a.chars()
|
||||
.zip(b.chars())
|
||||
.filter(|(char_a, char_b)| char_a != char_b)
|
||||
.count(),
|
||||
)
|
||||
}
|
||||
|
||||
fn edit_distance(a: &str, b: &str) -> usize {
|
||||
let m = a.len();
|
||||
let n = b.len();
|
||||
|
||||
let mut dp = vec![vec![0; n + 1]; m + 1];
|
||||
|
||||
for i in 0..=m {
|
||||
for j in 0..=n {
|
||||
if i == 0 {
|
||||
dp[i][j] = j;
|
||||
} else if j == 0 {
|
||||
dp[i][j] = i;
|
||||
} else if a.chars().nth(i - 1) == b.chars().nth(j - 1) {
|
||||
dp[i][j] = dp[i - 1][j - 1];
|
||||
} else {
|
||||
dp[i][j] = 1 + dp[i - 1][j - 1].min(dp[i - 1][j]).min(dp[i][j - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dp[m][n]
|
||||
}
|
||||
|
||||
fn check_similarity(target: &str) -> Option<String> {
|
||||
let max_hamming_distance: usize = 2;
|
||||
let max_edit_distance: usize = 2;
|
||||
let mut best_match: Option<String> = None;
|
||||
let mut best_distance = usize::MAX;
|
||||
|
||||
for (cmd_name,_) in COMMAND_MANAGER.read().get_commands() {
|
||||
if let Some(hamming_dist) = hamming_distance(target, cmd_name) {
|
||||
if hamming_dist <= max_hamming_distance && hamming_dist < best_distance {
|
||||
best_distance = hamming_dist;
|
||||
best_match = Some(String::from(cmd_name));
|
||||
}
|
||||
} else {
|
||||
let edit_dist = edit_distance(target, cmd_name);
|
||||
if edit_dist <= max_edit_distance && edit_dist < best_distance {
|
||||
best_distance = edit_dist;
|
||||
best_match = Some(String::from(cmd_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
best_match
|
||||
}
|
||||
|
||||
pub struct CommandManager {
|
||||
pub commands: HashMap<String, Box<dyn Command>>,
|
||||
pub aliases: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl CommandManager {
|
||||
pub fn init() -> CommandManager {
|
||||
CommandManager {
|
||||
commands: HashMap::new(),
|
||||
aliases: HashMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn get_commands(&self) -> std::collections::hash_map::Iter<'_, String, Box<dyn Command>> {
|
||||
self.commands.iter()
|
||||
}
|
||||
pub fn execute_command(&self,command: &str,args: Option<Vec<String>>) -> Result<(),anyhow::Error> {
|
||||
if let Some(command) = self.commands.get(command) {
|
||||
command.execute(args)?;
|
||||
Ok(())
|
||||
} else {
|
||||
println!("Command '{}' not found.", command);
|
||||
let corrected_cmd = check_similarity(command);
|
||||
if corrected_cmd.is_some() {
|
||||
println!("Command: {} was not found. Did you mean {}?",command.red().bold(),corrected_cmd
|
||||
.expect("A command was editied during execution, something has gone seriously wrong").green().bold().italic());
|
||||
return Ok(());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(&self, command: &str,args: Option<Vec<String>>) -> Result<(), anyhow::Error> {
|
||||
match self.aliases.get(command) {
|
||||
Some(command) => self.execute(command,args),
|
||||
// check to see if we are using an alias or the command just doesnt exist
|
||||
None => {
|
||||
self.execute_command(command,args)?;
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn add_command(&mut self, command: Box<dyn Command>) {
|
||||
self.commands.insert(command.get_name().to_lowercase(), command);
|
||||
}
|
||||
pub fn add_alias(&mut self, alias: &str, command: &str) {
|
||||
self.aliases.insert(alias.to_string(), command.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Command: Send + Sync {
|
||||
fn execute(&self, args: Option<Vec<String>>) -> Result<(),anyhow::Error>;
|
||||
fn undo(&self);
|
||||
fn redo(&self);
|
||||
fn get_description(&self) -> String;
|
||||
fn get_name(&self) -> String;
|
||||
fn get_help(&self) -> String;
|
||||
fn get_params(&self) -> String;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue