fully compiles

This commit is contained in:
Sebastian Moser
2026-10-23 12:22:23 +02:00
parent 2b2d40148b
commit 9141fc5441
6 changed files with 251 additions and 83 deletions

1
Cargo.lock generated
View File

@@ -845,6 +845,7 @@ dependencies = [
name = "mize-part-dev"
version = "0.1.0"
dependencies = [
"cmd_lib",
"log",
"mize",
]

View File

@@ -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)
}

View File

@@ -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
};
}

View File

@@ -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 ---

View File

@@ -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(|| {

View File

@@ -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]