diff --git a/Cargo.lock b/Cargo.lock index fb80a3e..7b11dcd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -845,6 +845,7 @@ dependencies = [ name = "mize-part-dev" version = "0.1.0" dependencies = [ + "cmd_lib", "log", "mize", ] diff --git a/core/src/api.rs b/core/src/api.rs index 5e46c8f..80658a3 100644 --- a/core/src/api.rs +++ b/core/src/api.rs @@ -4,3 +4,8 @@ pub fn dyn_call(part_id: MizeId, method: &str, args: ItemData) -> MizeResult MizeResult { + let mut mize = get_global_mize()?; + mize.get_config(name) +} diff --git a/core/src/lib.rs b/core/src/lib.rs index facd1d4..2d7c2b0 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -31,10 +31,10 @@ pub mod prelude { pub use item::{Item, ItemData, IntoItemData}; pub use config::*; pub use api::*; - pub use super::include_native; pub use mize_macros::*; } pub use flume; +pub use tracing; @@ -133,82 +133,3 @@ pub mod platform { Ok("alsdkjf".to_owned()) } } - - -////////////////////////////////////// include_native! macro ///////////////////////////////////// -// A macro to register methods of a foreign type (like clap::Command) with mize. -// This generates bindings for the type to be stored in the native_values registry. - -// Usage 1: New simplified syntax (string name, type path, list of method names) -// ```ignore -// include_native!("Command", clap::Command, [new, arg, subcommand]); -// ``` -// -// This expands to: -// ```ignore -// opt("Command") -// .method("new", |args| { -// let this = get_config("Command")?.native_mut::()?; -// let result = this.new(); -// Ok(from_native(result)) -// }) -// .method("arg", |args| { -// let this = get_config("Command")?.native_mut::()?; -// let result = this.arg(); -// Ok(from_native(result)) -// }) -// .method("subcommand", |args| { -// let this = get_config("Command")?.native_mut::()?; -// let result = this.subcommand(); -// Ok(from_native(result)) -// }); -// ``` -// -// Usage 2: Traditional syntax (type path, method signatures in braces) -// ```ignore -// include_native!(clap::Command, { -// fn arg(&mut self, arg: clap::Arg) -> &mut clap::Command; -// fn subcommand(&mut self, cmd: clap::Command) -> &mut clap::Command; -// }); -// ``` -// -// This expands to: -// ```ignore -// opt("clap::Command") -// .method("arg", |args| { -// let arg = args.arr_nth(0)?.native::()?; -// let this = get_config("clap::Command")?.native_mut::()?; -// let result = this.arg(arg); -// Ok(from_native(result)) -// }) -// .method("subcommand", |args| { -// let cmd = args.arr_nth(0)?.native::()?; -// let this = get_config("clap::Command")?.native_mut::()?; -// let result = this.subcommand(cmd); -// Ok(from_native(result)) -// }); -// ``` - -#[macro_export] -macro_rules! include_native { - // New simplified syntax: ("name", type_path, [method1, method2, ...]) - ($name:literal, $type_path:ty, [$($method:ident),* $(,)?]) => { - $crate::config::opt($name) - $( - .method(stringify!($method), |args| { - let this = $crate::prelude::get_global_mize()? - .get_config($name)? - .native_mut::<$type_path>()?; - let result = this.$method(); - Ok($crate::item::ItemData::from_native(result)?) - }) - )* - }; - - // Traditional syntax: (type_path, { fn method1(...) -> ...; fn method2(...) -> ...; }) - ($type_path:ty, { $($methods:tt)* }) => { - $crate::config::opt(stringify!($type_path)) - // TODO: parse method signatures and generate appropriate bindings - // For now, this is a placeholder for the traditional syntax - }; -} diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 0b2cf54..a1a5bac 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,9 +1,242 @@ use proc_macro::TokenStream; use quote::quote; -use syn; +use syn::{self, parse::Parse, parse::ParseStream, Ident, LitStr, Token, TypePath}; +use syn::punctuated::Punctuated; +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 +/// ```ignore +/// include_native!("Command", clap::Command, [arg, subcommand]); +/// ``` +/// +/// Usage 2: With associated functions +/// ```ignore +/// include_native!("Command", clap::Command, { +/// associated: [new], +/// methods: [arg, subcommand] +/// }); +/// ``` + +/// Input for the simplified syntax: include_native!("name", Type, [method1, method2, ...]) +struct IncludeNativeSimple { + name: LitStr, + type_path: TypePath, + methods: Punctuated, +} + +impl Parse for IncludeNativeSimple { + fn parse(input: ParseStream) -> syn::Result { + let name = input.parse()?; + input.parse::()?; + let type_path = input.parse()?; + input.parse::()?; + + let content; + syn::bracketed!(content in input); + let methods = Punctuated::parse_terminated(&content)?; + + 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, +} + +impl Parse for IncludeNativeBlock { + fn parse(input: ParseStream) -> syn::Result { + let name = input.parse()?; + input.parse::()?; + let type_path = input.parse()?; + input.parse::()?; + + let content; + syn::braced!(content in input); + + let mut associated = Vec::new(); + let mut methods = Vec::new(); + + // Parse the content looking for associated: [...] and methods: [...] + // We'll collect all tokens first and then parse + let tokens: Vec = content.parse::()?.into_iter().collect(); + let mut i = 0; + while i < tokens.len() { + if let proc_macro2::TokenTree::Ident(id) = &tokens[i] { + let id_str = id.to_string(); + if id_str == "associated" { + // Find colon + let mut j = i + 1; + while j < tokens.len() { + if let proc_macro2::TokenTree::Punct(p) = &tokens[j] { + if p.as_char() == ':' { + break; + } + } + j += 1; + } + j += 1; // Skip colon + + // Find bracket group + 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()); + break; + } + } + j += 1; + } + i = j + 1; + } else if id_str == "methods" { + // Find colon + let mut j = i + 1; + while j < tokens.len() { + if let proc_macro2::TokenTree::Punct(p) = &tokens[j] { + if p.as_char() == ':' { + break; + } + } + j += 1; + } + j += 1; // Skip colon + + // Find bracket group + 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()); + break; + } + } + j += 1; + } + i = j + 1; + } else { + i += 1; + } + } else { + i += 1; + } + } + + Ok(IncludeNativeBlock { name, type_path, associated, methods }) + } +} + +#[proc_macro] +pub fn include_native(input: TokenStream) -> TokenStream { + // Try to parse as simplified syntax: include_native!("name", Type, [m1, m2, ...]) + if let Ok(simple) = syn::parse::(input.clone()) { + return expand_simple_include_native(simple); + } + + // Try to parse as block syntax: include_native!("name", Type, { associated: [...], methods: [...] }) + if let Ok(block) = syn::parse::(input) { + return expand_block_include_native(block); + } + + // If both fail, return a compile error + 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] })", + ) + .to_compile_error() + .into() +} + +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(); + quote! { + .method(#method_name, |args| { + let this = mize::prelude::get_global_mize()? + .get_config(#name)? + .native_mut::<#type_path>()?; + let result = this.#method(); + Ok(mize::item::ItemData::from_native(result)?) + }) + } + }); + + let expanded = quote! { + mize::config::opt(#name) + #(#method_calls)* + }; + + TokenStream::from(expanded) +} + +fn expand_block_include_native(block: IncludeNativeBlock) -> TokenStream { + let name = block.name; + let type_path = block.type_path; + let associated = block.associated; + let methods = block.methods; + + let method_calls = methods.iter().map(|method| { + let method_name = method.to_string(); + quote! { + .method(#method_name, |args| { + let this = mize::prelude::get_global_mize()? + .get_config(#name)? + .native_mut::<#type_path>()?; + let result = this.#method(); + Ok(mize::item::ItemData::from_native(result)?) + }) + } + }); + + let associated_calls = associated.iter().map(|func| { + let func_name = func.to_string(); + quote! { + .method(#func_name, |args| { + let result = <#type_path>::#func(); + Ok(mize::item::ItemData::from_native(result)?) + }) + } + }); + + let expanded = quote! { + mize::config::opt(#name) + #(#associated_calls)* + #(#method_calls)* + }; + + TokenStream::from(expanded) +} + + //////////////////////////////////// data! macro /////////////////////////////////////// // A procedural macro to define data in Nix-style and expand to a `ciborium::Value` (CBOR). @@ -20,7 +253,7 @@ use syn; // ciborium = "0.2" // ``` -use syn::{Ident, Lit, Token, parse::Parse, parse::ParseStream, punctuated::Punctuated, token}; +use syn::Lit; // --- AST --- diff --git a/parts/cli/src/lib.rs b/parts/cli/src/lib.rs index ae786e4..9140a38 100644 --- a/parts/cli/src/lib.rs +++ b/parts/cli/src/lib.rs @@ -6,7 +6,14 @@ use mize::prelude::*; #[unsafe(no_mangle)] pub extern "C" fn part_cli() -> MizeResult<()> { - include_native!("Command", clap::Command, [new]); + // 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, []); opt("state") .fun(Box::new(|| { diff --git a/parts/mize.dev/Cargo.toml b/parts/mize.dev/Cargo.toml index 1f6d24e..e8b4e10 100644 --- a/parts/mize.dev/Cargo.toml +++ b/parts/mize.dev/Cargo.toml @@ -12,6 +12,7 @@ crate-type = [ "cdylib", "rlib" ] [dependencies] log = "0.4.34" mize = { workspace = true } +cmd_lib = { version = "2.0.1", optional = false } [profile.dev]