dyn calling works + vibed dioxus ui components + some mo provision prototyping

This commit is contained in:
Sebastian Moser
2026-10-31 17:50:46 +01:00
parent 37cb94d1d4
commit 66b8713e3a
60 changed files with 9056 additions and 201 deletions

View File

@@ -0,0 +1,167 @@
//! Code generation for mize bindings.
//!
//! Takes recorded PartMetadata (collected while in generate mode) and produces
//! a Rust source file that looks like the hand-written `generated.rs`.
use mize::prelude::*;
use mize::instance::{PartMetadata, MethodMetadata, OptMetadata, NativeMetadata};
use std::fmt::Write as _;
/// Convert a part name like "cli" into a PascalCase struct name like "CliPart".
fn part_name_to_struct(part_name: &str) -> String {
let mut out = String::new();
let mut upper_next = true;
for c in part_name.chars() {
if c == '.' || c == '-' || c == '_' || c == ' ' {
upper_next = true;
continue;
}
if upper_next {
out.extend(c.to_uppercase());
upper_next = false;
} else {
out.push(c);
}
}
out.push_str("Part");
out
}
/// Generate the full `src/lib.rs` content for the generated bindings package.
pub fn generate_lib_rs(all_metadata: &[PartMetadata]) -> String {
let mut out = String::new();
out.push_str("// AUTO-GENERATED by mize dev gen -- do not edit manually\n");
out.push_str("use mize::prelude::*;\n\n\n");
for meta in all_metadata {
generate_part_struct(&mut out, meta);
}
out
}
fn generate_part_struct(out: &mut String, meta: &PartMetadata) {
let struct_name = part_name_to_struct(&meta.part_name);
let part_id_str = meta.part_name.replace('.', ".");
// struct definition
let _ = writeln!(out, "pub struct {struct_name} {{");
let _ = writeln!(out, " id: MizeId");
let _ = writeln!(out, "}}");
let _ = writeln!(out);
// MizePart impl
let _ = writeln!(out, "impl MizePart for {struct_name} {{");
let _ = writeln!(out, " fn id(&self) -> MizeId {{");
let _ = writeln!(out, " self.id.clone()");
let _ = writeln!(out, " }}");
let _ = writeln!(out, "}}");
let _ = writeln!(out);
// inherent impl
let _ = writeln!(out, "impl {struct_name} {{");
// get() constructor
let _ = writeln!(out, " pub fn get(name: &str) -> MizeResult<{struct_name}> {{");
let _ = writeln!(out, " let mut mize = get_global_mize()?;");
let _ = writeln!(out, " let part = mize.get_part(name)?;");
let _ = writeln!(out, " Ok({struct_name} {{ id: part.id() }})");
let _ = writeln!(out, " }}");
// one method per recorded method/effect/thread
for m in meta.methods.iter().chain(meta.effects.iter()).chain(meta.threads.iter()) {
generate_method_fn(out, &struct_name, m);
}
// one getter-ish fn per recorded opt (opts are config values; expose a dyn_call getter)
for o in &meta.opts {
generate_opt_fn(out, &struct_name, o);
}
let _ = writeln!(out, "}}");
let _ = writeln!(out);
// native type wrappers
for n in &meta.natives {
generate_native_struct(out, n);
}
let _ = writeln!(out, "// part id string: {part_id_str}");
let _ = writeln!(out);
}
fn generate_method_fn(out: &mut String, _struct_name: &str, m: &MethodMetadata) {
let fn_name = &m.name;
let _ = writeln!(
out,
" pub fn {fn_name}(&self, args: ItemData) -> MizeResult<ItemData> {{"
);
let _ = writeln!(out, " dyn_call(self.id.clone(), \"{fn_name}\", args)?;");
// note: dyn_call returns ItemData already, so wrap:
let _ = writeln!(out, " dyn_call(self.id.clone(), \"{fn_name}\", args)");
let _ = writeln!(out, " }}");
let _ = writeln!(out);
}
fn generate_opt_fn(out: &mut String, _struct_name: &str, o: &OptMetadata) {
let fn_name = &o.name;
let _ = writeln!(
out,
" pub fn opt_{fn_name}(&self) -> MizeResult<ItemData> {{"
);
let _ = writeln!(out, " get_config(&format!(\"{{}}/{fn_name}\", self.id.dot()))");
let _ = writeln!(out, " }}");
let _ = writeln!(out);
}
fn generate_native_struct(out: &mut String, n: &NativeMetadata) {
let struct_name = &n.name;
let _ = writeln!(out, "// native type: {} ({})", n.name, n.type_name);
let _ = writeln!(out, "pub struct {struct_name} {{");
let _ = writeln!(out, " native_id: u32");
let _ = writeln!(out, "}}");
let _ = writeln!(out);
let _ = writeln!(out, "impl {struct_name} {{");
for assoc in &n.associated {
let _ = writeln!(
out,
" pub fn {assoc}(args: ItemData) -> MizeResult<ItemData> {{"
);
let _ = writeln!(out, " let mut mize = get_global_mize()?;");
let _ = writeln!(out, " let id = mize.id_new_dots(\"{}.{assoc}\")?;", n.name);
let _ = writeln!(out, " dyn_call(id, \"{assoc}\", args)");
let _ = writeln!(out, " }}");
}
for meth in &n.methods {
let _ = writeln!(
out,
" pub fn {meth}(&self, args: ItemData) -> MizeResult<ItemData> {{"
);
let _ = writeln!(out, " let mut mize = get_global_mize()?;");
let _ = writeln!(out, " let id = mize.id_new_dots(\"{}.{meth}\")?;", n.name);
let _ = writeln!(out, " dyn_call(id, \"{meth}\", args)");
let _ = writeln!(out, " }}");
}
let _ = writeln!(out, "}}");
let _ = writeln!(out);
}
/// Generate the `Cargo.toml` content for the generated package.
pub fn generate_cargo_toml(package_name: &str) -> String {
format!(
r#"[package]
name = "{package_name}"
version = "0.1.0"
edition = "2024"
[lib]
name = "{lib_name}"
path = "src/lib.rs"
[dependencies]
mize = {{ path = "../../core" }}
"#,
package_name = package_name,
lib_name = package_name.replace('-', "_"),
)
}

