diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml deleted file mode 100644 index 763eaf5..0000000 --- a/.github/workflows/code-quality.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Code Quality - -on: [push, pull_request] - -jobs: - code-quality: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - override: true - components: clippy, rustfmt - - - name: Check formatting - run: cargo fmt -- --check - - - name: Run Clippy - run: cargo clippy -- -D warnings - - - name: Compilation Check - run: cargo check \ No newline at end of file diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b0c08ac..b0a72d0 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -13,26 +13,35 @@ jobs: # Credit to https://github.com/Far-Beyond-Dev/Horizon/blob/main/.github/workflows/main.yml check-version: runs-on: ubuntu-latest - outputs: - should_release: ${{ steps.check.outputs.should_release }} - version: ${{ steps.check.outputs.version }} steps: - uses: actions/checkout@v3 with: fetch-depth: 2 - - - name: Check if Cargo.toml version changed - id: check + - name: Get binary name + id: binary + run: | + BINARY_NAME=$(cargo metadata --format-version 1 | jq -r '.packages[0].targets[] | select(.kind[] | contains("bin")) | .name') + echo "name=$BINARY_NAME" >> "$GITHUB_OUTPUT" + - name: Check version change + id: version run: | - CURRENT_VERSION=$(grep -m1 version Cargo.toml | cut -d '"' -f2) - git checkout HEAD^1 - PREVIOUS_VERSION=$(grep -m1 version Cargo.toml | cut -d '"' -f2) - if [ "$CURRENT_VERSION" != "$PREVIOUS_VERSION" ]; then - echo "should_release=true" >> $GITHUB_OUTPUT - echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT - else - echo "should_release=false" >> $GITHUB_OUTPUT + git fetch + OLD_VERSION=$(git show HEAD^:Cargo.toml | grep -m 1 '^version = ' | cut -d '"' -f 2) + NEW_VERSION=$(grep -m 1 '^version = ' Cargo.toml | cut -d '"' -f 2) + if [ "$OLD_VERSION" != "$NEW_VERSION" ]; then + echo "changed=true" >> "$GITHUB_OUTPUT" + echo "version=$NEW_VERSION" >> "$GITHUB_OUTPUT" fi + - name: Create Release + if: steps.version.outputs.changed == 'true' + uses: softprops/action-gh-release@v1 + with: + tag_name: v${{ steps.version.outputs.version }} + name: Release v${{ steps.version.outputs.version }} + files: target/release/${{ steps.binary.outputs.name }} + generate_release_notes: true + draft: false + prerelease: false build: strategy: fail-fast: false @@ -76,7 +85,7 @@ jobs: - name: 🔧 Install Rust uses: actions-rs/toolchain@v1 with: - toolchain: stable + toolchain: nightly override: true target: ${{ matrix.target }} profile: minimal diff --git a/Cargo.toml b/Cargo.toml index 6e96b37..dd8f14c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["engine", "subcrates/zephyr", "plugin_api"] +members = ["engine","subcrates/zen_core"] [profile.dev] rpath = false @@ -32,4 +32,5 @@ incremental = true codegen-units = 512 [workspace.dependencies] -zephyr = { path = "./subcrates/zephyr" } +lazy_static = "1.5.0" +parking_lot = "0.12.3" diff --git a/engine/.gitignore b/engine/.gitignore deleted file mode 100644 index ea8c4bf..0000000 --- a/engine/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 3dc51af..230f3b7 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -1,23 +1,17 @@ [package] -name = "zenyx" +name = "engine" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] -anyhow = "1.0.93" -chrono = "0.4.38" -colored = "2.1.0" -lazy_static = "1.5.0" +anyhow = "1.0.94" +chrono = "0.4.39" +colored = "2.2.0" + +lazy_static.workspace = true log = "0.4.22" once_cell = "1.20.2" -parking_lot = "0.12.3" -rand = "0.8.5" +parking_lot.workspace = true regex = "1.11.1" rustyline = { version = "15.0.0", features = ["derive", "rustyline-derive"] } -thiserror = "2.0.3" -tokio = { version = "1.41.1", features = ["macros", "rt", "rt-multi-thread"] } -wgpu = "23.0.1" -winit = "0.30.5" -zephyr.workspace = true -plugin_api = { path = "../plugin_api" } -horizon-plugin-api = "0.1.13" +tokio = { version = "1.42.0", features = ["macros", "parking_lot", "rt", "rt-multi-thread"] } diff --git a/engine/src/utils/logger.rs b/engine/src/core/logger/mod.rs similarity index 99% rename from engine/src/utils/logger.rs rename to engine/src/core/logger/mod.rs index 765f0e8..7cd8814 100644 --- a/engine/src/utils/logger.rs +++ b/engine/src/core/logger/mod.rs @@ -81,4 +81,4 @@ impl Log for DynamicLogger { let mut writer = self.writer.lock(); writer.flush().unwrap(); } -} +} \ No newline at end of file diff --git a/engine/src/core/mod.rs b/engine/src/core/mod.rs index 2e15980..a831946 100644 --- a/engine/src/core/mod.rs +++ b/engine/src/core/mod.rs @@ -1,13 +1,3 @@ -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()?; - event_loop.set_control_flow(ControlFlow::Poll); - let mut app = App::default(); - Ok(event_loop.run_app(&mut app)?) -} +pub mod logger; +pub mod splash; \ No newline at end of file diff --git a/engine/src/core/renderer/ctx.rs b/engine/src/core/renderer/ctx.rs deleted file mode 100644 index d2045d8..0000000 --- a/engine/src/core/renderer/ctx.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::sync::Arc; - -use anyhow::Result; -use winit::window::Window; - -pub struct WgpuCtx<'window> { - device: wgpu::Device, - queue: wgpu::Queue, - surface: wgpu::Surface<'window>, - surface_config: wgpu::SurfaceConfiguration, - adapter: wgpu::Adapter, -} - -impl<'window> WgpuCtx<'window> { - pub async fn new(window: Arc) -> Result> { - let instnace = wgpu::Instance::default(); - let surface = instnace.create_surface(Arc::clone(&window))?; - let adapter = instnace - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::default(), - force_fallback_adapter: false, - compatible_surface: Some(&surface), - }) - .await - .expect("Failed to obtain render adapter"); - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - required_features: wgpu::Features::empty(), - required_limits: wgpu::Limits::downlevel_webgl2_defaults() - .using_resolution(adapter.limits()), - memory_hints: wgpu::MemoryHints::Performance, - }, - None, - ) - .await - .expect("Failed to create rendering device"); - - let size = window.inner_size(); - let width = size.width.max(1); - let height = size.height.max(1); - - let surface_config = surface - .get_default_config(&adapter, width, height) - .expect("Failed to get default surface configuration"); - surface.configure(&device, &surface_config); - - Ok(WgpuCtx { - device, - queue, - surface, - surface_config, - adapter, - }) - } - - pub fn new_blocking(window: Arc) -> Result> { - tokio::task::block_in_place(|| { - tokio::runtime::Runtime::new()?.block_on(async { WgpuCtx::new(window).await }) - }) - } - - pub fn resize(&mut self, new_size: (u32, u32)) { - let (width, height) = new_size; - self.surface_config.width = width.max(1); - self.surface_config.height = height.max(1); - self.surface.configure(&self.device, &self.surface_config); - } - - pub fn draw(&mut self) { - let surface_texture = self - .surface - .get_current_texture() - .expect("Failed to get surface texture"); - let view = surface_texture - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - let mut encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); - { - let rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: None, - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color { - r: 0.1, - g: 0.2, - b: 0.3, - a: 1.0, - }), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, - }); - } - self.queue.submit(Some(encoder.finish())); - surface_texture.present(); - } -} diff --git a/engine/src/core/renderer/mod.rs b/engine/src/core/renderer/mod.rs deleted file mode 100644 index 7a9147b..0000000 --- a/engine/src/core/renderer/mod.rs +++ /dev/null @@ -1,61 +0,0 @@ -pub mod ctx; -use std::sync::Arc; - -use ctx::WgpuCtx; -use log::{debug, trace}; -use winit::application::ApplicationHandler; -use winit::event::WindowEvent; -use winit::event_loop::ActiveEventLoop; -use winit::window::{Window, WindowId}; - -#[derive(Default)] -pub struct App<'window> { - window: Option>, - ctx: Option>, -} - -impl ApplicationHandler for App<'_> { - fn resumed(&mut self, event_loop: &ActiveEventLoop) { - if self.window.is_none() { - let win_attr = Window::default_attributes().with_title("Zenyx"); - let window = Arc::new( - event_loop - .create_window(win_attr) - .expect("create window err."), - ); - self.window = Some(window.clone()); - let wgpu_ctx = WgpuCtx::new_blocking(window.clone()).unwrap(); - self.ctx = Some(wgpu_ctx) - } - } - - fn window_event( - &mut self, - event_loop: &ActiveEventLoop, - _window_id: WindowId, - event: WindowEvent, - ) { - match event { - WindowEvent::CloseRequested => { - event_loop.exit(); - debug!("Window closed, exiting"); - std::process::exit(0) - } - WindowEvent::RedrawRequested => { - if let Some(ctx) = &mut self.ctx { - ctx.draw(); - } - } - WindowEvent::Resized(size) => { - if let (Some(wgpu_ctx), Some(window)) = (&mut self.ctx, &self.window) { - wgpu_ctx.resize(size.into()); - window.request_redraw(); - - let size_str: String = size.height.to_string() + "x" + &size.width.to_string(); - debug!("Window resized to {:?}", size_str); - } - } - _ => trace!("Unhandled window event"), - } - } -} diff --git a/engine/src/core/repl/commands.rs b/engine/src/core/repl/commands.rs index 4c1a8d9..0b1a38e 100644 --- a/engine/src/core/repl/commands.rs +++ b/engine/src/core/repl/commands.rs @@ -1,84 +1,253 @@ -use std::{ffi::OsStr, process::Command}; +use std::{fs, path::PathBuf, str::FromStr}; -use lazy_static::lazy_static; -use parking_lot::Mutex; +use anyhow::anyhow; +use parking_lot::RwLock; +use regex::Regex; -use super::COMMAND_LIST; -use crate::core::repl::exec::evaluate_command; -// increasing this value WILL cause a stack overflow -// attempt at your own risk - Caz -const MAX_RECURSION_DEPTH: usize = 500; +use crate::core::repl::handler::COMMAND_MANAGER; -lazy_static! { - static ref RECURSION_DEPTH: Mutex = parking_lot::Mutex::new(0); -} +use super::{handler::Command, input::tokenize}; + +#[derive(Default)] +pub struct HelpCommand; + +impl Command for HelpCommand { + fn execute(&self, _args: Option>) -> Result<(), anyhow::Error> { + let manager = COMMAND_MANAGER.read(); + println!("Available commands:\n"); + + for (_, command) in manager.get_commands() { + println!( + "Command: {}\n\tDescription: {}\n\tParameters: {}\n\tHelp: {}\n", + command.get_name(), + command.get_description(), + command.get_params(), + command.get_help() + ); + } + + if !manager.aliases.is_empty() { + println!("Aliases:"); + for (alias, command) in &manager.aliases { + println!("\t{} -> {}", alias, command); + } + } + Ok(()) + } + + fn undo(&self) {} + + fn redo(&self) {} + + fn get_description(&self) -> String { + String::from("help") + } + + fn get_help(&self) -> String { + String::from("Displays a list of available commands and their descriptions.") + } + + fn get_params(&self) -> String { + String::from("No parameters required.") + } -pub(crate) fn say_hello() -> anyhow::Result<()> { - println!("Hello, World!"); - Ok(()) + fn get_name(&self) -> String { + String::from("Help") + } } +#[derive(Default)] +pub struct ClearCommand; + +impl Command for ClearCommand { + fn execute(&self, _args: Option>) -> Result<(), anyhow::Error> { + println!("Clearing screen..., running command"); + let _result = if cfg!(target_os = "windows") { + std::process::Command::new("cmd").args(["/c", "cls"]).spawn() + } else { + std::process::Command::new("clear").spawn() + }; + Ok(()) + } + + fn undo(&self) {} -pub(crate) fn echo(args: Vec) -> anyhow::Result<()> { - println!("{}", args.join(" ")); - Ok(()) + fn redo(&self) {} + + fn get_description(&self) -> String { + String::from("A simple command that clears the terminal") + } + + fn get_name(&self) -> String { + String::from("clear") + } + + fn get_help(&self) -> String { + String::from("Clears the terminal") + } + + fn get_params(&self) -> String { + String::from("None") + } } -pub(crate) fn exit() -> anyhow::Result<()> { - println!("Exiting..."); - std::process::exit(0) +#[derive(Default)] +pub struct ExitCommand; + +impl Command for ExitCommand { + fn execute(&self, args: Option>) -> Result<(), anyhow::Error> { + match args { + Some(args) => { + + let exit_code = args[0].parse()?; + std::process::exit(exit_code); + // Ok(()) + }, + None => { + std::process::exit(0); + }, + } + } + + fn undo(&self) { + todo!() + } + + fn redo(&self) { + todo!() + } + + fn get_description(&self) -> String { + String::from("Gracefully exists the program") + } + + fn get_name(&self) -> String { + String::from("exit") + } + + fn get_help(&self) -> String { + String::from("Exits, probably") + } + + fn get_params(&self) -> String { + String::from("None") + } } +#[derive(Default)] +pub struct ExecFile; + +impl Command for ExecFile { + fn execute(&self, args: Option>) -> Result<(),anyhow::Error> { + match args { + Some(args) => { + + let file_path = PathBuf::from_str(&args[0])?; + if file_path.extension().is_some() && file_path.extension().unwrap() != "zensh" { + return Err(anyhow!("Selected file was not a zensh file")); + } else { + let zscript = fs::read_to_string(file_path)?; + if let Ok(command) = eval(zscript) { + println!("{:#?}",command); + for (cmd_name,cmd_args) in command { + COMMAND_MANAGER.read().execute(&cmd_name, cmd_args)? + } + } + } + Ok(()) + }, + None => { + Err(anyhow!("Not enough argumentss")) + }, + } + } + + fn undo(&self) {} -pub(crate) fn clear() -> anyhow::Result<()> { - println!("Clearing screen..., running command"); - let _result = if cfg!(target_os = "windows") { - Command::new("cmd").args(["/c", "cls"]).spawn() - } else { - Command::new("clear").spawn() - }; - Ok(()) + fn redo(&self) {} + + fn get_description(&self) -> String { + String::from("Executes a file path") + } + + fn get_name(&self) -> String { + String::from("exec") + } + + fn get_help(&self) -> String { + String::from("this will read the contents of a .zensh file, evaluate it, and run its input") + } + + fn get_params(&self) -> String { + String::from("1: File path") + } } -pub(crate) fn help() -> anyhow::Result<()> { - println!("Commands:"); - for cmd in COMMAND_LIST.commands.read().iter() { - println!("{:#}", cmd); +fn eval(input: String) -> Result>)>, anyhow::Error> { + if input.trim().is_empty() { + return Err(anyhow!("Input was empty")); + } + + let pattern = Regex::new(r"[;|\n]").unwrap(); + let commands: Vec<&str> = pattern.split(&input).collect(); + let mut evaluted = vec![]; + + for command in commands { + let command = command.trim(); + if command.is_empty() { + println!("Empty command, skipping."); + continue; + } + + let tokens = tokenize(command); + if tokens.is_empty() { + println!("Empty command, skipping."); + continue; + } + let cmd_name = &tokens[0]; + let args: Option> = if tokens.len() > 1 { + Some(tokens[1..].iter().map(|s| s.to_string()).collect()) + } else { + None + }; + evaluted.push((cmd_name.to_owned(),args)); } - Ok(()) + Ok(evaluted) } -pub(crate) fn exec(args: Vec) -> anyhow::Result<()> { - *RECURSION_DEPTH.lock() += 1; - if *RECURSION_DEPTH.lock() > MAX_RECURSION_DEPTH { - eprintln!("Maximum recursion depth reached. Aborting."); - *RECURSION_DEPTH.lock() = 0; - return Ok(()); - } - println!("Recursion depth: {}", *RECURSION_DEPTH.lock()); - let file_path_str = &args[0]; - let file_path = std::path::Path::new(file_path_str); - println!("File path: {:#?}", file_path); - - if !file_path.is_file() { - eprintln!( - "Error: File does not exist or is not a valid file: {}", - file_path.display() - ); - return Ok(()); - } - if file_path.extension() != Some(OsStr::new("zensh")) { - eprintln!( - "Error: File is not a zenshell script: {:#?}", - file_path.extension() - ); - //TODO: dont panic on this error - return Ok(()); - } - println!("Executing file: {:#?}", file_path); - let file_content = std::fs::read_to_string(file_path)?; - if file_content.is_empty() { - eprintln!("Error: file has no content. Is this a valid zenshell script?"); - return Ok(()); - } - println!("File contents:\n{file_content}"); - evaluate_command(file_content.trim())?; - Ok(()) + +#[derive(Default)] +pub struct CounterCommand { + counter: RwLock, } + +impl Command for CounterCommand { + fn execute(&self, _args: Option>) -> Result<(), anyhow::Error> { + // Increment the counter + let mut count = self.counter.write(); + *count += 1; + println!("CounterCommand executed. Current count: {}", *count); + Ok(()) + } + + fn undo(&self) { + println!("Undo CounterCommand."); + } + + fn redo(&self) { + println!("Redo CounterCommand."); + } + + fn get_description(&self) -> String { + String::from("counter") + } + + fn get_help(&self) -> String { + String::from("Increments a counter every time it's executed.") + } + + fn get_params(&self) -> String { + String::from("No parameters for CounterCommand.") + } + + fn get_name(&self) -> String { + String::from("count") + } +} \ No newline at end of file diff --git a/engine/src/core/repl/handler.rs b/engine/src/core/repl/handler.rs new file mode 100644 index 0000000..563368d --- /dev/null +++ b/engine/src/core/repl/handler.rs @@ -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 = 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 { + 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 { + let max_hamming_distance: usize = 2; + let max_edit_distance: usize = 2; + let mut best_match: Option = 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>, + pub aliases: HashMap, +} + +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> { + self.commands.iter() + } + pub fn execute_command(&self,command: &str,args: Option>) -> 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>) -> 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) { + 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>) -> 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; +} \ No newline at end of file diff --git a/engine/src/core/repl/exec.rs b/engine/src/core/repl/input.rs similarity index 68% rename from engine/src/core/repl/exec.rs rename to engine/src/core/repl/input.rs index bda5252..7a5a52e 100644 --- a/engine/src/core/repl/exec.rs +++ b/engine/src/core/repl/input.rs @@ -9,15 +9,13 @@ use log::debug; use parking_lot::Mutex; use regex::Regex; use rustyline::{ - completion::Completer, error::ReadlineError, highlight::Highlighter, hint::HistoryHinter, - history::DefaultHistory, Cmd, Completer, ConditionalEventHandler, Editor, Event, EventContext, - EventHandler, Helper, Hinter, KeyEvent, RepeatCount, Validator, + Cmd, Completer, ConditionalEventHandler, Editor, Event, EventContext, EventHandler, Helper, + Hinter, KeyEvent, RepeatCount, Validator, completion::Completer, error::ReadlineError, + highlight::Highlighter, hint::HistoryHinter, history::DefaultHistory, }; -use crate::{ - core::repl::{commands, Callable, COMMAND_LIST}, - utils::logger::LOGGER, -}; +use super::handler::COMMAND_MANAGER; +use crate::core::logger::LOGGER; struct CommandCompleter; impl CommandCompleter { @@ -35,21 +33,21 @@ impl Completer for CommandCompleter { pos: usize, _ctx: &rustyline::Context<'_>, ) -> rustyline::Result<(usize, Vec)> { - let binding = COMMAND_LIST.commands.read(); + let binding = COMMAND_MANAGER.read(); + let binding = binding.get_commands(); let filtered_commands: Vec<_> = binding - .iter() - .filter(|command| command.name.starts_with(line)) + .filter(|(command, _)| command.starts_with(line)) .collect(); let completions: Vec = filtered_commands .iter() - .filter(|command| command.name.starts_with(&line[..pos])) - .map(|command| command.name[pos..].to_string()) + .filter(|(command, _)| command.starts_with(&line[..pos])) + .map(|(command, _)| command[pos..].to_string()) .collect(); + println!("{:#?}", completions); Ok((pos, completions)) } } - #[derive(Completer, Helper, Hinter, Validator)] struct MyHelper { #[rustyline(Hinter)] @@ -106,53 +104,7 @@ impl ConditionalEventHandler for BacktickEventHandler { } } -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, - ); - COMMAND_LIST.add_command( - "exec", - Some("Executes a .nyx file."), - Callable::WithArgs(commands::exec), - Some(1), - ); - - // Example of adding aliases for commands - COMMAND_LIST.add_alias("clear".to_string(), "cls".to_string()); -} - -fn tokenize(command: &str) -> Vec { +pub fn tokenize(command: &str) -> Vec { let mut tokens = Vec::new(); let mut current_token = String::new(); let mut inside_string = false; @@ -180,12 +132,11 @@ fn tokenize(command: &str) -> Vec { pub fn parse_command(input: &str) -> anyhow::Result> { let pattern = Regex::new(r"[;|\n]").unwrap(); - let commands: Vec = pattern.split(input).map(|s| String::from(s)).collect(); + let commands: Vec = pattern.split(input).map(String::from).collect(); Ok(commands) } pub fn evaluate_command(input: &str) -> anyhow::Result<()> { if input.trim().is_empty() { - println!("Empty command, skipping. type 'help' for a list of commands."); return Ok(()); } @@ -205,12 +156,15 @@ pub fn evaluate_command(input: &str) -> anyhow::Result<()> { continue; } 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) }, - )?; + let args: Option> = if tokens.len() > 1 { + Some(tokens[1..].iter().map(|s| s.to_string()).collect()) + } else { + None + }; + match COMMAND_MANAGER.read().execute(cmd_name, args) { + Ok(_) => continue, + Err(e) => return Err(e) + } } Ok(()) } @@ -233,8 +187,6 @@ pub async fn handle_repl() -> anyhow::Result<()> { debug!("No previous history."); } - register_commands(); - loop { let time = Local::now().format("%H:%M:%S.%3f").to_string(); let prompt = format!("[{}/{}] {}", time, "SHELL", ">>\t"); @@ -243,7 +195,10 @@ pub async fn handle_repl() -> anyhow::Result<()> { match sig { Ok(line) => { rl.add_history_entry(line.as_str())?; - evaluate_command(line.as_str())?; + match evaluate_command(line.as_str()) { + Ok(_) => continue, + Err(e) => println!("{e}"), + } } Err(ReadlineError::Interrupted) => { println!("CTRL+C received, exiting..."); diff --git a/engine/src/core/repl/mod.rs b/engine/src/core/repl/mod.rs index 698eb82..a5b0613 100644 --- a/engine/src/core/repl/mod.rs +++ b/engine/src/core/repl/mod.rs @@ -1,245 +1,12 @@ -pub mod commands; -pub mod exec; - -use std::{borrow::Borrow, collections::HashMap, sync::Arc}; - -use anyhow::Context; -use colored::Colorize; -use lazy_static::lazy_static; -use log::{debug, info}; -use parking_lot::RwLock; - -lazy_static! { - pub static ref COMMAND_LIST: Arc = Arc::new(CommandList::new()); -} - -#[derive(Clone, Debug)] -enum Callable { - Simple(fn() -> anyhow::Result<()>), - WithArgs(fn(Vec) -> anyhow::Result<()>), -} - -#[derive(Debug)] -pub struct Command { - pub name: &'static str, - pub description: Option<&'static str>, - function: Callable, - pub arg_count: u8, -} -#[allow(private_interfaces)] -impl Command { - pub fn new( - name: &'static str, - description: Option<&'static str>, - function: Callable, - arg_count: Option, - ) -> Self { - Command { - name, - description, - function, - arg_count: arg_count.unwrap_or(0), - } - } - - pub fn execute(&self, args: Option>) -> anyhow::Result<()> { - match &self.function { - Callable::Simple(f) => { - if let Some(args) = args { - eprintln!( - "Command expected 0 arguments but {} args were given. Ignoring..", - args.len() - ); - } - f()?; - Ok(()) - } - Callable::WithArgs(f) => match args { - Some(args) => f(args), - None => Ok(()), - }, - } - } -} - -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"), - if self.arg_count > 0 { - format!("{} args", self.arg_count) - } else { - "No args".to_string() - } - ) - } -} - -pub struct CommandList { - pub commands: RwLock>, - pub aliases: RwLock>, -} +use commands::{ClearCommand, CounterCommand, ExecFile, ExitCommand, HelpCommand}; -fn hamming_distance(a: &str, b: &str) -> Option { - if a.len() != b.len() { - return None; - } - Some( - a.chars() - .zip(b.chars()) - .filter(|(char_a, char_b)| char_a != char_b) - .count(), - ) -} +use crate::commands; -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, strings: &[String]) -> Option { - let max_hamming_distance: usize = 2; - let max_edit_distance: usize = 2; - let mut best_match: Option = None; - let mut best_distance = usize::MAX; - - for s in strings { - if let Some(hamming_dist) = hamming_distance(target, s) { - if hamming_dist <= max_hamming_distance && hamming_dist < best_distance { - best_distance = hamming_dist; - best_match = Some(s.clone()); - } - } else { - let edit_dist = edit_distance(target, s); - if edit_dist <= max_edit_distance && edit_dist < best_distance { - best_distance = edit_dist; - best_match = Some(s.clone()); - } - } - } - - best_match -} - -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, - ) { - info!("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) { - if self.aliases.read().contains_key(&alias) { - eprintln!("Alias: '{}' already exists", alias); - return; - } - - let mut commands = self.commands.write(); - if let Some(command) = commands.iter_mut().find(|cmd| cmd.name == name) { - debug!("Adding alias: {} for cmd: {}", alias, command.name); - self.aliases - .write() - .insert(alias.to_string(), name.to_string()); - } else { - eprintln!("Command: '{}' was not found", name); - } - } - - fn execute_command(&self, mut name: String, args: Option>) -> anyhow::Result<()> { - let commands = self.commands.borrow(); - if self.aliases.read().contains_key(&name) { - name = self - .aliases - .read() - .get_key_value(&name) - .context("Failed to get alias")? - .1 - .to_string(); +pub mod commands; +pub mod input; +pub mod handler; - 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() - ); - Ok(()) - } - (expected, None) => { - eprintln!( - "Command: '{}' expected {} arguments but received none", - name, expected - ); - Ok(()) - } - (_, _) => command.execute(args), - } - } else { - eprintln!("Command: '{}' was not found", name.red().italic()); - let most_similar = check_similarity( - &name, - &self - .commands - .read() - .iter() - .map(|cmd| cmd.name.to_string()) - .collect::>(), - ); - match most_similar { - Some(similar) => { - eprintln!("Did you mean: '{}'?", similar.green().italic().bold()); - Ok(()) - } - None => { - println!("Type 'help' for a list of commands"); - Ok(()) - } - } - } - } +pub fn setup() { + commands!(HelpCommand,ClearCommand,ExitCommand,ExecFile,CounterCommand); } diff --git a/engine/src/core/splash.rs b/engine/src/core/splash.rs new file mode 100644 index 0000000..7381a46 --- /dev/null +++ b/engine/src/core/splash.rs @@ -0,0 +1,29 @@ +use colored::Colorize; + +pub fn print_splash() { + println!( + "{}", + format!( + r#" + ▓▓▓▓▓▓▓▓▓▓▓ + ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + ▓▓ ▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓ + ▓▓▓▓▓▓▓▓▓ ▓▓ + ▓▓▓▓▓▓▓▓▓ ▓▓ + ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + ▓▓▓▓▓▓▓▓▓▓▓ + + Version: {} + "#, + env!("CARGO_PKG_VERSION").green() + ) + .bright_yellow() + ); +} \ No newline at end of file diff --git a/engine/src/main.rs b/engine/src/main.rs index 767db02..3beed07 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -1,36 +1,19 @@ -#![deny(clippy::unwrap_in_result)] -use anyhow::Result; -use log::LevelFilter; -use plugin_api::plugin_imports::*; -use plugin_api::{get_plugin, PluginManager}; +use core::{repl::{handler::COMMAND_MANAGER, input::handle_repl, setup}, splash}; -pub mod core; -pub mod utils; - -use utils::{logger::LOGGER, splash::print_splash}; - -#[tokio::main] -async fn main() -> Result<()> { - // Load all plugins +use anyhow::Ok; - log::set_logger(&*LOGGER).ok(); - log::set_max_level(LevelFilter::Off); - print_splash(); - let mut plugin_manager = PluginManager::new(); - let plugins = plugin_manager.load_all(); - println!("Plugins loaded: {:?}", plugins); - - // Get the player plugin - let player_lib = get_plugin!(player_lib, plugins); - player_lib.test(); - - LOGGER.write_to_stdout(); +pub mod core; - let shell_thread = tokio::task::spawn(async { core::repl::exec::handle_repl().await }); +#[tokio::main] +async fn main() -> anyhow::Result<()> { + setup(); + splash::print_splash(); + COMMAND_MANAGER.read().execute("help", None)?; + let t = tokio::spawn(handle_repl()); - core::init_renderer()?; - shell_thread.await??; + t.await??; Ok(()) + } diff --git a/engine/src/utils/mathi.rs b/engine/src/utils/mathi.rs deleted file mode 100644 index 8b13789..0000000 --- a/engine/src/utils/mathi.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/engine/src/utils/mod.rs b/engine/src/utils/mod.rs deleted file mode 100644 index df309c5..0000000 --- a/engine/src/utils/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod logger; -pub mod mathi; -pub mod splash; diff --git a/engine/src/utils/splash.rs b/engine/src/utils/splash.rs deleted file mode 100644 index 051d11e..0000000 --- a/engine/src/utils/splash.rs +++ /dev/null @@ -1,29 +0,0 @@ -use colored::Colorize; - -pub fn print_splash() { - println!( - "{}", - format!( - r#" - &&&&&&&&&&& - &&&&&&&&&&&&&&&&& - &&&&&&&&&&&&&&&&&&&&& - && &&&&&&&&& -&&&&&&&&&&&& &&&&&&&&&&& -&&&&&&&&&&&&& &&&&&&&&&&&& -&&&&&&&&&&&&& &&&&&&&&&&&&& -&&&&&&&&&&&& &&&&&&&&&&&&& -&&&&&&&&&&& &&&&&&&&&&&& - &&&&&&&&& && - &&&&&&&&& && - &&&&&&&&&&&&&&&&&&&&& - &&&&&&&&&&&&&&&&& - &&&&&&&&&&& - - Version: {} - "#, - env!("CARGO_PKG_VERSION").color("yellow") - ) - .bright_black() - ); -} diff --git a/engine/test.zensh b/engine/test.zensh new file mode 100644 index 0000000..5149af1 --- /dev/null +++ b/engine/test.zensh @@ -0,0 +1,7 @@ +count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count + +count +help + + +exit 102 \ No newline at end of file diff --git a/plugin_api/Cargo.toml b/plugin_api/Cargo.toml deleted file mode 100644 index f702587..0000000 --- a/plugin_api/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "plugin_api" -version = "0.3.0" -authors = ["Tristan Poland "] -description = "Horizon Plugins API" -license = "MIT" -edition = "2021" - -[build-dependencies] -toml_edit = "0.22.22" -pathdiff = "0.2.3" - -[dependencies] -async-trait = "0.1.83" -tokio = { version = "1.42.0", features = ["rt", "net", "rt-multi-thread"] } -uuid = "1.11.0" -socketioxide = "0.15.0" -horizon-plugin-api = "0.1.11" -# -# -# -# -###### BEGIN AUTO-GENERATED PLUGIN DEPENDENCIES - DO NOT EDIT THIS SECTION ###### -player_lib = { path = "../plugins/player_lib", version = "0.1.0" } -###### END AUTO-GENERATED PLUGIN DEPENDENCIES ###### diff --git a/plugin_api/build.rs b/plugin_api/build.rs deleted file mode 100644 index 801cf3a..0000000 --- a/plugin_api/build.rs +++ /dev/null @@ -1,224 +0,0 @@ -use std::fs::{self, File}; -use std::io::{Read, Write}; -use std::path::Path; - -fn main() { - // Get the path to the plugins directory - let plugins_dir = Path::new("..").join("plugins"); - - println!("cargo:warning=Looking for plugins in: {:?}", plugins_dir); - - // Ensure the plugins directory exists - if !plugins_dir.exists() { - println!( - "cargo:warning=Plugins directory not found at {:?}", - plugins_dir - ); - return; - } - - // Find all valid plugin directories - let plugin_paths = discover_plugins(&plugins_dir); - println!("cargo:warning=Found {} plugins", plugin_paths.len()); - - // Update Cargo.toml with plugin dependencies - if let Err(e) = update_cargo_toml(&plugin_paths) { - println!("cargo:warning=Failed to update Cargo.toml: {}", e); - std::process::exit(1); - } - - // Generate the plugin macro and imports files - if let Err(e) = generate_plugin_files(&plugin_paths) { - println!("cargo:warning=Failed to generate plugin files: {}", e); - std::process::exit(1); - } - - // Tell Cargo to rerun this script if the plugins directory or Cargo.toml - // changes - println!("cargo:rerun-if-changed=../plugins"); - println!("cargo:rerun-if-changed=Cargo.toml"); -} - -fn discover_plugins(plugins_dir: &Path) -> Vec<(String, String, String)> { - let mut valid_plugins = Vec::new(); - - if let Ok(entries) = fs::read_dir(plugins_dir) { - for entry in entries.flatten() { - let path = entry.path(); - - // Check if this is a directory - if !path.is_dir() { - continue; - } - - let plugin_name = path - .file_name() - .and_then(|n| n.to_str()) - .unwrap_or("") - .to_string(); - - // Skip if empty plugin name - if plugin_name.is_empty() { - continue; - } - - // Check for required files/directories - let cargo_toml = path.join("Cargo.toml"); - let src_dir = path.join("src"); - let lib_rs = path.join("src").join("lib.rs"); - - if cargo_toml.exists() && src_dir.exists() && lib_rs.exists() { - // Read the Cargo.toml to get the package name and version - if let Ok(mut file) = File::open(&cargo_toml) { - let mut contents = String::new(); - if file.read_to_string(&mut contents).is_ok() { - // Simple parsing for package name and version - let mut name = None; - let mut version = None; - - for line in contents.lines() { - let line = line.trim(); - if line.starts_with("name") { - name = line - .split('=') - .nth(1) - .map(|s| s.trim().trim_matches('"').to_string()); - } else if line.starts_with("version") { - version = line - .split('=') - .nth(1) - .map(|s| s.trim().trim_matches('"').to_string()); - } - } - - if let (Some(name), Some(version)) = (name, version) { - println!( - "cargo:warning=Found plugin: {} v{} in {}", - name, version, plugin_name - ); - valid_plugins.push((name, version, plugin_name)); - } - } - } - } - } - } - - valid_plugins -} - -const AUTO_GENERATED_START: &str = - "###### BEGIN AUTO-GENERATED PLUGIN DEPENDENCIES - DO NOT EDIT THIS SECTION ######"; -const AUTO_GENERATED_END: &str = "###### END AUTO-GENERATED PLUGIN DEPENDENCIES ######"; - -fn update_cargo_toml(plugin_paths: &[(String, String, String)]) -> std::io::Result<()> { - let cargo_path = "Cargo.toml"; - let mut contents = String::new(); - File::open(cargo_path)?.read_to_string(&mut contents)?; - - // Normalize line endings to \n for consistent processing - contents = contents.replace("\r\n", "\n"); - - // Find the boundaries of the auto-generated section - let start_idx = contents.find(AUTO_GENERATED_START); - let end_idx = contents.find(AUTO_GENERATED_END); - - let base_contents = match (start_idx, end_idx) { - (Some(start), Some(end)) => { - // If an existing section is found, take everything before it - contents[..start].trim_end().to_string() - } - _ => { - // If no section exists, use all current contents - contents.trim_end().to_string() - } - }; - - // Generate the new dependencies section - let mut new_section = String::new(); - new_section.push('\n'); // Add a newline before the section - new_section.push_str(AUTO_GENERATED_START); - new_section.push('\n'); // Add newline after start marker - - // Sort plugins by name for consistent output - let mut sorted_plugins = plugin_paths.to_vec(); - sorted_plugins.sort_by(|a, b| a.0.cmp(&b.0)); - - for (name, version, plugin_dir) in sorted_plugins { - new_section.push_str(&format!( - "{} = {{ path = \"../plugins/{}\", version = \"{}\" }}\n", - name, plugin_dir, version - )); - } - - new_section.push_str(AUTO_GENERATED_END); - - // Combine the base contents with the new section - let mut final_contents = base_contents; - final_contents.push_str(&new_section); - - // Ensure file ends with a single newline - if !final_contents.ends_with('\n') { - final_contents.push('\n'); - } - - // Write the updated Cargo.toml - fs::write(cargo_path, final_contents)?; - - Ok(()) -} - -fn generate_plugin_files(plugin_paths: &[(String, String, String)]) -> std::io::Result<()> { - // Create the output directory if it doesn't exist - let out_dir = Path::new("src"); - fs::create_dir_all(out_dir)?; - - // Then generate the imports file that uses the macro - generate_imports_file(plugin_paths, out_dir)?; - - Ok(()) -} - -fn generate_imports_file( - plugin_paths: &[(String, String, String)], - out_dir: &Path, -) -> std::io::Result<()> { - let mut file = fs::File::create(out_dir.join("plugin_imports.rs"))?; - - // Write the header - writeln!(file, "// This file is automatically generated by build.rs")?; - writeln!(file, "// Do not edit this file manually!\n")?; - writeln!( - file, - "use horizon_plugin_api::{{Pluginstate, LoadedPlugin, Plugin}};" - )?; - writeln!(file, "use std::collections::HashMap;\n")?; - for (i, (name, _, _)) in plugin_paths.iter().enumerate() { - write!(file, "pub use {};\n", name)?; - write!(file, "pub use {}::*;\n", name)?; - write!(file, "pub use {}::Plugin as {}_plugin;\n", name, name)?; - } - writeln!(file, "\n"); - - // Use the macro with discovered plugins - writeln!(file, "// Invoke the macro with all discovered plugins")?; - writeln!( - file, - "pub fn load_plugins() -> HashMap {{" - )?; - write!(file, " let plugins = crate::load_plugins!(")?; - - // Add each plugin to the macro invocation - for (i, (name, _, _)) in plugin_paths.iter().enumerate() { - if i > 0 { - write!(file, ",")?; - } - write!(file, "\n {}", name)?; - } - - writeln!(file, "\n );")?; - writeln!(file, " plugins")?; - writeln!(file, "}}")?; - - Ok(()) -} diff --git a/plugin_api/src/lib.rs b/plugin_api/src/lib.rs deleted file mode 100644 index 426ed2e..0000000 --- a/plugin_api/src/lib.rs +++ /dev/null @@ -1,77 +0,0 @@ -use std::collections::HashMap; - -pub use horizon_plugin_api::{get_plugin, LoadedPlugin, Plugin, Pluginstate, Version}; - -pub mod plugin_imports; - -// Define the current plugin version -const PLUGIN_API_VERSION: Version = Version { - major: 0, - minor: 1, - hotfix: 0, -}; - -#[derive(Clone)] -pub struct PluginManager { - plugins: HashMap, -} - -#[macro_export] -macro_rules! load_plugins { - ($($plugin:ident),* $(,)?) => { - { - let mut plugins = HashMap::new(); - $( - plugins.insert( - stringify!($plugin).to_string(), - (Pluginstate::ACTIVE, <$plugin::Plugin as $plugin::PluginConstruct>::new(plugins.clone())), - ); - )* - - plugins - } - }; -} - -impl PluginManager { - /// Allow instantiation of the ``PluginManager`` struct - pub fn new() -> PluginManager { - let new_manager = PluginManager { - plugins: HashMap::new(), - }; - - new_manager - } - - pub fn load_plugin(mut self, name: String, plugin: Plugin) { - self.plugins.insert(name, (Pluginstate::ACTIVE, plugin)); - } - - pub fn unload_plugin(mut self, name: String) { - self.plugins.remove(&name); - } - - pub fn get_plugins(self) -> HashMap { - self.plugins - } - - pub fn load_all(&mut self) -> HashMap { - self.plugins = plugin_imports::load_plugins(); - - //let my_test_plugin = get_plugin!(test_plugin, plugins); - //let result = my_test_plugin.thing(); - - let mut loaded_plugins = HashMap::new(); - for (name, (state, plugin)) in &self.plugins { - if *state == Pluginstate::ACTIVE { - loaded_plugins.insert( - name.clone(), - LoadedPlugin { - instance: plugin.clone(), - }, - ); - } - } - loaded_plugins - } -} diff --git a/plugin_api/src/plugin_imports.rs b/plugin_api/src/plugin_imports.rs deleted file mode 100644 index 7875142..0000000 --- a/plugin_api/src/plugin_imports.rs +++ /dev/null @@ -1,18 +0,0 @@ -// This file is automatically generated by build.rs -// Do not edit this file manually! - -use horizon_plugin_api::{Pluginstate, LoadedPlugin, Plugin}; -use std::collections::HashMap; - -pub use player_lib; -pub use player_lib::*; -pub use player_lib::Plugin as player_lib_plugin; - - -// Invoke the macro with all discovered plugins -pub fn load_plugins() -> HashMap { - let plugins = crate::load_plugins!( - player_lib - ); - plugins -} diff --git a/plugins/player_lib/Cargo.toml b/plugins/player_lib/Cargo.toml deleted file mode 100644 index 567b135..0000000 --- a/plugins/player_lib/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "player_lib" -version = "0.1.0" -edition = "2021" - -[dependencies] -PebbleVault = "0.6.1" -horizon-plugin-api = "0.1.13" -horizon_data_types = "0.4.0" -socketioxide = "0.15.1" -parking_lot = "0.12.3" diff --git a/plugins/player_lib/src/lib.rs b/plugins/player_lib/src/lib.rs deleted file mode 100644 index 9fbadb7..0000000 --- a/plugins/player_lib/src/lib.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::collections::HashMap; - -pub use horizon_plugin_api::{LoadedPlugin, Plugin, Pluginstate}; - -// Define the trait properly -pub trait PluginAPI { - fn test(&self); -} - -pub trait PluginConstruct { - fn get_structs(&self) -> Vec<&str>; - // If you want default implementations, mark them with 'default' - fn new(plugins: HashMap) -> Plugin; -} - -// Implement constructor separately -impl PluginConstruct for Plugin { - fn new(plugins: HashMap) -> Plugin { - Plugin {} - } - - fn get_structs(&self) -> Vec<&str> { - vec!["MyPlayer"] - } -} - -// Implement the trait for Plugin -impl PluginAPI for Plugin { - fn test(&self) { - println!("test"); - } -} - -//----------------------------------------------------------------------------- -// Plugin Implementation -//----------------------------------------------------------------------------- diff --git a/subcrates/zen_core/Cargo.toml b/subcrates/zen_core/Cargo.toml new file mode 100644 index 0000000..0604d37 --- /dev/null +++ b/subcrates/zen_core/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "zen_core" +version = "0.1.0" +edition = "2024" + +[dependencies] +anyhow = "1.0.94" +thiserror = "2.0.8" diff --git a/subcrates/zen_core/src/lib.rs b/subcrates/zen_core/src/lib.rs new file mode 100644 index 0000000..9b8cf04 --- /dev/null +++ b/subcrates/zen_core/src/lib.rs @@ -0,0 +1,9 @@ +use thiserror::Error; + + +#[derive(Debug,Error)] +enum ZError { + #[error(transparent)] + Unknown(#[from] anyhow::Error) + +} \ No newline at end of file diff --git a/subcrates/zephyr/Cargo.toml b/subcrates/zephyr/Cargo.toml deleted file mode 100644 index 739c027..0000000 --- a/subcrates/zephyr/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "zephyr" -version = "0.1.0" -edition = "2021" - -[dependencies] diff --git a/subcrates/zephyr/README.md b/subcrates/zephyr/README.md deleted file mode 100644 index ee6059c..0000000 --- a/subcrates/zephyr/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Zephyr ECS
- - -🚧 **Work In Progress** 🚧 - -This README is currently under construction. Please check back later for more detailed information about the project. - -
\ No newline at end of file diff --git a/subcrates/zephyr/src/lib.rs b/subcrates/zephyr/src/lib.rs deleted file mode 100644 index 8b13789..0000000 --- a/subcrates/zephyr/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -