diff --git a/engine/src/core/mod.rs b/engine/src/core/mod.rs index cebc91e..fe2a8cd 100644 --- a/engine/src/core/mod.rs +++ b/engine/src/core/mod.rs @@ -1,16 +1,14 @@ -pub mod commands; -pub mod repl; -pub mod splash; pub mod renderer; +pub mod repl; + use anyhow::Result; use renderer::App; use winit::event_loop::{ControlFlow, EventLoop}; - - pub fn init_renderer() -> Result<()> { let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); let mut app = App::default(); Ok(event_loop.run_app(&mut app)?) -} \ No newline at end of file +} + diff --git a/engine/src/core/repl.rs b/engine/src/core/repl.rs deleted file mode 100644 index 899c606..0000000 --- a/engine/src/core/repl.rs +++ /dev/null @@ -1,248 +0,0 @@ -use super::commands; -use chrono::Local; -use lazy_static::lazy_static; -use log2::{debug, error, info}; -use parking_lot::RwLock; -use reedline::{Prompt, Reedline, Signal}; -use regex::Regex; -use std::{borrow::Borrow, collections::HashMap, sync::Arc}; - -struct ZPrompt { - left_text: String, - right_text: String, -} - -#[derive(Clone, Debug)] -enum Callable { - Simple(fn()), - WithArgs(fn(Vec)), -} -#[derive(Debug)] -pub struct Command { - pub name: &'static str, - pub description: Option<&'static str>, - function: Callable, - pub arg_count: u8, -} - -impl Command { - pub fn execute(&self, args: Option>) { - //debug!("Executing command: {}", self.name); - match &self.function { - Callable::Simple(f) => { - if let Some(args) = args { - error!( - "Command expected 0 arguments but {} args were given. Ignoring..", - args.len() - ); - } - f() - } - Callable::WithArgs(f) => match args { - Some(args) => f(args), - None => error!("Command expected arguments but received 0"), - }, - } - } -} - -impl std::fmt::Display for Command { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Name: {}\n\t{}", - self.name, - self.description.unwrap_or("No description") - ) - } -} - -lazy_static! { - pub static ref COMMAND_LIST: Arc = Arc::new(CommandList::new()); -} - -pub struct CommandList { - pub commands: RwLock>, - pub aliases: RwLock>, -} - -impl CommandList { - fn new() -> Self { - CommandList { - commands: RwLock::new(Vec::new()), - aliases: RwLock::new(HashMap::new()), - } - } - - fn add_command( - &self, - name: &'static str, - description: Option<&'static str>, - func: Callable, - arg_count: Option, - ) { - debug!("Adding command: {}", name); - let mut commands = self.commands.write(); - - commands.push(Command { - name, - description, - function: func, - arg_count: arg_count.unwrap_or(0), - }); - } - fn add_alias(&self, name: String, alias: String) { - //println!("Input alias: {}", alias); - if self.aliases.read().contains_key(&alias) { - error!("Alias: '{}' already exists", alias); - return; - } - let mut commands = self.commands.write(); - if let Some(command) = commands.iter_mut().find(|cmd| cmd.name == name) { - info!("Adding alias: {} for cmd: {}", alias, command.name); - self.aliases - .write() - .insert(alias.to_string(), name.to_string()); - } else { - error!("Command: '{}' was not found", name); - } - } - - fn execute_command(&self, mut name: String, args: Option>) { - //info!("received input command: {}", name); - let commands = self.commands.borrow(); - if self.aliases.read().contains_key(&name) { - name = self - .aliases - .read() - .get_key_value(&name) - .unwrap() - .1 - .to_string(); - debug!("changed to {}", name); - } - if let Some(command) = commands.read().iter().find(|cmd| cmd.name == name) { match (command.arg_count, args.as_ref()) { - (expected, Some(args_vec)) if args_vec.len() != expected as usize => { - eprintln!( - "Command: '{}' expected {} arguments but received {}", - name, - expected, - args_vec.len() - ); - } - (_, _) => command.execute(args), - } } - } -} - -impl Prompt for ZPrompt { - fn render_prompt_left(&self) -> std::borrow::Cow { - std::borrow::Cow::Borrowed(&self.left_text) - } - - fn render_prompt_right(&self) -> std::borrow::Cow { - std::borrow::Cow::Borrowed(&self.right_text) - } - - fn render_prompt_history_search_indicator( - &self, - _history_search: reedline::PromptHistorySearch, - ) -> std::borrow::Cow { - std::borrow::Cow::Borrowed("") - } - - fn render_prompt_indicator( - &self, - prompt_mode: reedline::PromptEditMode, - ) -> std::borrow::Cow { - match prompt_mode { - reedline::PromptEditMode::Default => std::borrow::Cow::Borrowed(">>"), - reedline::PromptEditMode::Emacs => { - let timestamp = Local::now().format("[%H:%M:%S.%3f/SHELL] >>\t").to_string(); - std::borrow::Cow::Owned(timestamp) - } - reedline::PromptEditMode::Vi(_) => std::borrow::Cow::Borrowed("vi>>"), - reedline::PromptEditMode::Custom(_) => std::borrow::Cow::Borrowed("custom>>"), - } - } - - fn render_prompt_multiline_indicator(&self) -> std::borrow::Cow { - std::borrow::Cow::Borrowed("><") - } -} - -fn setup() { - COMMAND_LIST.add_command( - "hello", - Some("test"), - Callable::Simple(commands::say_hello), - None, - ); - COMMAND_LIST.add_command("exit", None, Callable::Simple(commands::exit), None); - COMMAND_LIST.add_command("clear", None, Callable::Simple(commands::clear), None); - COMMAND_LIST.add_command("echo", None, Callable::WithArgs(commands::echo), Some(1)); - COMMAND_LIST.add_command("cmds", None, Callable::Simple(commands::cmds), None); - COMMAND_LIST.add_alias("cmds".to_string(), "help".to_string()); - COMMAND_LIST.add_alias("cmds".to_string(), "cmd_list".to_string()); - COMMAND_LIST.add_alias("hello".to_string(), "exit".to_string()); - COMMAND_LIST.add_alias("clear".to_string(), "exit".to_string()); -} - -pub async fn handle_repl() { - let mut line_editor = Reedline::create(); - setup(); - - loop { - let sig = line_editor.read_line(&ZPrompt { - left_text: String::new(), - right_text: "<<".to_string(), - }); - - match sig { - Ok(Signal::Success(buffer)) => { - if buffer == "exit" { - std::process::exit(0); - } else { - evaluate_command(&buffer); - } - } - Ok(Signal::CtrlC) => { - println!("\nCONTROL+C RECEIVED, TERMINATING"); - std::process::exit(0); - } - err => { - eprintln!("Error: {:?}", err); - } - } - } -} - -fn evaluate_command(input: &str) { - if input.trim().is_empty() { - return; - } - - let pattern = Regex::new(r"[;|\n]").unwrap(); - let commands: Vec<&str> = pattern.split(input).collect(); - - for command in commands { - let command = command.trim(); - if command.is_empty() { - println!("Empty command, skipping."); - continue; - } - - let tokens: Vec<&str> = command.split_whitespace().collect(); - if tokens.is_empty() { - return; - } - - let cmd_name = tokens[0]; - let args: Vec = tokens[1..].iter().map(|&s| s.to_string()).collect(); - - COMMAND_LIST.execute_command( - cmd_name.to_string(), - if args.is_empty() { None } else { Some(args) }, - ); - } -} diff --git a/engine/src/core/commands.rs b/engine/src/core/repl/commands.rs similarity index 72% rename from engine/src/core/commands.rs rename to engine/src/core/repl/commands.rs index 45884ac..c4d2437 100644 --- a/engine/src/core/commands.rs +++ b/engine/src/core/repl/commands.rs @@ -1,34 +1,34 @@ +use super::COMMAND_LIST; use std::process::Command; - use log2::{debug, info}; -use crate::core::repl::COMMAND_LIST; - -pub fn say_hello() { - println!("Hello from your new command!"); +pub(crate) fn say_hello() { + println!("Hello, World!"); } -pub fn echo(args: Vec) { +pub(crate) fn echo(args: Vec) { debug!("{}", args.join(" ")); println!("{}", args.join(" ")) } -pub fn exit() { +pub(crate) fn exit() { debug!("Exiting..."); std::process::exit(0) } -pub fn clear() { + +pub(crate) fn clear() { info!("Clearing screen..., running command"); let _result = if cfg!(target_os = "windows") { debug!("target_os is windows"); Command::new("cmd").args(["/c", "cls"]).spawn() } else { - debug!("target_os was unix"); + debug!("target_os is unix"); // "clear" or "tput reset" Command::new("tput").arg("reset").spawn() }; } -pub fn cmds() { + +pub(crate) fn help() { println!("Commands:"); for cmd in COMMAND_LIST.commands.read().iter() { println!("{:#}", cmd); diff --git a/engine/src/core/repl/mod.rs b/engine/src/core/repl/mod.rs new file mode 100644 index 0000000..6a0ad4c --- /dev/null +++ b/engine/src/core/repl/mod.rs @@ -0,0 +1,135 @@ +pub mod commands; +pub mod repl; + +use lazy_static::lazy_static; +use log2::{debug, error, info}; +use parking_lot::RwLock; +use std::{borrow::Borrow, collections::HashMap, sync::Arc}; + +lazy_static! { + pub static ref COMMAND_LIST: Arc = Arc::new(CommandList::new()); +} + +#[derive(Clone, Debug)] +enum Callable { + Simple(fn()), + WithArgs(fn(Vec)), +} + +#[derive(Debug)] +pub struct Command { + pub name: &'static str, + pub description: Option<&'static str>, + function: Callable, + pub arg_count: u8, +} + +impl Command { + pub fn execute(&self, args: Option>) { + //debug!("Executing command: {}", self.name); + match &self.function { + Callable::Simple(f) => { + if let Some(args) = args { + error!( + "Command expected 0 arguments but {} args were given. Ignoring..", + args.len() + ); + } + f() + } + Callable::WithArgs(f) => match args { + Some(args) => f(args), + None => error!("Command expected arguments but received 0"), + }, + } + } +} + +impl std::fmt::Display for Command { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + " {:<10} {}", + self.name, + self.description.unwrap_or("No description available") + ) + } +} + +pub struct CommandList { + pub commands: RwLock>, + pub aliases: RwLock>, +} + +impl CommandList { + fn new() -> Self { + CommandList { + commands: RwLock::new(Vec::new()), + aliases: RwLock::new(HashMap::new()), + } + } + + fn add_command( + &self, + name: &'static str, + description: Option<&'static str>, + func: Callable, + arg_count: Option, + ) { + debug!("Adding command: {}", name); + let mut commands = self.commands.write(); + + commands.push(Command { + name, + description, + function: func, + arg_count: arg_count.unwrap_or(0), + }); + } + + fn add_alias(&self, name: String, alias: String) { + //println!("Input alias: {}", alias); + if self.aliases.read().contains_key(&alias) { + error!("Alias: '{}' already exists", alias); + return; + } + let mut commands = self.commands.write(); + if let Some(command) = commands.iter_mut().find(|cmd| cmd.name == name) { + info!("Adding alias: {} for cmd: {}", alias, command.name); + self.aliases + .write() + .insert(alias.to_string(), name.to_string()); + } else { + error!("Command: '{}' was not found", name); + } + } + + fn execute_command(&self, mut name: String, args: Option>) { + //info!("received input command: {}", name); + let commands = self.commands.borrow(); + if self.aliases.read().contains_key(&name) { + name = self + .aliases + .read() + .get_key_value(&name) + .unwrap() + .1 + .to_string(); + + debug!("changed to {}", name); + } + if let Some(command) = commands.read().iter().find(|cmd| cmd.name == name) { + match (command.arg_count, args.as_ref()) { + (expected, Some(args_vec)) if args_vec.len() != expected as usize => { + eprintln!( + "Command: '{}' expected {} arguments but received {}", + name, + expected, + args_vec.len() + ); + } + (_, _) => command.execute(args), + } + } + } +} diff --git a/engine/src/core/repl/repl.rs b/engine/src/core/repl/repl.rs new file mode 100644 index 0000000..12c5667 --- /dev/null +++ b/engine/src/core/repl/repl.rs @@ -0,0 +1,146 @@ +use super::{commands, Callable, COMMAND_LIST}; +use chrono::Local; +use reedline::{Prompt, Reedline, Signal}; +use regex::Regex; +use std::{borrow::Borrow, collections::HashMap, sync::Arc}; + +fn register_commands() { + COMMAND_LIST.add_command( + "hello", + Some("Displays \"Hello World\"!"), + Callable::Simple(commands::say_hello), + None, + ); + + COMMAND_LIST.add_command( + "exit", + Some("Exits the application gracefully."), + Callable::Simple(commands::exit), + None, + ); + + COMMAND_LIST.add_command( + "clear", + Some("Clears the terminal screen."), + Callable::Simple(commands::clear), + None, + ); + + COMMAND_LIST.add_command( + "echo", + Some("Prints the provided arguments back to the terminal."), + Callable::WithArgs(commands::echo), + Some(1), // Requires at least one argument + ); + + COMMAND_LIST.add_command( + "help", + Some("Displays a list of all available commands."), + Callable::Simple(commands::help), + None, + ); + + // EXAMPLE + // Adding aliases for commands + COMMAND_LIST.add_alias("cls".to_string(), "clear".to_string()); // Likely unintended; consider removing or renaming. +} + +struct ZPrompt { + left_text: String, + right_text: String, +} + +impl Prompt for ZPrompt { + fn render_prompt_left(&self) -> std::borrow::Cow { + std::borrow::Cow::Borrowed(&self.left_text) + } + + fn render_prompt_right(&self) -> std::borrow::Cow { + std::borrow::Cow::Borrowed(&self.right_text) + } + + fn render_prompt_history_search_indicator( + &self, + _history_search: reedline::PromptHistorySearch, + ) -> std::borrow::Cow { + std::borrow::Cow::Borrowed("") + } + + fn render_prompt_indicator( + &self, + prompt_mode: reedline::PromptEditMode, + ) -> std::borrow::Cow { + match prompt_mode { + reedline::PromptEditMode::Default => std::borrow::Cow::Borrowed(">>"), + reedline::PromptEditMode::Emacs => { + let timestamp = Local::now().format("[%H:%M:%S.%3f/SHELL] >>\t").to_string(); + std::borrow::Cow::Owned(timestamp) + } + reedline::PromptEditMode::Vi(_) => std::borrow::Cow::Borrowed("vi>>"), + reedline::PromptEditMode::Custom(_) => std::borrow::Cow::Borrowed("custom>>"), + } + } + + fn render_prompt_multiline_indicator(&self) -> std::borrow::Cow { + std::borrow::Cow::Borrowed("><") + } +} + +fn evaluate_command(input: &str) { + if input.trim().is_empty() { + return; + } + + let pattern = Regex::new(r"[;|\n]").unwrap(); + let commands: Vec<&str> = pattern.split(input).collect(); + + for command in commands { + let command = command.trim(); + if command.is_empty() { + println!("Empty command, skipping."); + continue; + } + + let tokens: Vec<&str> = command.split_whitespace().collect(); + if tokens.is_empty() { + return; + } + + let cmd_name = tokens[0]; + let args: Vec = tokens[1..].iter().map(|&s| s.to_string()).collect(); + + COMMAND_LIST.execute_command( + cmd_name.to_string(), + if args.is_empty() { None } else { Some(args) }, + ); + } +} + +pub async fn handle_repl() { + let mut line_editor = Reedline::create(); + register_commands(); + + loop { + let sig = line_editor.read_line(&ZPrompt { + left_text: String::new(), + right_text: "<<".to_string(), + }); + + match sig { + Ok(Signal::Success(buffer)) => { + if buffer == "exit" { + std::process::exit(0); + } else { + evaluate_command(&buffer); + } + } + Ok(Signal::CtrlC) => { + println!("\nCONTROL+C RECEIVED, TERMINATING"); + std::process::exit(0); + } + err => { + eprintln!("Error: {:?}", err); + } + } + } +} diff --git a/engine/src/core/splash.rs b/engine/src/core/splash.rs deleted file mode 100644 index f35c870..0000000 --- a/engine/src/core/splash.rs +++ /dev/null @@ -1,26 +0,0 @@ -use colored::Colorize; - -pub fn print_splash() { - println!( - r#" - &&&&&&&&&&& - &&&&&&&&&&&&&&&&& - &&&&&&&&&&&&&&&&&&&&& - && &&&&&&&&& - && &&&&&&&&& -&&&&&&&&&&&& &&&&&&&&&&& -&&&&&&&&&&&&& &&&&&&&&&&&& -&&&&&&&&&&&&& &&&&&&&&&&&&& -&&&&&&&&&&&& &&&&&&&&&&&&& -&&&&&&&&&&& &&&&&&&&&&&& - &&&&&&&&& && - &&&&&&&&& && - &&&&&&&&&&&&&&&&&&&&& - &&&&&&&&&&&&&&&&& - &&&&&&&&&&& - - Version: {} - "#, - env!("CARGO_PKG_VERSION").yellow().italic().underline() - ); -} diff --git a/engine/src/main.rs b/engine/src/main.rs index 5baa920..ec45bad 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -1,18 +1,44 @@ use anyhow::Result; +use colored::Colorize; use log2::info; pub mod core; +pub fn print_splash() { + println!( + r#" + &&&&&&&&&&& + &&&&&&&&&&&&&&&&& + &&&&&&&&&&&&&&&&&&&&& + && &&&&&&&&& + && &&&&&&&&& +&&&&&&&&&&&& &&&&&&&&&&& +&&&&&&&&&&&&& &&&&&&&&&&&& +&&&&&&&&&&&&& &&&&&&&&&&&&& +&&&&&&&&&&&& &&&&&&&&&&&&& +&&&&&&&&&&& &&&&&&&&&&&& + &&&&&&&&& && + &&&&&&&&& && + &&&&&&&&&&&&&&&&&&&&& + &&&&&&&&&&&&&&&&& + &&&&&&&&&&& + + Version: {} + "#, + env!("CARGO_PKG_VERSION").yellow().italic().underline() + ); +} + #[tokio::main] async fn main() -> Result<()> { let _log2 = log2::open("z.log").tee(true).level("debug").start(); info!("Initalizing Engine"); let shell_thread = tokio::task::spawn(async { info!("Shell thread started"); - core::repl::handle_repl().await; + core::repl::repl::handle_repl().await; }); - core::splash::print_splash(); + print_splash(); info!("Engine Initalized"); core::init_renderer()?; shell_thread.await?;