diff --git a/Cargo.lock b/Cargo.lock index 7b11dcd..da7eb12 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -850,6 +850,14 @@ dependencies = [ "mize", ] +[[package]] +name = "mize-part-mos" +version = "0.1.0" +dependencies = [ + "log", + "mize", +] + [[package]] name = "mize_macros" version = "0.1.0" diff --git a/core/src/config.rs b/core/src/config.rs index 681dc36..1ce835a 100644 --- a/core/src/config.rs +++ b/core/src/config.rs @@ -56,7 +56,7 @@ pub fn opt(name: &str) -> crate::config::ConfigOptId { let mut opts = mize.config_opts.lock().unwrap(); let id = cur_part.join(name).unwrap(); let opt = ConfigOpt { - name: name.to_owned(), + name: id.dot(), val: None, fun: None, deps: Vec::new(), @@ -65,13 +65,49 @@ pub fn opt(name: &str) -> crate::config::ConfigOptId { ConfigOptId { id } } -pub fn method MizeResult>(_name: &str, _fun: F) { +pub fn method MizeResult>(name: &str, fun: F) -> ConfigOptId { + let mize = get_global_mize().unwrap(); + let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap(); + let mut opts = mize.config_opts.lock().unwrap(); + let id = cur_part.join(name).unwrap(); + let opt = ConfigOpt { + name: id.dot(), + val: Some(ItemData::from_native(fun)?), + fun: None, + deps: Vec::new(), + }; + opts.insert(id.clone(), opt); + ConfigOptId { id } } -pub fn effect MizeResult>(_name: &str, _fun: F) { +pub fn effect MizeResult>(_name: &str, _fun: F) -> ConfigOptId { + let mize = get_global_mize().unwrap(); + let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap(); + let mut opts = mize.config_opts.lock().unwrap(); + let id = cur_part.join(name).unwrap(); + let opt = ConfigOpt { + name: id.dot(), + val: Some(ItemData::from_native(fun)?), + fun: None, + deps: Vec::new(), + }; + opts.insert(id.clone(), opt); + ConfigOptId { id } } -pub fn thread MizeResult>(_name: &str, _fun: F) { +pub fn thread MizeResult>(_name: &str, _fun: F) -> ConfigOptId { + let mize = get_global_mize().unwrap(); + let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap(); + let mut opts = mize.config_opts.lock().unwrap(); + let id = cur_part.join(name).unwrap(); + let opt = ConfigOpt { + name: id.dot(), + val: Some(ItemData::from_native(fun)?), + fun: None, + deps: Vec::new(), + }; + opts.insert(id.clone(), opt); + ConfigOptId { id } } type CFun = unsafe extern "C" fn (*mut ItemData) -> *mut ItemData; diff --git a/core/src/id.rs b/core/src/id.rs index 396360b..5dc18b4 100644 --- a/core/src/id.rs +++ b/core/src/id.rs @@ -32,6 +32,14 @@ pub trait IntoMizeId { } impl MizeId { + pub fn dot(&self) -> String { + let path = self.path(); + let mut new_path = Vec::new(); + for el in path.iter() { + new_path.push(el.to_owned()); + } + new_path.join(".") + } pub fn join(&mut self, to_add: &str) -> MizeResult { let path = self.path(); let mut new_path = Vec::new(); diff --git a/core/src/instance/mod.rs b/core/src/instance/mod.rs index 14338d7..f93f440 100644 --- a/core/src/instance/mod.rs +++ b/core/src/instance/mod.rs @@ -235,7 +235,7 @@ impl Mize { } pub fn has_part(&mut self, name: &str) -> MizeResult { let id = self.id_new_dots(name)?; - Ok(self.parts.lock().unwrap().contains_key(&id)) + Ok(self.config_opts.lock().unwrap().contains_key(&id)) } pub fn add_listener( &mut self, @@ -249,9 +249,6 @@ impl Mize { Ok(()) } - fn part_names(&mut self) -> Vec<&'static str> { - self.part_names.lock().unwrap().clone() - } pub fn init(&mut self) -> MizeResult<()> { @@ -834,10 +831,11 @@ impl Mize { } pub fn dyn_call(&mut self, part_id: MizeId, method: &str, args: ItemData) -> MizeResult { - let mut parts = self.parts.lock()?; - let _part = parts.get_mut(&part_id).ok_or(mize_err!("part {part_id} is not loaded"))?; - println!("would be dyn_call ing from part, {:?}, {method}, {:?}", part_id, args); - Ok(ItemData::new()) + let mut opts = self.config_opts.lock()?; + let part = opts.get_mut(&part_id).ok_or(mize_err!("part {} is not loaded", part_id.dot()))?; + let fun = part.val.ok_or(mize_err!("fun has no val"))?.native:: MizeResult>()?; + println!("dyn_call ing from part, {:?}, {method}, {:?}", part_id, args); + fun(args) } pub fn get_part_gen( diff --git a/core/src/item.rs b/core/src/item.rs index 4851ab0..45b7b49 100644 --- a/core/src/item.rs +++ b/core/src/item.rs @@ -356,8 +356,7 @@ impl ItemData { /// The native value must have been stored via from_native(). /// Returns a guard that holds the lock on the native_values registry. pub fn native(&self) -> MizeResult> { - let mize_arc = crate::GLOBAL_MIZE.get().ok_or(mize_err!("No global Mize"))?; - let mize = mize_arc.lock().map_err(|_| mize_err!("Failed to lock global Mize"))?; + let mize = get_global_mize()?; let native_values = mize.native_values.lock().map_err(|_| mize_err!("Failed to lock native_values"))?; let id_data = self.get_path("_native") @@ -389,8 +388,7 @@ impl ItemData { /// The native value must have been stored via from_native(). /// Returns a guard that holds the lock on the native_values registry. pub fn native_mut(&self) -> MizeResult> { - let mize_arc = crate::GLOBAL_MIZE.get().ok_or(mize_err!("No global Mize"))?; - let mize = mize_arc.lock().map_err(|_| mize_err!("Failed to lock global Mize"))?; + let mize = get_global_mize()?; let mut native_values = mize.native_values.lock().map_err(|_| mize_err!("Failed to lock native_values"))?; let id_data = self.get_path("_native") @@ -418,11 +416,18 @@ impl ItemData { }) } + /// Get a clone of the native value stored in this ItemData. + /// The native value must have been stored via from_native(). + /// This is useful when you need to pass the value by ownership (e.g., as a function argument). + pub fn native_clone(&self) -> MizeResult { + let guard = self.native::()?; + Ok(guard.clone()) + } + /// Take the native value from this ItemData, removing it from the registry. /// The native value must have been stored via from_native(). pub fn native_take(&self) -> MizeResult { - let mize_arc = crate::GLOBAL_MIZE.get().ok_or(mize_err!("No global Mize"))?; - let mize = mize_arc.lock().map_err(|_| mize_err!("Failed to lock global Mize"))?; + let mize = get_global_mize()?; let mut native_values = mize.native_values.lock().map_err(|_| mize_err!("Failed to lock native_values"))?; let id_data = self.get_path("_native") diff --git a/core/src/lib.rs b/core/src/lib.rs index 2d7c2b0..2d7f387 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -21,6 +21,57 @@ pub fn write_to_mize_stdout(args: std::fmt::Arguments) -> MizeResult<()> { Ok(()) } +/// Mize Shell (msh) macro - wraps command execution with nixpkgs executable resolution. +/// +/// If the executable is given as a bare name (identifier), it is resolved via +/// [`msh_get_nix_bin`] to a nixpkgs store path. If it is given as a string literal +/// containing `/`, it is treated as a path and used as-is. +/// +/// # Examples +/// ```ignore +/// msh!(git status)?; // resolves `git` from nixpkgs +/// msh!(curl https://example.com)?; +/// msh!("/usr/bin/git" status)?; // uses path directly +/// ``` +#[macro_export] +macro_rules! msh { + // First token is a string literal — check if it's a path at runtime + ($cmd:literal $($rest:tt)*) => {{ + let cmd_str: &str = $cmd; + let resolved: String = if cmd_str.contains('/') { + cmd_str.to_string() + } else { + $crate::msh_get_nix_bin(cmd_str) + }; + let mut cmd = ::std::process::Command::new(resolved); + $(cmd.arg(::std::stringify!($rest));)* + let status = cmd.status()?; + if status.success() { + ::std::result::Result::Ok(()) + } else { + ::std::result::Result::Err(::std::io::Error::new( + ::std::io::ErrorKind::Other, + ::std::format!("msh: command exited with {}", status), + )) + } + }}; + // First token is an identifier — always resolve via nixpkgs + ($cmd:ident $($rest:tt)*) => {{ + let resolved: String = $crate::msh_get_nix_bin(::std::stringify!($cmd)); + let mut cmd = ::std::process::Command::new(resolved); + $(cmd.arg(::std::stringify!($rest));)* + let status = cmd.status()?; + if status.success() { + ::std::result::Result::Ok(()) + } else { + ::std::result::Result::Err(::std::io::Error::new( + ::std::io::ErrorKind::Other, + ::std::format!("msh: command exited with {}", status), + )) + } + }}; +} + pub mod prelude { pub use super::*; pub use instance::Mize; @@ -97,6 +148,99 @@ pub extern "C" fn mize_rust_hack_set_global(mize: Arc>) { let _ = GLOBAL_MIZE.set(mize); } +/// Resolve an executable name to its full path in the nixpkgs store. +/// +/// Reads `nix.pkgs.ref` (nixpkgs commit hash) and `nix.bin` (path to the `nix` +/// binary) from the Mize config, then runs: +/// +/// ```text +/// eval --raw nixpkgs/# +/// ``` +/// +/// and returns `/bin/`. +/// +/// Certain executable names are mapped to different nixpkgs attribute names +/// via a hardcoded match (e.g. `npm` → `nodejs`). +/// +/// # Panics +/// Panics if the global Mize instance is not set, the config values are missing, +/// or `nix eval` fails. +pub fn msh_get_nix_bin(name: &str) -> String { + // Hardcoded mapping of executable names to nixpkgs attribute names + let pkg = match name { + "npm" => "nodejs", + "node" => "nodejs", + "yarn" => "yarn", + "pnpm" => "nodePackages.pnpm", + _ => name, + }; + + let mut mize = get_global_mize().expect("msh_get_nix_bin: no global Mize instance set"); + + let nixpkgs_ref = mize + .get_config("nix.pkgs.ref") + .expect("msh_get_nix_bin: failed to get config nix.pkgs.ref") + .value_string() + .expect("msh_get_nix_bin: nix.pkgs.ref is not a string"); + + let nix_bin = mize + .get_config("nix.bin") + .expect("msh_get_nix_bin: failed to get config nix.bin") + .value_string() + .expect("msh_get_nix_bin: nix.bin is not a string"); + + // Run: nix eval --raw nixpkgs/# + // --raw outputs just the store path without quotes + let attr = format!("nixpkgs/{}#{}", nixpkgs_ref, pkg); + let output = std::process::Command::new(&nix_bin) + .args(["eval", "--raw", &attr]) + .output() + .expect("msh_get_nix_bin: failed to spawn nix eval"); + + if !output.status.success() { + panic!( + "msh_get_nix_bin: nix eval failed for {}: {}", + attr, + String::from_utf8_lossy(&output.stderr) + ); + } + + let out_path = String::from_utf8(output.stdout) + .expect("msh_get_nix_bin: nix eval output is not valid UTF-8") + .trim() + .to_string(); + + format!("{}/bin/{}", out_path, name) +} + +/// Run a command with Mize Shell executable resolution. +/// +/// This is the function-based equivalent of the [`msh!`] macro, with proper +/// argument handling (no `stringify!` limitations). +/// +/// If `cmd` contains `/` it is treated as a path and used as-is. +/// Otherwise it is resolved via [`msh_get_nix_bin`]. +/// +/// Returns `Ok(())` if the command exits successfully, or an `io::Error` otherwise. +pub fn msh_run(cmd: &str, args: &[&str]) -> std::io::Result<()> { + let resolved = if cmd.contains('/') { + cmd.to_string() + } else { + msh_get_nix_bin(cmd) + }; + let mut command = std::process::Command::new(resolved); + command.args(args); + let status = command.status()?; + if status.success() { + Ok(()) + } else { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("msh: command exited with {}", status), + )) + } +} + // platform specific stuff pub mod platform { use crate::{Mize, MizeResult}; diff --git a/macros/src/lib.rs b/macros/src/lib.rs index a1a5bac..cfa5c20 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,6 +1,6 @@ use proc_macro::TokenStream; use quote::quote; -use syn::{self, parse::Parse, parse::ParseStream, Ident, LitStr, Token, TypePath}; +use syn::{self, parse::Parse, parse::ParseStream, Ident, LitStr, Token, Type, TypePath}; use syn::punctuated::Punctuated; use syn::token; @@ -9,24 +9,76 @@ use syn::token; //////////////////////////////////// include_native! macro /////////////////////////////////////// /// A procedural macro to register methods and associated functions of a foreign type with mize. /// -/// Usage 1: Simplified syntax with method names +/// Usage 1: Simplified syntax with no-argument methods /// ```ignore -/// include_native!("Command", clap::Command, [arg, subcommand]); +/// include_native!("Command", clap::Command, [get_name, is_empty]); /// ``` /// -/// Usage 2: With associated functions +/// Usage 2: With argument specifications +/// ```ignore +/// include_native!("Command", clap::Command, [ +/// arg(arg: clap::Arg), +/// subcommand(cmd: clap::Command) +/// ]); +/// ``` +/// +/// Usage 3: Block syntax separating associated functions and methods /// ```ignore /// include_native!("Command", clap::Command, { -/// associated: [new], -/// methods: [arg, subcommand] +/// associated: [new(name: String)], +/// methods: [arg(arg: clap::Arg), subcommand(cmd: clap::Command)] /// }); /// ``` +/// A function specification: name or name(arg1: Type1, arg2: Type2, ...) +#[derive(Debug, Clone)] +struct FuncSpec { + name: Ident, + args: Vec<(Ident, Type)>, +} + +impl Parse for FuncSpec { + fn parse(input: ParseStream) -> syn::Result { + let name: Ident = input.parse()?; + let mut args = Vec::new(); + + // Check for parentheses with arguments + if input.peek(syn::token::Paren) { + let content; + syn::parenthesized!(content in input); + + // Parse comma-separated arg: Type pairs + while !content.is_empty() { + let arg_name: Ident = content.parse()?; + content.parse::()?; + let arg_type: Type = content.parse()?; + args.push((arg_name, arg_type)); + + if !content.is_empty() { + content.parse::()?; + } + } + } + + Ok(FuncSpec { name, args }) + } +} + +/// Helper struct to parse a list of function specifications +struct FuncSpecList(Vec); + +impl Parse for FuncSpecList { + fn parse(input: ParseStream) -> syn::Result { + let punctuated = Punctuated::::parse_terminated(input)?; + Ok(FuncSpecList(punctuated.into_iter().collect())) + } +} + /// Input for the simplified syntax: include_native!("name", Type, [method1, method2, ...]) struct IncludeNativeSimple { name: LitStr, type_path: TypePath, - methods: Punctuated, + methods: Vec, } impl Parse for IncludeNativeSimple { @@ -38,35 +90,18 @@ impl Parse for IncludeNativeSimple { let content; syn::bracketed!(content in input); - let methods = Punctuated::parse_terminated(&content)?; + let methods = content.parse::()?.0; Ok(IncludeNativeSimple { name, type_path, methods }) } } -/// Helper struct to parse a list of identifiers -struct IdentList(Vec); - -impl Parse for IdentList { - fn parse(input: ParseStream) -> syn::Result { - let punctuated = Punctuated::::parse_terminated(input)?; - Ok(IdentList(punctuated.into_iter().collect())) - } -} - -/// Helper to parse a token stream as a list of identifiers (expects comma-separated idents) -fn parse_ident_list(stream: proc_macro2::TokenStream) -> Vec { - syn::parse2::(stream) - .map(|list| list.0) - .unwrap_or_default() -} - /// Struct for parsing the block syntax with associated and methods struct IncludeNativeBlock { name: LitStr, type_path: TypePath, - associated: Vec, - methods: Vec, + associated: Vec, + methods: Vec, } impl Parse for IncludeNativeBlock { @@ -106,7 +141,9 @@ impl Parse for IncludeNativeBlock { while j < tokens.len() { if let proc_macro2::TokenTree::Group(g) = &tokens[j] { if g.delimiter() == proc_macro2::Delimiter::Bracket { - associated = parse_ident_list(g.stream()); + associated = syn::parse2::(g.stream()) + .map(|l| l.0) + .unwrap_or_default(); break; } } @@ -130,7 +167,9 @@ impl Parse for IncludeNativeBlock { while j < tokens.len() { if let proc_macro2::TokenTree::Group(g) = &tokens[j] { if g.delimiter() == proc_macro2::Delimiter::Bracket { - methods = parse_ident_list(g.stream()); + methods = syn::parse2::(g.stream()) + .map(|l| l.0) + .unwrap_or_default(); break; } } @@ -165,26 +204,47 @@ pub fn include_native(input: TokenStream) -> TokenStream { syn::Error::new( proc_macro2::Span::call_site(), "Failed to parse include_native! input. Expected:\n \ - include_native!(\"name\", Type, [method1, method2, ...])\n or\n \ - include_native!(\"name\", Type, { associated: [func1], methods: [method1] })", + include_native!(\"name\", Type, [method1, method2(arg: Type), ...])\n or\n \ + include_native!(\"name\", Type, { associated: [func1(arg: Type)], methods: [method1] })", ) .to_compile_error() .into() } +/// Generate the argument extraction code for a function spec +fn generate_arg_extraction(args: &[(Ident, Type)]) -> Vec { + args.iter().enumerate().map(|(i, (arg_name, arg_type))| { + quote! { + let #arg_name = args.arr_nth(#i)?.native_clone::<#arg_type>()?; + } + }).collect() +} + +/// Generate the argument passing code for a function call +fn generate_arg_passing(args: &[(Ident, Type)]) -> Vec { + args.iter().map(|(arg_name, _)| { + quote! { #arg_name } + }).collect() +} + fn expand_simple_include_native(simple: IncludeNativeSimple) -> TokenStream { let name = simple.name; let type_path = simple.type_path; let methods = simple.methods; let method_calls = methods.iter().map(|method| { - let method_name = method.to_string(); + let method_name = method.name.to_string(); + let method_ident = &method.name; + let arg_extractions = generate_arg_extraction(&method.args); + let arg_passings = generate_arg_passing(&method.args); + quote! { .method(#method_name, |args| { + #(#arg_extractions)* let this = mize::prelude::get_global_mize()? .get_config(#name)? .native_mut::<#type_path>()?; - let result = this.#method(); + let result = this.#method_ident(#(#arg_passings),*); Ok(mize::item::ItemData::from_native(result)?) }) } @@ -205,23 +265,33 @@ fn expand_block_include_native(block: IncludeNativeBlock) -> TokenStream { let methods = block.methods; let method_calls = methods.iter().map(|method| { - let method_name = method.to_string(); + let method_name = method.name.to_string(); + let method_ident = &method.name; + let arg_extractions = generate_arg_extraction(&method.args); + let arg_passings = generate_arg_passing(&method.args); + quote! { .method(#method_name, |args| { + #(#arg_extractions)* let this = mize::prelude::get_global_mize()? .get_config(#name)? .native_mut::<#type_path>()?; - let result = this.#method(); + let result = this.#method_ident(#(#arg_passings),*); Ok(mize::item::ItemData::from_native(result)?) }) } }); let associated_calls = associated.iter().map(|func| { - let func_name = func.to_string(); + let func_name = func.name.to_string(); + let func_ident = &func.name; + let arg_extractions = generate_arg_extraction(&func.args); + let arg_passings = generate_arg_passing(&func.args); + quote! { .method(#func_name, |args| { - let result = <#type_path>::#func(); + #(#arg_extractions)* + let result = <#type_path>::#func_ident(#(#arg_passings),*); Ok(mize::item::ItemData::from_native(result)?) }) } diff --git a/parts/cli/src/lib.rs b/parts/cli/src/lib.rs index 9140a38..189d048 100644 --- a/parts/cli/src/lib.rs +++ b/parts/cli/src/lib.rs @@ -6,14 +6,10 @@ use mize::prelude::*; #[unsafe(no_mangle)] pub extern "C" fn part_cli() -> MizeResult<()> { - // Register methods of clap::Command with mize - // Note: associated functions like `new` require arguments, so they're not included here - // Use the block syntax to separate associated functions from methods: - // include_native!("Command", clap::Command, { - // associated: [some_associated_fn], // functions called as Type::fn() - // methods: [arg, subcommand] // methods called as this.fn() - // }); - include_native!("Command", clap::Command, []); + include_native!("Command", clap::Command, { + associated: [new(name: String)], + //methods: [arg(arg: clap::Arg), subcommand(cmd: clap::Command)] + }); opt("state") .fun(Box::new(|| { diff --git a/parts/mos/Cargo.toml b/parts/mos/Cargo.toml new file mode 100644 index 0000000..4b06b45 --- /dev/null +++ b/parts/mos/Cargo.toml @@ -0,0 +1,21 @@ +cargo-features = ["profile-rustflags"] + +[package] +name = "mize-part-mos" +version = "0.1.0" +edition = "2024" + +[lib] +name = "mize_part_mos" +crate-type = [ "cdylib", "rlib" ] + +[dependencies] +log = "0.4.34" +mize = { workspace = true } + + +[profile.dev] +rustflags = ["-C", "prefer-dynamic", "-C", "link-args=-Wl,--export-dynamic"] + +[profile.release] +rustflags = ["-C", "prefer-dynamic"] diff --git a/parts/mos/src/generated.rs b/parts/mos/src/generated.rs new file mode 100644 index 0000000..f36c1e0 --- /dev/null +++ b/parts/mos/src/generated.rs @@ -0,0 +1,70 @@ +use mize::prelude::*; + + +pub struct CliPart { + id: MizeId +} + +impl MizePart for CliPart { + fn id(&self) -> MizeId { + self.id.clone() + } +} + +impl CliPart { + pub fn get(name: &str) -> MizeResult { + let mut mize = get_global_mize()?; + let part = mize.get_part(name)?; + Ok(CliPart { id: part.id() }) + } + pub fn run(&self, cmdline: Vec) -> MizeResult<()> { + let cmdline = cmdline.into_item_data(); + let mut args = ItemData::new(); + args.set_path("0", cmdline)?; + dyn_call(self.id.clone(), "run", args)?; + Ok(()) + } + pub fn set_command(&self, name: &str) -> MizeResult<()> { + let name = name.into_item_data(); + let mut args = ItemData::new(); + args.set_path("0", name)?; + dyn_call(self.id.clone(), "set_command", args)?; + Ok(()) + } + pub fn subcommand MizeResult<()> + Send + Sync + 'static>(&self, subcmd: Command, action: T) -> MizeResult<()> { + let mut subcmd_arg = ItemData::new(); + subcmd_arg.set_path("_native", subcmd.native_id)?; + let mut action_arg = ItemData::from_native(action)?; + let mut args = ItemData::new(); + args.set_path("0", subcmd_arg)?; + args.set_path("1", action_arg)?; + dyn_call(self.id.clone(), "subcommand", args)?; + Ok(()) + } +} + +// the clap::Command native stuff +pub struct Command { + native_id: u32 +} + +pub struct ArgMatches { + native_id: u32 +} + +pub struct Action { + native_id: u32 +} + +impl Command { + pub fn new(name: &str) -> MizeResult { + let name = name.into_item_data(); + let mut args = ItemData::new(); + args.set_path("0", name)?; + let mut mize = get_global_mize()?; + let id = mize.id_new_dots("cli.Command")?; + let data = dyn_call(id.clone(), "new", args)?; + let native_id: u32 = data.get_path("_native")?.native_take()?; + Ok(Command { native_id }) + } +} diff --git a/parts/mos/src/lib.rs b/parts/mos/src/lib.rs new file mode 100644 index 0000000..45c5cb4 --- /dev/null +++ b/parts/mos/src/lib.rs @@ -0,0 +1,63 @@ +use std::fs::create_dir_all; +use mize::prelude::*; +use cmd_lib::run_cmd as sh; +use mize::tracing::info; + +// a static version of what would be generated. +// as we need mize.dev to generate anything. +mod generated; +use generated::*; + +// spacetime gen command: sh!(RUST_LOG=error RUST_BACKTRACE=0 spacetime generate --build-options="--lint-dir=" -p $SRC/mize/marts/spacetime -o $SRC/mize/marts/generated/src/spacetime --lang rust)?; + +#[unsafe(no_mangle)] +extern "C" fn part_dev() -> MizeResult<()> { + let mut mize = get_global_mize()?; + let mut cli = generated::CliPart::get("cli")?; + cli.set_command("dev"); + + cli.subcommand(Command::new("test")?, |_| {println!("hello from test command"); Ok(())})?; + //cli.subcommand(Command::new("publish").alias("p"), publish_mize)?; + //cli.subcommand(Command::new("genBindings").alias("b"), gen_bindings)?; + + Ok(()) +} + +pub fn publish_mize(_ : &ArgMatches, mut mize: Mize) -> MizeResult<()> { + let mize_src = mize.get_config("mize.src")?.value_string()?; + let cargo_dir = std::env::home_dir().unwrap().join(".cargo"); + + let targets = vec![ + "x86_64-pc-windows-gnu", + "aarch64-pc-windows-gnullvm", + + "x86_64-unknown-linux-gnu", + "aarch64-unknown-linux-gnu", + "armv7-unknown-linux-gnueabihf", + "arm-unknown-linux-gnueabi", + + "aarch64-apple-darwin", + "x86_64-apple-darwin", + ]; + + for target_name in targets { + let docker_image = match target_name { + "aarch64-apple-darwin" => "darwin", + "x86_64-apple-darwin" => "darwin", + _ => "base", + }; + sh!(docker build -t mize-build-$docker_image $mize_src/parts/mize.dev/targets/$docker_image)?; + let res = sh!(docker run -it --rm -v $cargo_dir/registry:/usr/local/cargo/registry -v $cargo_dir/git:/usr/local/cargo/git -v $mize_src:/src -v $mize_src/target:/src/target mize-build-$docker_image cargo build --target $target_name --release); + if res.is_err() { + info!("build for {target_name} failed..."); + } + }; + + + Ok(()) +} + +pub fn gen_bindings(_ : &ArgMatches, mize: Mize) -> MizeResult<()> { + + Ok(()) +}