fully compiles
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -845,6 +845,7 @@ dependencies = [
|
|||||||
name = "mize-part-dev"
|
name = "mize-part-dev"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"cmd_lib",
|
||||||
"log",
|
"log",
|
||||||
"mize",
|
"mize",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -4,3 +4,8 @@ pub fn dyn_call(part_id: MizeId, method: &str, args: ItemData) -> MizeResult<Ite
|
|||||||
let mut mize = get_global_mize()?;
|
let mut mize = get_global_mize()?;
|
||||||
mize.dyn_call(part_id, method, args)
|
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 item::{Item, ItemData, IntoItemData};
|
||||||
pub use config::*;
|
pub use config::*;
|
||||||
pub use api::*;
|
pub use api::*;
|
||||||
pub use super::include_native;
|
|
||||||
pub use mize_macros::*;
|
pub use mize_macros::*;
|
||||||
}
|
}
|
||||||
pub use flume;
|
pub use flume;
|
||||||
|
pub use tracing;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -133,82 +133,3 @@ pub mod platform {
|
|||||||
Ok("alsdkjf".to_owned())
|
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 proc_macro::TokenStream;
|
||||||
use quote::quote;
|
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 ///////////////////////////////////////
|
//////////////////////////////////// data! macro ///////////////////////////////////////
|
||||||
// A procedural macro to define data in Nix-style and expand to a `ciborium::Value` (CBOR).
|
// 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"
|
// ciborium = "0.2"
|
||||||
// ```
|
// ```
|
||||||
|
|
||||||
use syn::{Ident, Lit, Token, parse::Parse, parse::ParseStream, punctuated::Punctuated, token};
|
use syn::Lit;
|
||||||
|
|
||||||
// --- AST ---
|
// --- AST ---
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,14 @@ use mize::prelude::*;
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn part_cli() -> MizeResult<()> {
|
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")
|
opt("state")
|
||||||
.fun(Box::new(|| {
|
.fun(Box::new(|| {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ crate-type = [ "cdylib", "rlib" ]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4.34"
|
log = "0.4.34"
|
||||||
mize = { workspace = true }
|
mize = { workspace = true }
|
||||||
|
cmd_lib = { version = "2.0.1", optional = false }
|
||||||
|
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
|
|||||||
Reference in New Issue
Block a user