improve error handling and add metadata
This commit is contained in:
parent
13893e96a9
commit
43fd5966b7
17 changed files with 1162 additions and 787 deletions
|
@ -1,18 +1,17 @@
|
|||
use std::{fs, path::PathBuf, str::FromStr};
|
||||
|
||||
use anyhow::anyhow;
|
||||
|
||||
use parking_lot::RwLock;
|
||||
use regex::Regex;
|
||||
|
||||
use super::{handler::Command, input::tokenize};
|
||||
use crate::core::repl::handler::COMMAND_MANAGER;
|
||||
use crate::error::{ZenyxError,ZenyxErrorKind};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct HelpCommand;
|
||||
|
||||
impl Command for HelpCommand {
|
||||
fn execute(&self, _args: Option<Vec<String>>) -> Result<(), anyhow::Error> {
|
||||
fn execute(&self, _args: Option<Vec<String>>) -> Result<(), ZenyxError> {
|
||||
let manager = COMMAND_MANAGER.read();
|
||||
println!("Available commands:\n");
|
||||
|
||||
|
@ -55,11 +54,12 @@ impl Command for HelpCommand {
|
|||
String::from("Help")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ClearCommand;
|
||||
|
||||
impl Command for ClearCommand {
|
||||
fn execute(&self, _args: Option<Vec<String>>) -> Result<(), anyhow::Error> {
|
||||
fn execute(&self, _args: Option<Vec<String>>) -> Result<(), ZenyxError> {
|
||||
println!("Clearing screen..., running command");
|
||||
let _result = if cfg!(target_os = "windows") {
|
||||
std::process::Command::new("cmd")
|
||||
|
@ -96,12 +96,16 @@ impl Command for ClearCommand {
|
|||
pub struct ExitCommand;
|
||||
|
||||
impl Command for ExitCommand {
|
||||
fn execute(&self, args: Option<Vec<String>>) -> Result<(), anyhow::Error> {
|
||||
fn execute(&self, args: Option<Vec<String>>) -> Result<(), ZenyxError> {
|
||||
match args {
|
||||
Some(args) => {
|
||||
let exit_code = args[0].parse()?;
|
||||
let exit_code = args[0].parse().map_err(|e| {
|
||||
ZenyxError::builder(ZenyxErrorKind::CommandParsing)
|
||||
.with_message("Failed to parse exit code")
|
||||
.with_source(e)
|
||||
.build()
|
||||
})?;
|
||||
std::process::exit(exit_code);
|
||||
// Ok(())
|
||||
}
|
||||
None => {
|
||||
std::process::exit(0);
|
||||
|
@ -133,18 +137,31 @@ impl Command for ExitCommand {
|
|||
String::from("None")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ExecFile;
|
||||
|
||||
impl Command for ExecFile {
|
||||
fn execute(&self, args: Option<Vec<String>>) -> Result<(), anyhow::Error> {
|
||||
fn execute(&self, args: Option<Vec<String>>) -> Result<(), ZenyxError> {
|
||||
match args {
|
||||
Some(args) => {
|
||||
let file_path = PathBuf::from_str(&args[0])?;
|
||||
let file_path = PathBuf::from_str(&args[0]).map_err(|e| {
|
||||
ZenyxError::builder(ZenyxErrorKind::CommandParsing)
|
||||
.with_message("Invalid file path")
|
||||
.with_source(e)
|
||||
.build()
|
||||
})?;
|
||||
if file_path.extension().is_some() && file_path.extension().unwrap() != "zensh" {
|
||||
return Err(anyhow!("Selected file was not a zensh file"));
|
||||
return Err(ZenyxError::builder(ZenyxErrorKind::CommandParsing)
|
||||
.with_message("Selected file was not a zensh file")
|
||||
.build());
|
||||
} else {
|
||||
let zscript = fs::read_to_string(file_path)?;
|
||||
let zscript = fs::read_to_string(file_path).map_err(|e| {
|
||||
ZenyxError::builder(ZenyxErrorKind::Io)
|
||||
.with_message("Failed to read file")
|
||||
.with_source(e)
|
||||
.build()
|
||||
})?;
|
||||
if let Ok(command) = eval(zscript) {
|
||||
println!("{:#?}", command);
|
||||
for (cmd_name, cmd_args) in command {
|
||||
|
@ -163,7 +180,9 @@ impl Command for ExecFile {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
None => Err(anyhow!("Not enough argumentss")),
|
||||
None => Err(ZenyxError::builder(ZenyxErrorKind::CommandParsing)
|
||||
.with_message("Not enough arguments")
|
||||
.build()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,7 +213,7 @@ pub struct CounterCommand {
|
|||
}
|
||||
|
||||
impl Command for CounterCommand {
|
||||
fn execute(&self, _args: Option<Vec<String>>) -> Result<(), anyhow::Error> {
|
||||
fn execute(&self, _args: Option<Vec<String>>) -> Result<(), ZenyxError> {
|
||||
// Increment the counter
|
||||
let mut count = self.counter.write();
|
||||
*count += 1;
|
||||
|
@ -226,10 +245,11 @@ impl Command for CounterCommand {
|
|||
String::from("count")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PanicCommmand;
|
||||
impl Command for PanicCommmand {
|
||||
fn execute(&self, args: Option<Vec<String>>) -> Result<(), anyhow::Error> {
|
||||
fn execute(&self, args: Option<Vec<String>>) -> Result<(), ZenyxError> {
|
||||
if args.is_some() {
|
||||
let panic_msg = &args.unwrap()[0];
|
||||
panic!("{}", panic_msg)
|
||||
|
@ -260,12 +280,19 @@ impl Command for PanicCommmand {
|
|||
}
|
||||
}
|
||||
|
||||
fn eval(input: String) -> Result<Vec<(String, Option<Vec<String>>)>, anyhow::Error> {
|
||||
fn eval(input: String) -> Result<Vec<(String, Option<Vec<String>>)>, ZenyxError> {
|
||||
if input.trim().is_empty() {
|
||||
return Err(anyhow!("Input was empty"));
|
||||
return Err(ZenyxError::builder(ZenyxErrorKind::CommandParsing)
|
||||
.with_message("Input was empty")
|
||||
.build());
|
||||
}
|
||||
|
||||
let pattern = Regex::new(r"[;|\n]").unwrap();
|
||||
let pattern = Regex::new(r"[;|\n]").map_err(|e| {
|
||||
ZenyxError::builder(ZenyxErrorKind::CommandParsing)
|
||||
.with_message("Failed to compile regex")
|
||||
.with_source(e)
|
||||
.build()
|
||||
})?;
|
||||
let commands: Vec<&str> = pattern.split(&input).collect();
|
||||
let mut evaluted = vec![];
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
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());
|
||||
}
|
||||
use std::sync::LazyLock;
|
||||
use crate::error::{ZenyxError, ZenyxErrorKind};
|
||||
|
||||
pub static COMMAND_MANAGER: LazyLock<RwLock<CommandManager>> = LazyLock::new(|| { RwLock::new(CommandManager::init()) });
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! commands {
|
||||
[$($command:ty),*] => [
|
||||
|
@ -110,24 +111,28 @@ impl CommandManager {
|
|||
&self,
|
||||
command: &str,
|
||||
args: Option<Vec<String>>,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
) -> Result<(), ZenyxError> {
|
||||
if let Some(command) = self.commands.get(command) {
|
||||
command.execute(args)?;
|
||||
Ok(())
|
||||
} else {
|
||||
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());
|
||||
if let Some(corrected_cmd) = corrected_cmd {
|
||||
println!(
|
||||
"Command: {} was not found. Did you mean {}?",
|
||||
command.red().bold(),
|
||||
corrected_cmd.green().bold().italic()
|
||||
);
|
||||
}
|
||||
Err(anyhow::anyhow!("Command '{}' not found.", command))
|
||||
Err(ZenyxError::builder(ZenyxErrorKind::CommandExecution)
|
||||
.with_message(format!("Command '{}' not found.", command))
|
||||
.build())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(&self, command: &str, args: Option<Vec<String>>) -> Result<(), anyhow::Error> {
|
||||
pub fn execute(&self, command: &str, args: Option<Vec<String>>) -> Result<(), ZenyxError> {
|
||||
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(())
|
||||
|
@ -149,7 +154,7 @@ impl CommandManager {
|
|||
}
|
||||
|
||||
pub trait Command: Send + Sync {
|
||||
fn execute(&self, args: Option<Vec<String>>) -> Result<(), anyhow::Error>;
|
||||
fn execute(&self, args: Option<Vec<String>>) -> Result<(), ZenyxError>;
|
||||
fn undo(&self);
|
||||
fn redo(&self);
|
||||
fn get_description(&self) -> String;
|
||||
|
|
|
@ -15,6 +15,7 @@ use rustyline::{
|
|||
use tracing::{debug, error, info, warn};
|
||||
|
||||
use super::handler::COMMAND_MANAGER;
|
||||
use crate::error::{Result, ZenyxError, ZenyxErrorKind};
|
||||
|
||||
struct CommandCompleter;
|
||||
impl CommandCompleter {
|
||||
|
@ -129,29 +130,38 @@ pub fn tokenize(command: &str) -> Vec<String> {
|
|||
tokens
|
||||
}
|
||||
|
||||
pub fn parse_command(input: &str) -> anyhow::Result<Vec<String>> {
|
||||
let pattern = Regex::new(r"[;|\n]").unwrap();
|
||||
pub fn parse_command(input: &str) -> Result<Vec<String>> {
|
||||
let pattern = Regex::new(r"[;|\n]").map_err(|_| {
|
||||
ZenyxError::builder(ZenyxErrorKind::CommandParsing)
|
||||
.with_message("Failed to compile regex pattern")
|
||||
.build()
|
||||
})?;
|
||||
let commands: Vec<String> = pattern.split(input).map(String::from).collect();
|
||||
Ok(commands)
|
||||
}
|
||||
pub fn evaluate_command(input: &str) -> anyhow::Result<()> {
|
||||
|
||||
pub fn evaluate_command(input: &str) -> Result<()> {
|
||||
if input.trim().is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let pattern = Regex::new(r"[;|\n]").unwrap();
|
||||
let pattern = Regex::new(r"[;|\n]").map_err(|_| {
|
||||
ZenyxError::builder(ZenyxErrorKind::CommandParsing)
|
||||
.with_message("Failed to compile regex pattern")
|
||||
.build()
|
||||
})?;
|
||||
let commands: Vec<&str> = pattern.split(input).collect();
|
||||
|
||||
for command in commands {
|
||||
let command = command.trim();
|
||||
if command.is_empty() {
|
||||
println!("Empty command, skipping.");
|
||||
error!("Empty command, skipping.");
|
||||
continue;
|
||||
}
|
||||
|
||||
let tokens = tokenize(command);
|
||||
if tokens.is_empty() {
|
||||
println!("Empty command, skipping.");
|
||||
error!("Empty command, skipping.");
|
||||
continue;
|
||||
}
|
||||
let cmd_name = &tokens[0];
|
||||
|
@ -160,15 +170,20 @@ pub fn evaluate_command(input: &str) -> anyhow::Result<()> {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
match COMMAND_MANAGER.read().execute(cmd_name, args) {
|
||||
Ok(_) => continue,
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
COMMAND_MANAGER
|
||||
.read()
|
||||
.execute(cmd_name, args)
|
||||
.map_err(|e| {
|
||||
ZenyxError::builder(ZenyxErrorKind::CommandExecution)
|
||||
.with_message(format!("Failed to execute command: {cmd_name}"))
|
||||
.with_context(format!("{e}"))
|
||||
.build()
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handle_repl() -> anyhow::Result<()> {
|
||||
pub async fn handle_repl() -> Result<()> {
|
||||
let mut rl = Editor::<MyHelper, DefaultHistory>::new()?;
|
||||
rl.set_helper(Some(MyHelper {
|
||||
hinter: HistoryHinter::new(),
|
||||
|
@ -196,7 +211,7 @@ pub async fn handle_repl() -> anyhow::Result<()> {
|
|||
rl.add_history_entry(line.as_str())?;
|
||||
match evaluate_command(line.as_str()) {
|
||||
Ok(_) => continue,
|
||||
Err(e) => println!("{e}"),
|
||||
Err(e) => error!("{e}"),
|
||||
}
|
||||
}
|
||||
Err(ReadlineError::Interrupted) => {
|
||||
|
|
|
@ -6,6 +6,9 @@ pub mod commands;
|
|||
pub mod handler;
|
||||
pub mod input;
|
||||
|
||||
|
||||
|
||||
|
||||
pub fn setup() {
|
||||
commands!(
|
||||
HelpCommand,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue