diff --git a/Cargo.lock b/Cargo.lock index c6caa25..94fe878 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1769,6 +1769,7 @@ dependencies = [ name = "mize" version = "0.0.1" dependencies = [ + "async-trait", "axum", "axum-extra", "axum-macros", @@ -1785,6 +1786,7 @@ dependencies = [ "http_req", "interner", "libloading", + "mize_macros", "nix", "serde", "serde-wasm-bindgen", @@ -1806,6 +1808,14 @@ dependencies = [ "wee_alloc", ] +[[package]] +name = "mize_macros" +version = "0.1.0" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "multipart" version = "0.18.0" diff --git a/Cargo.toml b/Cargo.toml index edce4b8..8a6bd81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,4 @@ members = [ "parts/js", "apps/ppc", "parts/mize" -, "parts/cli"] +, "parts/cli", "parts/macro_mize"] diff --git a/apps/ppc/src/main.rs b/apps/ppc/src/main.rs index ed5b289..3b3ec9c 100644 --- a/apps/ppc/src/main.rs +++ b/apps/ppc/src/main.rs @@ -1,20 +1,31 @@ use std::collections::HashMap; +use clap::crate_version; +use cli_part::Cli; use config::Config; use deno_part::JsPart; -use mize::{Mize, MizeError, MizePart, MizeResult, add_parts, mize_err}; -use clap::crate_version; +use mize::{ + DynMizePartGuard, Mize, MizeError, MizePart, MizePartGuard, MizeResult, add_parts, mize_err, +}; fn main() { let mut mize = Mize::new(); - add_parts!(&mut mize, Cli::new(&mut mize, "ppc")); - let cli = Cli::get(&mut mize); - cli.with_cmd(|cmd| cmd.version(crate_version!()).author("ppc").about("the ppc desktop program")); - cli.subcommand("test", |sub_matches| { + add_parts!(&mut mize, Cli); + + let cli = mize.get_part_native::("cli"); + + cli.with_cmd(|cmd| { + cmd.version(crate_version!()) + .name("ppc") + .author("ppc") + .about("the ppc desktop program") + }); + + cli.subcommand(Command::new("test"), |sub_matches| { println!("test ppc..."); Ok(()) }); - + mize.run(); } diff --git a/parts/Cargo.toml b/parts/Cargo.toml new file mode 100644 index 0000000..892826f --- /dev/null +++ b/parts/Cargo.toml @@ -0,0 +1,3 @@ + +[[lib]] +entry = standalone_parts.rs diff --git a/parts/cli/src/lib.rs b/parts/cli/src/lib.rs index ae318fd..1da63b9 100644 --- a/parts/cli/src/lib.rs +++ b/parts/cli/src/lib.rs @@ -4,33 +4,17 @@ use std::collections::HashMap; use mize::{Mize, MizePart, MizeResult, instance::MizePartGuard}; +#[mize_part("cli")] struct CliPart { mize: Mize, cmd: Option, actions: HashMap MizeResult<()>>, } -pub fn new(mize: Mize) -> CliPart { - CliPart { - mize, - cmd: None, - actions: HashMap::new(), - } -} - impl MizePart for CliPart { - fn name(&self) -> &'static str { - "cli" - } - fn get_mize(&mut self) -> &mut Mize { - &mut self.mize - } fn init(&mut self, _mize: &mut Mize) -> MizeResult<()> { Ok(()) } - fn opts(&self, mize: &mut Mize) { - mize.new_opt("cli.name"); - } fn run(&mut self, _mize: &mut Mize) -> MizeResult<()> { let matches = self.cmd.take().unwrap().get_matches(); let sub_cmd = matches.subcommand_name().unwrap(); @@ -38,14 +22,8 @@ impl MizePart for CliPart { action(matches.subcommand_matches(sub_cmd).unwrap()); Ok(()) } - fn as_any(&self) -> &dyn std::any::Any { - self - } - fn as_any_mut(&self) -> &dyn std::any::Any { - self - } - fn into_any(self: Box) -> Box { - self + fn opts(&self, mize: &mut Mize) { + mize.new_opt("cli.name"); } } @@ -60,13 +38,4 @@ impl CliPart { self.cmd = Some(cmd); self.cmd.as_mut().unwrap() } - pub fn get(mize: &mut Mize) -> MizePartGuard { - let mut dyn_guard = mize.get_part("cli").unwrap(); - let part = dyn_guard.part.take().unwrap(); - let cli_part = part.into_any().downcast::().unwrap(); - MizePartGuard { - mize: dyn_guard.mize.clone(), - part: Some(*cli_part), - } - } } diff --git a/parts/js/src/lib.rs b/parts/js/src/lib.rs index fb751ac..0eae8e6 100644 --- a/parts/js/src/lib.rs +++ b/parts/js/src/lib.rs @@ -1,32 +1,143 @@ +use deno_core::FsModuleLoader; use deno_core::error::AnyError; -use mize::{Mize, MizePart, MizeResult}; +use deno_core::{JsRuntime, ModuleSpecifier, PollEventLoopOptions, RuntimeOptions, extension, op2}; +use mize::async_trait; +use mize::instance::MizePartCreate; +use mize::{Mize, MizePart, MizeResult, mize_part}; +use std::path::Path; use std::rc::Rc; +#[mize_part("js")] pub struct JsPart { - mize: Mize, -} - -impl JsPart { - pub fn new(mize: Mize) -> Self { - JsPart { mize } - } + mize: Mize, + js_runtime: JsRuntime, } impl MizePart for JsPart { - fn name(&self) -> &'static str { - "js" - } - - fn get_mize(&mut self) -> &mut Mize { - &mut self.mize - } - fn init(&mut self, mize: &mut Mize) -> MizeResult<()> { - println!("deno part init"); + println!("js part init"); + Ok(()) + } +} +impl MizePartCreate for JsPart { + fn create(mize: &mut Mize) -> Box { + let mut js_runtime = JsRuntime::new(RuntimeOptions { + module_loader: Some(Rc::new(FsModuleLoader)), + extensions: vec![mize_ext::init_ops()], + ..Default::default() + }); + let bootstrap_code = r#" + globalThis.mize = { + get_part: (name) => Deno.core.ops.op_mize_get_part(name), + get_config: (key) => Deno.core.ops.op_mize_get_config(key), + version: "1.0.0", + // Add more methods as needed + }; + "#; + + js_runtime.execute_script("[bootstrap]", bootstrap_code)?; + JsPart { mize, js_runtime } + } +} + +impl JsPart { + pub fn part_from_js_file( + &mut self, + name: &'static str, + path: &str, + ) -> Box { + Box::new(PartFromJsFileAdapter { + mize: self.mize.clone(), + name, + js_file: path, + }) + } +} + +#[mize_part] +struct PartFromJsFileAdapter { + mize: Mize, + name: &'static str, + js_file: &'static str, +} + +#[async_trait] +impl MizePart for PartFromJsFileAdapter { + fn deps(&self) -> &'static [&'static str] { + &["js"] + } + fn name(&self) -> &'static str { + self.name + } + async fn async_init(&mut self, mize: &mut Mize) -> MizeResult<()> { + let js: JsPart = mize.get_part_native("js"); + let path = self.js_file; + let js_code = format!( + r#" + import {{ opts, deps, create }} from "{path}"; + + const isPromise = (value) => { + return !!value && (typeof value === 'object' || typeof value === 'function') && typeof value.then === 'function'; + }; + + // Call the async function + let create_result = create(mize); + if (isPromise(create_result)) { + create_result = await create_result; + } + const result = { + opts: opts(mize), + deps: deps(mize), + create: create_result, + }; + + result; + "# + ); + + // Execute the script + let result = js.runtime.execute_script("[main]", js_code)?; + + // Resolve the promise and run event loop + let resolved_value = js.runtime.resolve_value(result).await?; + + // Run the event loop to completion + js.runtime + .run_event_loop(PollEventLoopOptions::default()) + .await?; + + println!("Execution of part {} completed successfully", self.name()); + Ok(()) + } + async fn async_run(&mut self, mize: &mut Mize) -> MizeResult<()> { + let js: JsPart = mize.get_part_native("js"); + let path = self.js_file; + let js_code = format!( + r#" + import {{ run }} from "{path}"; + + const result = run() + + result; + "# + ); + // Execute the script + let result = js.runtime.execute_script("[main]", js_code)?; + + // Resolve the promise and run event loop + let resolved_value = js.runtime.resolve_value(result).await?; + + // Run the event loop to completion + js.runtime + .run_event_loop(PollEventLoopOptions::default()) + .await?; + + println!("Running of part {} completed successfully", self.name()); Ok(()) } } +/* async fn run_js_file(file_path: &str) -> Result<(), AnyError> { let main_module = deno_core::resolve_path(file_path, &std::env::current_dir()?)?; let mut js_runtime = deno_core::JsRuntime::new(deno_core::RuntimeOptions { @@ -39,3 +150,19 @@ async fn run_js_file(file_path: &str) -> Result<(), AnyError> { js_runtime.run_event_loop(Default::default()).await?; result.await } + */ + +// Define your custom ops for the "mize" object inside deno +#[op2(fast)] +fn op_mize_get_part(#[string] name: &str) { + println!("js wants the part {}", name); +} + +#[op2] +#[string] +fn op_mize_get_config(#[string] key: &str) -> String { + format!("config_value_for_{}", key) +} + +// Create an extension with your custom ops +extension!(mize_ext, ops = [op_mize_get_part, op_mize_get_config]); diff --git a/parts/macro_mize/Cargo.toml b/parts/macro_mize/Cargo.toml new file mode 100644 index 0000000..cc251fd --- /dev/null +++ b/parts/macro_mize/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "mize_macros" +version = "0.1.0" +edition = "2024" + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0.44" +syn = "2.0.114" diff --git a/parts/macro_mize/src/lib.rs b/parts/macro_mize/src/lib.rs new file mode 100644 index 0000000..27e30db --- /dev/null +++ b/parts/macro_mize/src/lib.rs @@ -0,0 +1,68 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{Data, DeriveInput, Fields, parse_macro_input}; + +#[proc_macro_attribute] +pub fn mize_part(attr: TokenStream, item: TokenStream) -> TokenStream { + let input = parse_macro_input!(item as DeriveInput); + let struct_name = &input.ident; + + // Parse name from attribute + let part_name = if !attr.is_empty() { + let lit: syn::LitStr = parse_macro_input!(attr); + lit.value() + } else { + struct_name.to_string().to_lowercase() + }; + + // Verify mize field exists + let has_mize = match &input.data { + Data::Struct(s) => match &s.fields { + Fields::Named(f) => f + .named + .iter() + .any(|field| field.ident.as_ref().map(|i| i == "mize").unwrap_or(false)), + _ => false, + }, + _ => false, + }; + + if !has_mize { + return syn::Error::new_spanned(struct_name, "Struct must have a 'mize: Mize' field") + .to_compile_error() + .into(); + } + + let expanded = quote! { + #input + + // impl the default new impl + impl mize::MizePartGenerated for #struct_name { + fn name_generated(&self) -> &'static str { + #part_name + } + fn get_mize_generated(&mut self) -> &mut Mize { + &mut self.mize + } + fn as_any_generated(&self) -> &dyn Any { + &self + } + fn as_any_mut_generated(&mut self) -> &dyn Any { + &mut self + } + fn into_any_generated(self: Box) -> Box { + self + } + } + impl mize::MizePartCreateGenerated for #struct_name { + fn create_generated(mize: Mize) -> Self { + Self { + mize, + ... Default::default(), + } + } + } + }; + + TokenStream::from(expanded) +} diff --git a/parts/mize/Cargo.toml b/parts/mize/Cargo.toml index 8b154c1..118159d 100644 --- a/parts/mize/Cargo.toml +++ b/parts/mize/Cargo.toml @@ -39,6 +39,7 @@ toml = { version = "0.8.13", features = ["parse"], optional = false } dyn-clone = "1.0.17" sha2 = "0.10.8" flume = "0.11.1" +mize_macros = { path = "../macro_mize" } # buildin modules #mize_module_blob = { path = "./modules/modules/Blob" } @@ -74,6 +75,7 @@ http_req = { git = "https://github.com/c2vi/http_req", optional = true, default- axum = { version = "0.6.1", features = ["ws", "headers"], optional = true } axum-extra = { version = "0.4.2", features = ["spa"], optional = true } axum-macros = { version = "0.3.0", optional = true } +async-trait = "0.1.89" #tokio-core = "0.1.18" #tokio-io = "0.1.13" @@ -94,4 +96,3 @@ wasm-bindgen-test = { version = "0.3.34" } [profile.release] # Tell `rustc` to optimize for small code size. opt-level = "s" - diff --git a/parts/mize/flake.nix b/parts/mize/flake.nix index dfd4609..ba54707 100644 --- a/parts/mize/flake.nix +++ b/parts/mize/flake.nix @@ -35,7 +35,7 @@ let ]; wasmCrane = (crane.mkLib pkgs).overrideToolchain wasmToolchain; - osToolchain = fenix.packages.${system}.stable.toolchain; + osToolchain = fenix.packages.${system}.nightly.toolchain; osCrane = (crane.mkLib pkgs).overrideToolchain osToolchain; @@ -222,4 +222,3 @@ in { rustPkgs = import nixpkgs { overlays = [rust-overlay.overlays.default]; system = "x86_64-linux"; }; }; } - diff --git a/parts/mize/lib.nix b/parts/mize/lib.nix index fc731bc..1189564 100644 --- a/parts/mize/lib.nix +++ b/parts/mize/lib.nix @@ -88,7 +88,7 @@ rec { (crane.mkLib pkgs).overrideToolchain (fenix.packages.${buildSystem}.combine [ #fenix.packages.${buildSystem}.minimal.rustc #fenix.packages.${buildSystem}.minimal.cargo - fenix.packages.${buildSystem}.stable.toolchain + fenix.packages.${buildSystem}.nightly.toolchain fenix.packages.${buildSystem}.targets."${hostSystem.cpu.name}-pc-windows-gnu".stable.toolchain ]) @@ -271,8 +271,8 @@ rec { nativeBuildInputs = attrs.nativeBuildInputs or [] ++ [ pkgs.pkg-config (fenix.packages."x86_64-linux".combine [ - fenix.packages."x86_64-linux".stable.toolchain - fenix.packages."x86_64-linux".targets.wasm32-unknown-unknown.stable.toolchain + fenix.packages."x86_64-linux".latest.toolchain + fenix.packages."x86_64-linux".targets.wasm32-unknown-unknown.latest.toolchain ]) # the shell script, to mk the dist folder, for a standard rust module @@ -388,5 +388,3 @@ rec { }; } - - diff --git a/parts/mize/src/core/instance/mod.rs b/parts/mize/src/core/instance/mod.rs index 60d8988..2a7743f 100644 --- a/parts/mize/src/core/instance/mod.rs +++ b/parts/mize/src/core/instance/mod.rs @@ -1,3 +1,4 @@ +use async_trait::async_trait; use colored::Colorize; use core::fmt; use flume::{bounded, unbounded, Receiver, Sender}; @@ -44,37 +45,6 @@ pub mod store; pub mod subscription; pub mod updater; -// console_log macro -// that can be copied into other files for debugging purposes -#[cfg(feature = "wasm-target")] -use wasm_bindgen::prelude::*; - -#[cfg(feature = "wasm-target")] -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(js_namespace = console)] - fn log(s: &str); -} - -#[cfg(feature = "wasm-target")] -macro_rules! console_log { - // Note that this is using the `log` function imported above during - // `bare_bones` - ($($t:tt)*) => (unsafe { log(&format_args!($($t)*).to_string())}) -} - -#[macro_export] -macro_rules! add_parts { - ($mize:expr, $($part:expr),+) => { - $( - $mize.add_part( - Box::new($part) - ); - )+ - mize.init_parts(); - }; -} - #[cfg(test)] mod tests; @@ -129,7 +99,7 @@ pub struct DynMizePartGuard { pub part: Option>, } -pub struct MizePartGuard { +pub struct MizePartGuard { pub mize: Mize, pub part: Option, } @@ -175,9 +145,40 @@ impl Drop for DynMizePartGuard { } } -pub trait MizePart: Any + Send + Sync { - fn name(&self) -> &'static str; - fn get_mize(&mut self) -> &mut Mize; +pub trait MizePartCreateGenerated: Sized { + fn create_generated(mize: Mize) -> Self; +} +pub trait MizePartGenerated { + fn name_generated(&self) -> &'static str; + fn get_mize_generated(&mut self) -> &mut Mize; + fn as_any_generated(&self) -> &dyn Any; + fn as_any_mut_generated(&mut self) -> &mut dyn Any; + fn into_any_generated(self: Box) -> Box; +} + +pub trait MizePartCreate: MizePartCreateGenerated + MizePart + 'static { + fn create(mize: &mut Mize) -> Box { + Box::new(Self::create_generated(mize.clone())) + } +} + +#[async_trait] +pub trait MizePart: MizePartGenerated { + fn name(&self) -> &'static str { + self.name_generated() + } + fn get_mize(&mut self) -> &mut Mize { + self.get_mize_generated() + } + fn as_any(&self) -> &dyn Any { + self.as_any_generated() + } + fn as_any_mut(&mut self) -> &mut dyn Any { + self.as_any_mut_generated() + } + fn into_any(self: Box) -> Box { + self.into_any_generated() + } fn init(&mut self, mize: &mut Mize) -> MizeResult<()> { Ok(()) } @@ -187,10 +188,15 @@ pub trait MizePart: Any + Send + Sync { fn deps(&self) -> &'static [&'static str] { &[] } - fn opts(&self, mize: &mut Mize) {} - fn as_any(&self) -> &dyn Any; - fn as_any_mut(&self) -> &dyn Any; - fn into_any(self: Box) -> Box; + fn opts(&self, mize: &mut Mize) { + () + } + async fn async_run(&mut self, mize: &mut Mize) -> MizeResult<()> { + Ok(()) + } + async fn async_init(&mut self, mize: &mut Mize) -> MizeResult<()> { + Ok(()) + } } impl Mize { @@ -300,7 +306,7 @@ impl Mize { None => Err(mize_err!("Part not found or currently taken")), } } - pub fn add_part(&mut self, part: Box) -> MizeResult<()> { + pub fn add_part(&mut self, part: Box) -> MizeResult<()> { self.part_names.lock().unwrap().push(part.name()); self.parts.lock().unwrap().insert(part.name(), Some(part)); Ok(()) @@ -828,6 +834,19 @@ impl Mize { } Ok(()) } + + pub fn get_part_native( + &mut self, + name: &str, + ) -> MizeResult> { + let mut dyn_guard = self.get_part(name)?; + let part = dyn_guard.part.take().unwrap(); + let concrete_part = part.into_any().downcast::().unwrap(); + Ok(MizePartGuard { + mize: self.clone(), + part: Some(*concrete_part), + }) + } } impl fmt::Debug for Mize { diff --git a/parts/mize/src/core/macros.rs b/parts/mize/src/core/macros.rs new file mode 100644 index 0000000..f578409 --- /dev/null +++ b/parts/mize/src/core/macros.rs @@ -0,0 +1,31 @@ +// console_log macro +// that can be copied into other files for debugging purposes +#[cfg(feature = "wasm-target")] +use wasm_bindgen::prelude::*; + +#[cfg(feature = "wasm-target")] +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(js_namespace = console)] + fn log(s: &str); +} + +#[cfg(feature = "wasm-target")] +macro_rules! console_log { + // Note that this is using the `log` function imported above during + // `bare_bones` + ($($t:tt)*) => (unsafe { log(&format_args!($($t)*).to_string())}) +} + +#[macro_export] +macro_rules! add_parts { + ($mize:expr, $($part:expr),+) => { + $( + let part = $part.create($mize.clone()); + $mize.add_part( + part + ); + )+ + mize.init_parts(); + }; +} diff --git a/parts/mize/src/lib.rs b/parts/mize/src/lib.rs index 6aadcee..5fecc39 100644 --- a/parts/mize/src/lib.rs +++ b/parts/mize/src/lib.rs @@ -36,17 +36,23 @@ mod core { pub mod id; pub mod instance; pub mod item; + pub mod macros; pub mod memstore; pub mod proto; pub mod types; } +pub use async_trait::async_trait; pub use core::error::MizeError; pub use core::error::MizeResult; pub use core::instance::module::Module; pub use core::instance::Mize; -pub use core::instance::MizePart; +pub use core::instance::{ + DynMizePartGuard, MizePart, MizePartCreate, MizePartCreateGenerated, MizePartGenerated, + MizePartGuard, +}; pub use core::*; +pub use mize_macros::*; use std::path::PathBuf; // platform specific stuff diff --git a/parts/my_part.rs b/parts/my_part.rs new file mode 100644 index 0000000..e69de29 diff --git a/parts/standalone_parts.rs b/parts/standalone_parts.rs new file mode 100644 index 0000000..15df6e2 --- /dev/null +++ b/parts/standalone_parts.rs @@ -0,0 +1,2 @@ + +mod my_part;