fully compiles
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -845,6 +845,7 @@ dependencies = [
|
||||
name = "mize-part-dev"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cmd_lib",
|
||||
"log",
|
||||
"mize",
|
||||
]
|
||||
|
||||
@@ -4,3 +4,8 @@ pub fn dyn_call(part_id: MizeId, method: &str, args: ItemData) -> MizeResult<Ite
|
||||
let mut mize = get_global_mize()?;
|
||||
mize.dyn_call(part_id, method, args)
|
||||
}
|
||||
|
||||
pub fn get_config(name: &str) -> MizeResult<ItemData> {
|
||||
let mut mize = get_global_mize()?;
|
||||
mize.get_config(name)
|
||||
}
|
||||
|
||||
@@ -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::<clap::Command>()?;
|
||||
// let result = this.new();
|
||||
// Ok(from_native(result))
|
||||
// })
|
||||
// .method("arg", |args| {
|
||||
// let this = get_config("Command")?.native_mut::<clap::Command>()?;
|
||||
// let result = this.arg();
|
||||
// Ok(from_native(result))
|
||||
// })
|
||||
// .method("subcommand", |args| {
|
||||
// let this = get_config("Command")?.native_mut::<clap::Command>()?;
|
||||
// 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::<clap::Arg>()?;
|
||||
// let this = get_config("clap::Command")?.native_mut::<clap::Command>()?;
|
||||
// let result = this.arg(arg);
|
||||
// Ok(from_native(result))
|
||||
// })
|
||||
// .method("subcommand", |args| {
|
||||
// let cmd = args.arr_nth(0)?.native::<clap::Command>()?;
|
||||
// let this = get_config("clap::Command")?.native_mut::<clap::Command>()?;
|
||||
// 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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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<Ident, Token![,]>,
|
||||
}
|
||||
|
||||
impl Parse for IncludeNativeSimple {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let name = input.parse()?;
|
||||
input.parse::<Token![,]>()?;
|
||||
let type_path = input.parse()?;
|
||||
input.parse::<Token![,]>()?;
|
||||
|
||||
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<Ident>);
|
||||
|
||||
impl Parse for IdentList {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let punctuated = Punctuated::<Ident, Token![,]>::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<Ident> {
|
||||
syn::parse2::<IdentList>(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<Ident>,
|
||||
methods: Vec<Ident>,
|
||||
}
|
||||
|
||||
impl Parse for IncludeNativeBlock {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let name = input.parse()?;
|
||||
input.parse::<Token![,]>()?;
|
||||
let type_path = input.parse()?;
|
||||
input.parse::<Token![,]>()?;
|
||||
|
||||
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<proc_macro2::TokenTree> = content.parse::<proc_macro2::TokenStream>()?.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::<IncludeNativeSimple>(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::<IncludeNativeBlock>(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 ---
|
||||
|
||||
|
||||
@@ -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(|| {
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user