View File

@@ -34,7 +34,11 @@ impl CliPart {
pub fn subcommand<T: Fn(ArgMatches) -> 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 action_arg = ItemData::fun(Box::new(move |args: ItemData| {
let matches = args.clone().get_path("0")?.native_take_from::<ArgMatches>()?;
action(matches)?;
Ok(ItemData::new())
}))?;
let mut args = ItemData::new();
args.set_path("0", subcmd_arg)?;
args.set_path("1", action_arg)?;
@@ -52,6 +56,12 @@ pub struct ArgMatches {
native_id: u32
}
impl FromItemData for ArgMatches {
fn from_itemdata(data: ItemData) -> MizeResult<Self> {
Ok(ArgMatches { native_id: data.get_path("_native")?.value_u32()? })
}
}
pub struct Action {
native_id: u32
}

View File

@@ -1,4 +1,4 @@
use std::fs::create_dir_all;
use std::fs::{create_dir_all, write};
use mize::prelude::*;
use cmd_lib::run_cmd as sh;
use mize::tracing::info;
@@ -6,6 +6,7 @@ use mize::tracing::info;
// a static version of what would be generated.
// as we need mize.dev to generate anything.
mod generated;
mod codegen;
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)?;
@@ -17,13 +18,16 @@ extern "C" fn part_dev() -> MizeResult<()> {
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)?;
cli.subcommand(Command::new("generate")?, gen_bindings)?;
cli.subcommand(Command::new("gen")?, gen_bindings)?; // alias for generate
cli.subcommand(Command::new("publish")?, publish_mize)?;
cli.subcommand(Command::new("p")?, publish_mize)?;
Ok(())
}
pub fn publish_mize(_ : &ArgMatches, mut mize: Mize) -> MizeResult<()> {
pub fn publish_mize(_ : ArgMatches) -> MizeResult<()> {
let mut mize = get_global_mize()?;
let mize_src = mize.get_config("mize.src")?.value_string()?;
let cargo_dir = std::env::home_dir().unwrap().join(".cargo");
@@ -57,7 +61,77 @@ pub fn publish_mize(_ : &ArgMatches, mut mize: Mize) -> MizeResult<()> {
Ok(())
}
pub fn gen_bindings(_ : &ArgMatches, mize: Mize) -> MizeResult<()> {
/// The `dev generate` command (alias `gen` to be added once bindings support it).
///
/// Reads a `part` TOML file from the current directory, loads the parts listed
/// in `gen = [...]` in generate mode (recording metadata instead of executing),
/// then writes a full Cargo package with Rust bindings to `<cwd>/generated/`.
pub fn gen_bindings(_ : ArgMatches) -> MizeResult<()> {
let mut mize = get_global_mize()?;
// 1. read the "part" file from the current directory
let cwd = std::env::current_dir().map_err(|e| mize_err!("could not get current dir: {e}"))?;
let part_file = cwd.join("part");
info!("reading part description file: {}", part_file.display());
let part_toml = std::fs::read_to_string(&part_file)
.map_err(|e| mize_err!("could not read part file {}: {e}", part_file.display()))?;
let part_data = ItemData::from_toml(&part_toml)?;
// 2. parse the part name and the gen = [...] list
let part_name: String = part_data
.get_path("name")
.ok()
.and_then(|v| v.value_string().ok())
.ok_or_else(|| mize_err!("part file must have a 'name' field"))?;
info!("part name: {part_name}");
let gen_data = part_data
.get_path("gen")
.map_err(|_| mize_err!("part file must have a 'gen' array, eg: gen = [\"cli\", \"auth\"]"))?;
let gen_arr = gen_data
.cbor()
.as_array()
.ok_or_else(|| mize_err!("'gen' is not an array"))?;
let mut gen_parts: Vec<String> = Vec::new();
for v in gen_arr.iter() {
let s = v.as_text().ok_or_else(|| mize_err!("'gen' array must contain only strings"))?;
gen_parts.push(s.to_owned());
}
info!("parts to generate bindings for: {:?}", gen_parts);
// 3. enable generate mode and clear any old metadata
mize.set_generate_mode(true)?;
mize.clear_metadata()?;
// 4. load each part — in generate mode their part_smth funcs only record metadata
for p in &gen_parts {
info!("loading part in generate mode: {p}");
mize.load_part(p)?;
}
// 5. disable generate mode again
mize.set_generate_mode(false)?;
// 6. collect all recorded metadata
let all_metadata = mize.get_all_metadata()?;
info!("recorded metadata for {} part(s)", all_metadata.len());
// 7. generate the bindings package at <cwd>/generated
let gen_dir = cwd.join("generated");
let src_dir = gen_dir.join("src");
create_dir_all(&src_dir).map_err(|e| mize_err!("could not create generated/src dir: {e}"))?;
let package_name = format!("mize-generated-{part_name}");
let cargo_toml = codegen::generate_cargo_toml(&package_name);
write(gen_dir.join("Cargo.toml"), cargo_toml)
.map_err(|e| mize_err!("could not write generated/Cargo.toml: {e}"))?;
let lib_rs = codegen::generate_lib_rs(&all_metadata);
write(src_dir.join("lib.rs"), lib_rs)
.map_err(|e| mize_err!("could not write generated/src/lib.rs: {e}"))?;
info!("bindings written to {}", gen_dir.display());
println!("generated bindings package: {}", gen_dir.display());
Ok(())
}