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

@@ -1,4 +1,5 @@
use std::any::Any;
use std::fmt::Debug;
use crate::MizeResult;
@@ -7,7 +8,8 @@ use crate::id::MizeId;
use crate::item::{IntoItemData, ItemData};
use crate::Mize;
type ConfigFun = Box<dyn Fn() -> MizeResult<ItemData> + Send + Sync + 'static>;
pub type ConfigFun = Box<dyn Fn() -> MizeResult<ItemData> + Send + Sync + 'static>;
pub type MizeFun = Box<dyn Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>;
pub struct ConfigOpt {
pub name: String,
@@ -16,6 +18,16 @@ pub struct ConfigOpt {
pub deps: Vec<String>,
}
pub trait FromItemData {
fn from_itemdata(data: ItemData) -> MizeResult<Self> where Self: Sized;
}
impl Debug for ConfigOpt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}: {})", self.name, self.val.clone().unwrap_or(ItemData::new()))
}
}
#[derive(Clone)]
#[repr(C)]
pub struct ConfigOptId {
@@ -25,6 +37,11 @@ pub struct ConfigOptId {
impl ConfigOptId {
pub fn default_val(self, val: ItemData) -> Self {
let mize = get_global_mize().unwrap();
// in generate mode: record the default value as metadata, skip normal op
if mize.is_generate_mode().unwrap_or(false) {
mize.record_opt("_default_val", self.id.clone(), Some(val)).ok();
return self.clone();
}
let mut config_opts = mize.config_opts.lock().unwrap();
let opt = config_opts.get_mut(&self.id).unwrap();
opt.val = Some(val);
@@ -32,13 +49,35 @@ impl ConfigOptId {
}
pub fn fun(self, fun: ConfigFun) -> Self {
let mize = get_global_mize().unwrap();
// in generate mode: record as an opt with a fun, skip normal op
if mize.is_generate_mode().unwrap_or(false) {
mize.record_opt("_fun", self.id.clone(), None).ok();
return self.clone();
}
let mut config_opts = mize.config_opts.lock().unwrap();
println!("get_mut: {}", self.id);
let opt = config_opts.get_mut(&self.id).unwrap();
opt.fun = Some(fun);
self.clone()
}
pub fn method<F: Fn(ItemData) -> MizeResult<ItemData>>(self, _name: &str, _fun: F) -> ConfigOptId {
pub fn method<F: Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>(self, name: &str, fun: F) -> ConfigOptId {
let mize = get_global_mize().unwrap();
let mut cur_part = self.id.clone();
let id = cur_part.join(name).unwrap();
// in generate mode: record method metadata, skip normal registration
if mize.is_generate_mode().unwrap_or(false) {
mize.record_method(name, id.clone(), "method").ok();
return self.clone();
}
let mut opts = mize.config_opts.lock().unwrap();
let opt = ConfigOpt {
name: id.dot(),
val: None,
fun: None,
deps: Vec::new(),
};
let mut fun_values = mize.fun_values.lock().unwrap();
fun_values.insert(id.clone(), Box::new(fun));
opts.insert(id.clone(), opt);
self.clone()
}
@@ -48,13 +87,27 @@ impl ConfigOptId {
//fn into_item_data(self) -> ItemData;
//}
pub trait MizeAny {
fn as_raw_ptr(&self) -> *const ();
}
impl<T> MizeAny for T {
fn as_raw_ptr(&self) -> *const () {
self as *const Self as *const ()
}
}
pub fn opt(name: &str) -> crate::config::ConfigOptId {
let mize = get_global_mize().unwrap();
let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap();
let mut opts = mize.config_opts.lock().unwrap();
let id = cur_part.join(name).unwrap();
// in generate mode: record opt metadata, skip normal registration
if mize.is_generate_mode().unwrap_or(false) {
mize.record_opt(name, id.clone(), None).ok();
return ConfigOptId { id };
}
let mut opts = mize.config_opts.lock().unwrap();
let opt = ConfigOpt {
name: id.dot(),
val: None,
@@ -65,47 +118,68 @@ pub fn opt(name: &str) -> crate::config::ConfigOptId {
ConfigOptId { id }
}
pub fn method<F: Fn(ItemData) -> MizeResult<ItemData>>(name: &str, fun: F) -> ConfigOptId {
pub fn method<F: Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>(name: &str, fun: F) -> ConfigOptId {
let mize = get_global_mize().unwrap();
let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap();
let mut opts = mize.config_opts.lock().unwrap();
let id = cur_part.join(name).unwrap();
// in generate mode: record method metadata, skip normal registration
if mize.is_generate_mode().unwrap_or(false) {
mize.record_method(name, id.clone(), "method").ok();
return ConfigOptId { id };
}
let mut opts = mize.config_opts.lock().unwrap();
let opt = ConfigOpt {
name: id.dot(),
val: Some(ItemData::from_native(fun)?),
val: None,
fun: None,
deps: Vec::new(),
};
let mut fun_values = mize.fun_values.lock().unwrap();
fun_values.insert(id.clone(), Box::new(fun));
opts.insert(id.clone(), opt);
ConfigOptId { id }
}
pub fn effect<F: Fn(ItemData) -> MizeResult<ItemData>>(_name: &str, _fun: F) -> ConfigOptId {
pub fn effect<F: Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>(name: &str, fun: F) -> ConfigOptId {
let mize = get_global_mize().unwrap();
let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap();
let mut opts = mize.config_opts.lock().unwrap();
let id = cur_part.join(name).unwrap();
// in generate mode: record effect metadata, skip normal registration
if mize.is_generate_mode().unwrap_or(false) {
mize.record_method(name, id.clone(), "effect").ok();
return ConfigOptId { id };
}
let mut opts = mize.config_opts.lock().unwrap();
let opt = ConfigOpt {
name: id.dot(),
val: Some(ItemData::from_native(fun)?),
val: None,
fun: None,
deps: Vec::new(),
};
let mut fun_values = mize.fun_values.lock().unwrap();
fun_values.insert(id.clone(), Box::new(fun));
opts.insert(id.clone(), opt);
ConfigOptId { id }
}
pub fn thread<F: Fn(ItemData) -> MizeResult<ItemData>>(_name: &str, _fun: F) -> ConfigOptId {
pub fn thread<F: Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>(name: &str, fun: F) -> ConfigOptId {
let mize = get_global_mize().unwrap();
let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap();
let mut opts = mize.config_opts.lock().unwrap();
let id = cur_part.join(name).unwrap();
// in generate mode: record thread metadata, skip normal registration
if mize.is_generate_mode().unwrap_or(false) {
mize.record_method(name, id.clone(), "thread").ok();
return ConfigOptId { id };
}
let mut opts = mize.config_opts.lock().unwrap();
let opt = ConfigOpt {
name: id.dot(),
val: Some(ItemData::from_native(fun)?),
val: None,
fun: None,
deps: Vec::new(),
};
let mut fun_values = mize.fun_values.lock().unwrap();
fun_values.insert(id.clone(), Box::new(fun));
opts.insert(id.clone(), opt);
ConfigOptId { id }
}
@@ -125,7 +199,7 @@ pub mod c_api {
super::opt(&string)
}
}
pub extern "C" fn method(name: *const c_char, c_fun: CFun) {
pub extern "C" fn method(name: *const c_char, c_fun: CFun) -> ConfigOptId {
unsafe {
let c_str = CStr::from_ptr(name);
let string = c_str.to_string_lossy();
@@ -137,7 +211,7 @@ pub mod c_api {
})
}
}
pub extern "C" fn effect(name: *const c_char, c_fun: CFun) {
pub extern "C" fn effect(name: *const c_char, c_fun: CFun) -> ConfigOptId {
unsafe {
let c_str = CStr::from_ptr(name);
let string = c_str.to_string_lossy();
@@ -149,7 +223,7 @@ pub mod c_api {
})
}
}
pub extern "C" fn thread(name: *const c_char, c_fun: CFun) {
pub extern "C" fn thread(name: *const c_char, c_fun: CFun) -> ConfigOptId {
unsafe {
let c_str = CStr::from_ptr(name);
let string = c_str.to_string_lossy();

View File

@@ -7,12 +7,12 @@ use interner::shared::{StringPool, VecStringPool};
use std::any::Any;
use std::collections::HashMap;
use std::ops::{Deref, DerefMut};
use std::sync::{Arc, Mutex};
use std::sync::{Arc, Mutex, RwLock};
use std::{thread, vec};
use tracing::{debug, info, trace};
use crate::set_global_mize;
use crate::config::{ConfigOpt, ConfigOptId};
use crate::config::{ConfigOpt, ConfigOptId, MizeFun};
use crate::error::{MizeError, MizeResult};
use crate::id::{IntoMizeId, MizeId, Namespace};
use crate::instance::store::Store;
@@ -86,11 +86,51 @@ pub struct Mize {
pub native_values: Arc<Mutex<HashMap<u32, Box<dyn Any + Send + Sync>>>>,
pub next_native_value_id: Arc<Mutex<u32>>,
pub fun_values: Arc<Mutex<HashMap<MizeId, MizeFun>>>,
pub next_fun_values_id: Arc<Mutex<u32>>,
// generate mode: when true, part_smth functions record metadata instead of executing
pub generate_mode: Arc<RwLock<bool>>,
// recorded metadata per part (keyed by part id) while in generate mode
pub part_metadata: Arc<Mutex<HashMap<MizeId, PartMetadata>>>,
#[cfg(feature = "async")]
pub runtime: Arc<Mutex<Runtime>>,
}
/// Metadata recorded about a part while in generate mode
#[derive(Clone, Debug, Default)]
pub struct PartMetadata {
pub part_name: String,
pub opts: Vec<OptMetadata>,
pub methods: Vec<MethodMetadata>,
pub effects: Vec<MethodMetadata>,
pub threads: Vec<MethodMetadata>,
pub natives: Vec<NativeMetadata>,
}
#[derive(Clone, Debug)]
pub struct OptMetadata {
pub name: String,
pub id: MizeId,
pub default_val: Option<ItemData>,
}
#[derive(Clone, Debug)]
pub struct MethodMetadata {
pub name: String,
pub id: MizeId,
pub kind: String, // "method", "effect", "thread"
}
#[derive(Clone, Debug)]
pub struct NativeMetadata {
pub name: String,
pub type_name: String,
pub associated: Vec<String>,
pub methods: Vec<String>,
}
pub struct InstanceRef {
inner: Arc<Mutex<Mize>>,
}
@@ -160,6 +200,10 @@ impl Mize {
cur_eval_part: Arc::new(Mutex::new(None)),
native_values: Arc::new(Mutex::new(HashMap::new())),
next_native_value_id: Arc::new(Mutex::new(0)),
fun_values: Arc::new(Mutex::new(HashMap::new())),
next_fun_values_id: Arc::new(Mutex::new(0)),
generate_mode: Arc::new(RwLock::new(false)),
part_metadata: Arc::new(Mutex::new(HashMap::new())),
#[cfg(feature = "async")]
runtime: Arc::new(Mutex::new(
@@ -242,7 +286,6 @@ impl Mize {
name: &str,
handler: Box<dyn Fn(MizeMessage, &mut Mize) -> MizeResult<()> + Send + Sync>,
) -> MizeResult<()> {
println!("adding listener: {}", name);
let name = name.to_owned();
let mut listeners = self.listeners.lock().unwrap();
listeners.insert(name, Arc::new(handler));
@@ -260,7 +303,7 @@ impl Mize {
// end of platform specific init code
// load the modules, as specified in the parts config
// load the parts, as specified in the parts config
match self
.get_config("parts")
{
@@ -274,7 +317,6 @@ impl Mize {
}
}
Err(_err) => {
println!("hooooo");
// no parts to load
}
}
@@ -286,7 +328,6 @@ impl Mize {
pub fn with_config(config: ItemData) -> MizeResult<Mize> {
trace!("[ {} ] Instance::with_config()", "CALL".yellow());
trace!("config: {}", config);
println!("here ....");
let mut mize = Mize::empty()?;
@@ -354,7 +395,6 @@ impl Mize {
drop(msg_wait_inner);
let id = rx.recv()?;
println!("new_item namespace: {:?}", id.namespace());
return Ok(Item::new(id, self));
}
@@ -463,7 +503,7 @@ impl Mize {
}
pub fn fetch_part(&mut self, name: &str) -> MizeResult<String> {
// platform specific way to fetch a module
// platform specific way to fetch a part
crate::platform::fetch_part(self, name)
}
@@ -472,6 +512,98 @@ impl Mize {
crate::platform::load_part(self, name, Some(path))
}
// ===== generate mode accessors =====
/// Enable or disable generate mode. When enabled, part_smth functions record
/// metadata (methods, opts, natives) instead of performing their normal operations.
pub fn set_generate_mode(&self, enabled: bool) -> MizeResult<()> {
let mut mode = self.generate_mode.write().map_err(|_| mize_err!("generate_mode lock poisoned"))?;
*mode = enabled;
Ok(())
}
/// Check if generate mode is currently enabled.
pub fn is_generate_mode(&self) -> MizeResult<bool> {
let mode = self.generate_mode.read().map_err(|_| mize_err!("generate_mode lock poisoned"))?;
Ok(*mode)
}
/// Record an opt declaration while in generate mode.
pub fn record_opt(&self, name: &str, id: MizeId, default_val: Option<ItemData>) -> MizeResult<()> {
let cur_part = self.cur_eval_part.lock()?.clone()
.ok_or_else(|| mize_err!("no cur_eval_part set"))?;
let mut metadata_map = self.part_metadata.lock()?;
let metadata = metadata_map.entry(cur_part.clone()).or_insert_with(|| PartMetadata {
part_name: cur_part.dot(),
..Default::default()
});
metadata.opts.push(OptMetadata {
name: name.to_string(),
id,
default_val,
});
Ok(())
}
/// Record a method/effect/thread declaration while in generate mode.
pub fn record_method(&self, name: &str, id: MizeId, kind: &str) -> MizeResult<()> {
let cur_part = self.cur_eval_part.lock()?.clone()
.ok_or_else(|| mize_err!("no cur_eval_part set"))?;
let mut metadata_map = self.part_metadata.lock()?;
let metadata = metadata_map.entry(cur_part.clone()).or_insert_with(|| PartMetadata {
part_name: cur_part.dot(),
..Default::default()
});
let m = MethodMetadata {
name: name.to_string(),
id,
kind: kind.to_string(),
};
match kind {
"effect" => metadata.effects.push(m),
"thread" => metadata.threads.push(m),
_ => metadata.methods.push(m),
}
Ok(())
}
/// Record a native type inclusion while in generate mode.
pub fn record_native(&self, name: &str, type_name: &str, associated: Vec<String>, methods: Vec<String>) -> MizeResult<()> {
let cur_part = self.cur_eval_part.lock()?.clone()
.ok_or_else(|| mize_err!("no cur_eval_part set"))?;
let mut metadata_map = self.part_metadata.lock()?;
let metadata = metadata_map.entry(cur_part.clone()).or_insert_with(|| PartMetadata {
part_name: cur_part.dot(),
..Default::default()
});
metadata.natives.push(NativeMetadata {
name: name.to_string(),
type_name: type_name.to_string(),
associated,
methods,
});
Ok(())
}
/// Get recorded metadata for a specific part.
pub fn get_metadata(&self, part_id: &MizeId) -> MizeResult<Option<PartMetadata>> {
let metadata_map = self.part_metadata.lock()?;
Ok(metadata_map.get(part_id).cloned())
}
/// Get all recorded metadata (for all parts).
pub fn get_all_metadata(&self) -> MizeResult<Vec<PartMetadata>> {
let metadata_map = self.part_metadata.lock()?;
Ok(metadata_map.values().cloned().collect())
}
/// Clear all recorded metadata.
pub fn clear_metadata(&self) -> MizeResult<()> {
let mut metadata_map = self.part_metadata.lock()?;
metadata_map.clear();
Ok(())
}
pub fn namespace_from_string(&self, ns_str: String) -> MizeResult<Namespace> {
let namespace_pool_inner = self.namespace_pool.lock()?;
let namespace = Namespace(namespace_pool_inner.get(ns_str));
@@ -812,32 +944,65 @@ impl Mize {
pub fn get_config(&mut self, name: &str) -> MizeResult<ItemData> {
let mut config_opts = self.config_opts.lock().unwrap();
let opt = config_opts
.get_mut(&self.clone().id_new_dots(name)?)
.ok_or_else(|| mize_err!("Config opt for {name} not in config_opts"))?;
// Evaluate the fun if present
if let Some(fun) = &opt.fun {
let result = fun()?;
return Ok(ItemData::from(result));
let mut id;
let mut split = name.split(".");
if split.nth(0).unwrap() == "self" {
let cur_part = self.cur_eval_part.lock()?.clone().ok_or(mize_err!("get_config called but no curr eval part set"))?;
id = cur_part.clone();
for el in split {
id = id.join(el)?;
}
} else {
id = self.clone().id_new_dots(name)?;
}
// Return cached value
let mut opt = config_opts
.get_mut(&id)
.ok_or_else(|| mize_err!("Config opt for {name} not in config_opts"))?;
if let Some(val) = opt.val.clone() {
return Ok(val);
}
// in case it is not cached run the fun if it exists
if let Some(fun) = &opt.fun {
let res = fun()?;
opt.val = Some(res.clone());
return Ok(res);
}
Err(mize_err!("ConfigOpt {name} has no fun and no cached value"))
}
pub fn dyn_call(&mut self, part_id: MizeId, method: &str, args: ItemData) -> MizeResult<ItemData> {
let mut opts = self.config_opts.lock()?;
let part = opts.get_mut(&part_id).ok_or(mize_err!("part {} is not loaded", part_id.dot()))?;
let fun = part.val.ok_or(mize_err!("fun has no val"))?.native::<Fn(ItemData) -> MizeResult<ItemData>>()?;
println!("dyn_call ing from part, {:?}, {method}, {:?}", part_id, args);
fun(args)
debug!("dyn call {}.{} with args: {}", part_id, method, args);
let mut fun_values = self.fun_values.lock()?;
let id = part_id.clone().join(method)?;
//let part = opts.get_mut(&id).ok_or(mize_err!("part {} is not loaded", part_id.dot()))?;
let fun = fun_values.remove(&id).ok_or(mize_err!("fun value for {id} not found"))?;
drop(fun_values);
let calling_part = self.clone().cur_eval_part()?;
self.clone().set_cur_eval_part(Some(part_id));
let res = fun(args);
self.clone().set_cur_eval_part(calling_part);
let mut fun_values = self.fun_values.lock()?;
fun_values.insert(id, fun);
return res;
}
pub fn cur_eval_part(&mut self) -> MizeResult<Option<MizeId>> {
Ok(self.cur_eval_part.lock()?.clone())
}
pub fn set_cur_eval_part(&mut self, part: Option<MizeId>) -> MizeResult<()> {
let mut guard = self.cur_eval_part.lock()?;
*guard = part;
Ok(())
}
pub fn get_part_gen<T: MizePart + Send + Sync + 'static>(
&mut self,
name: &str,

View File

@@ -1,21 +1,21 @@
use crate::get_global_mize;
use crate::config::{ConfigOpt};
use crate::{config, get_global_mize};
use crate::config::{ConfigOpt, FromItemData, MizeFun};
use ciborium::into_writer;
use colored::Colorize;
use serde_json::from_reader;
use core::fmt;
use serde::Deserialize;
use serde::Serialize;
use std::any::Any;
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::MutexGuard;
use std::io;
use std::{boxed, io};
use std::fmt::Display;
use std::i128;
use tracing::{debug, instrument, trace, warn};
use crate::error::{IntoMizeResult, MizeError, MizeResult};
use crate::error::{IntoMizeResult, MizeError, MizeErrorWhichImplementsError, MizeResult};
use crate::id::MizeId;
use crate::instance::connection::value_raw_con_by_id;
use crate::instance::Mize;
@@ -56,6 +56,7 @@ impl Item<'_> {
.mres_msg(format!("failed to convert raw value from item '{}' into a utf-8 string. raw value was: '{:02X?}'", self.id(), raw))
}
pub fn as_data_full(&self) -> MizeResult<ItemData> {
let id = self.id();
@@ -228,7 +229,7 @@ impl ItemData {
*next_id += 1;
native_values.insert(id, Box::new(val));
let mut data = ItemData::new();
data.set_path("_native", id.to_string())?;
data.set_path("_native", id)?;
Ok(data)
}
@@ -238,6 +239,43 @@ impl ItemData {
_ => Err(mize_err!("this ItemData has no string value")),
}
}
//pub fn value_fun(&self) -> MizeResult<config::MizeFun> {
pub fn value_fun(&self) -> MizeResult<()> {
let mut mize = get_global_mize()?;
let mut fun_values = mize.fun_values.lock()?;
let fun_id = self.get_path("_func")?.value_u32()?;
//let fun = fun_values.remove(fun_id).ok_or(mize_err!("fun with id {fun_id} not in fun_values"))?;
// TODO: finish
Ok(())
}
pub fn call(&self, args: ItemData) -> MizeResult<ItemData> {
let mut mize = get_global_mize()?;
let mut fun_values = mize.fun_values.lock()?;
let fun_num = self.get_path("_fun_num")?.value_u32()?;
let fun_id = mize.clone().id_new_dots("mize.fun")?.join(format!("{fun_num}").as_str())?;
let fun = fun_values.remove(&fun_id).ok_or(mize_err!("fun with id {fun_id} not in fun_values"))?;
let res = fun(args)?;
return Ok(res);
}
pub fn fun(fun: MizeFun) -> MizeResult<ItemData> {
let mut mize = get_global_mize()?;
let mut fun_values = mize.fun_values.lock()?;
let mut next_fun_values_id = mize.next_fun_values_id.lock()?;
let fun_num = next_fun_values_id.clone();
*next_fun_values_id = *next_fun_values_id +1;
let fun_id = mize.clone().id_new_dots("mize.fun")?.join(format!("{fun_num}").as_str())?;
fun_values.insert(fun_id, fun);
let mut ret = ItemData::new();
ret.set_path("_fun_num", fun_num)?;
Ok(ret)
}
pub fn value_u32(&self) -> MizeResult<u32> {
Ok(u32::try_from(self.cbor().as_integer().ok_or("value_u32 but data wos not a cbor Integer")?)?)
}
pub fn value_bool(self) -> MizeResult<bool> {
match self.cbor() {
@@ -352,132 +390,503 @@ impl ItemData {
Ok(ItemData::from_cbor(value.clone()))
}
/// Try to get the native id from the `_native` field.
/// Returns `Some(id)` if the field exists and is a valid u32 string.
/// Returns `None` if the field doesn't exist or is not a valid id.
fn try_get_native_id(&self) -> Option<u32> {
let id_data = self.get_path("_native").ok()?;
if id_data.cbor().is_null() {
return None;
}
if let Some(integer) = id_data.cbor().as_integer() {
if let Ok(val) = u32::try_from(integer) {
return Some(val)
}
}
return None;
}
/// Get an immutable reference to the native value stored in this ItemData.
/// The native value must have been stored via from_native().
/// Returns a guard that holds the lock on the native_values registry.
///
/// If the ItemData has a `_native` field (stored via `from_native()`), returns a guard
/// that takes the value out of the global registry and provides an immutable reference to it.
///
/// Otherwise, for supported hardcoded types (String, bool, Vec<String>, Vec<u8>,
/// integer types, float types), constructs the value directly from the CBOR content,
/// stores it in the registry, and returns a guard to it.
///
/// The guard temporarily removes the value from the registry to allow the mutex lock to be
/// dropped, improving concurrency. The value is automatically reinserted when the guard is dropped.
pub fn native<T: Any + Send + Sync + 'static>(&self) -> MizeResult<NativeRefGuard<T>> {
let mize = get_global_mize()?;
let native_values = mize.native_values.lock().map_err(|_| mize_err!("Failed to lock native_values"))?;
let id_data = self.get_path("_native")
.map_err(|_| mize_err!("No _native field in ItemData"))?;
let id_str = id_data.value_string()
.map_err(|_| mize_err!("_native field is not a string"))?;
let id: u32 = id_str.parse()
.map_err(|_| mize_err!("_native field is not a valid u32: {}", id_str))?;
let boxed = native_values.get(&id)
let mut native_values = mize.native_values.lock().map_err(|_| mize_err!("Failed to lock native_values"))?;
let id = match self.try_get_native_id() {
Some(id) => id,
None => {
// No _native field, try to construct from CBOR
let value = try_construct_native::<T>(self.cbor())?
.ok_or_else(|| mize_err!(
"ItemData has no _native field and type {} is not supported for direct construction from CBOR. Value: {}",
std::any::type_name::<T>(),
self
))?;
// Store in registry and use the new id
let mut next_id = mize.next_native_value_id.lock()
.map_err(|_| mize_err!("Failed to lock next_native_value_id"))?;
let new_id = *next_id;
*next_id += 1;
native_values.insert(new_id, Box::new(value));
new_id
}
};
let boxed = native_values.remove(&id)
.ok_or(mize_err!("No native value with id {}", id))?;
// SAFETY: The reference is valid as long as the guard exists.
// The guard holds the MutexGuard which keeps the lock on native_values.
// The data in the HashMap is owned by the global Mize instance.
// We extract the raw pointer first, then move the guard.
let ref_t_ptr = boxed.downcast_ref::<T>()
.ok_or(mize_err!("Failed to downcast native value to {}", std::any::type_name::<T>()))? as *const T;
let guard = std::mem::ManuallyDrop::new(native_values);
let typed = boxed.downcast::<T>()
.map_err(|_| mize_err!("Failed to downcast native value to {}", std::any::type_name::<T>()))?;
// Drop the mutex guard before returning - the value is now owned by the guard
drop(native_values);
Ok(NativeRefGuard {
_guard: unsafe { std::mem::transmute(guard) },
value: unsafe { &*ref_t_ptr },
value: std::mem::ManuallyDrop::new(*typed),
id,
needs_reinsert: true,
})
}
/// Get an immutable reference to a native value of type `T`, using [`FromItemData`]
/// to construct it when it is not already stored in the registry as a `T`.
///
/// If the ItemData has a `_native` field (stored via `from_native()`) and the registry
/// value is a `T`, returns a guard to it (the value is temporarily removed from the
/// registry and reinserted when the guard is dropped).
///
/// Otherwise, constructs the value via `T::from_itemdata(self)`, stores it in the
/// registry, and returns a guard to it.
pub fn native_from<T: FromItemData + Any + Send + Sync + 'static>(&self) -> MizeResult<NativeRefGuard<T>> {
// Fast path: the registry already holds a T for this ItemData
if let Some(id) = self.try_get_native_id() {
let mize = get_global_mize()?;
let mut native_values = mize.native_values.lock()
.map_err(|_| mize_err!("Failed to lock native_values"))?;
if native_values.get(&id).map(|b| b.downcast_ref::<T>().is_some()).unwrap_or(false) {
let boxed = native_values.remove(&id)
.ok_or(mize_err!("No native value with id {}", id))?;
let typed = boxed.downcast::<T>()
.map_err(|_| mize_err!("Failed to downcast native value to {}", std::any::type_name::<T>()))?;
drop(native_values);
return Ok(NativeRefGuard {
value: std::mem::ManuallyDrop::new(*typed),
id,
needs_reinsert: true,
});
}
}
// Fallback: let T construct itself from this ItemData (without holding the registry lock)
let value = T::from_itemdata(self.clone())?;
// Store in registry and hand out a guard to it
let mize = get_global_mize()?;
let mut native_values = mize.native_values.lock()
.map_err(|_| mize_err!("Failed to lock native_values"))?;
let new_id = {
let mut next_id = mize.next_native_value_id.lock()
.map_err(|_| mize_err!("Failed to lock next_native_value_id"))?;
let new_id = *next_id;
*next_id += 1;
new_id
};
native_values.insert(new_id, Box::new(value));
let boxed = native_values.remove(&new_id)
.ok_or(mize_err!("No native value with id {}", new_id))?;
let typed = boxed.downcast::<T>()
.map_err(|_| mize_err!("Failed to downcast native value to {}", std::any::type_name::<T>()))?;
drop(native_values);
Ok(NativeRefGuard {
value: std::mem::ManuallyDrop::new(*typed),
id: new_id,
needs_reinsert: true,
})
}
/// Get a mutable reference to the native value stored in this ItemData.
/// The native value must have been stored via from_native().
/// Returns a guard that holds the lock on the native_values registry.
///
/// If the ItemData has a `_native` field (stored via `from_native()`), returns a guard
/// that takes the value out of the global registry and provides a mutable reference to it.
///
/// Otherwise, for supported hardcoded types (String, bool, Vec<String>, Vec<u8>,
/// integer types, float types), constructs the value directly from the CBOR content,
/// stores it in the registry, and returns a mutable guard to it.
///
/// The guard temporarily removes the value from the registry to allow the mutex lock to be
/// dropped, improving concurrency. The value is automatically reinserted when the guard is dropped.
pub fn native_mut<T: Any + Send + Sync + 'static>(&self) -> MizeResult<NativeMutRefGuard<T>> {
let mize = get_global_mize()?;
let mut native_values = mize.native_values.lock().map_err(|_| mize_err!("Failed to lock native_values"))?;
let id_data = self.get_path("_native")
.map_err(|_| mize_err!("No _native field in ItemData"))?;
let id_str = id_data.value_string()
.map_err(|_| mize_err!("_native field is not a string"))?;
let id: u32 = id_str.parse()
.map_err(|_| mize_err!("_native field is not a valid u32: {}", id_str))?;
let boxed = native_values.get_mut(&id)
.ok_or(mize_err!("No native value with id {}", id))?;
// SAFETY: The reference is valid as long as the guard exists.
// The guard holds the MutexGuard which keeps the lock on native_values.
// The data in the HashMap is owned by the global Mize instance.
// We extract the raw pointer first, then move the guard.
let ref_mut_t_ptr = boxed.downcast_mut::<T>()
.ok_or(mize_err!("Failed to downcast native value to {}", std::any::type_name::<T>()))? as *mut T;
let guard = std::mem::ManuallyDrop::new(native_values);
Ok(NativeMutRefGuard {
_guard: unsafe { std::mem::transmute(guard) },
value: unsafe { &mut *ref_mut_t_ptr },
})
}
/// Get a clone of the native value stored in this ItemData.
/// The native value must have been stored via from_native().
/// This is useful when you need to pass the value by ownership (e.g., as a function argument).
pub fn native_clone<T: Any + Send + Sync + Clone + 'static>(&self) -> MizeResult<T> {
let guard = self.native::<T>()?;
Ok(guard.clone())
}
/// Take the native value from this ItemData, removing it from the registry.
/// The native value must have been stored via from_native().
pub fn native_take<T: Any + Send + Sync + 'static>(&self) -> MizeResult<T> {
let mize = get_global_mize()?;
let mut native_values = mize.native_values.lock().map_err(|_| mize_err!("Failed to lock native_values"))?;
let id_data = self.get_path("_native")
.map_err(|_| mize_err!("No _native field in ItemData"))?;
let id_str = id_data.value_string()
.map_err(|_| mize_err!("_native field is not a string"))?;
let id: u32 = id_str.parse()
.map_err(|_| mize_err!("_native field is not a valid u32: {}", id_str))?;
let id = match self.try_get_native_id() {
Some(id) => id,
None => {
// No _native field, try to construct from CBOR
let value = try_construct_native::<T>(self.cbor())?
.ok_or_else(|| mize_err!(
"ItemData has no _native field and type {} is not supported for direct construction from CBOR. Value: {}",
std::any::type_name::<T>(),
self
))?;
// Store in registry and use the new id
let mut next_id = mize.next_native_value_id.lock()
.map_err(|_| mize_err!("Failed to lock next_native_value_id"))?;
let new_id = *next_id;
*next_id += 1;
native_values.insert(new_id, Box::new(value));
new_id
}
};
let boxed = native_values.remove(&id)
.ok_or(mize_err!("No native value with id {}", id))?;
let typed = boxed.downcast::<T>()
.map_err(|_| mize_err!("Failed to downcast native value to {}", std::any::type_name::<T>()))?;
Ok(*typed)
// Drop the mutex guard before returning - the value is now owned by the guard
drop(native_values);
Ok(NativeMutRefGuard {
value: std::mem::ManuallyDrop::new(*typed),
id,
needs_reinsert: true,
})
}
/// Get a mutable reference to a native value of type `T`, using [`FromItemData`]
/// to construct it when it is not already stored in the registry as a `T`.
///
/// If the ItemData has a `_native` field (stored via `from_native()`) and the registry
/// value is a `T`, returns a mutable guard to it (the value is temporarily removed
/// from the registry and reinserted when the guard is dropped).
///
/// Otherwise, constructs the value via `T::from_itemdata(self)`, stores it in the
/// registry, and returns a mutable guard to it.
pub fn native_mut_from<T: FromItemData + Any + Send + Sync + 'static>(&self) -> MizeResult<NativeMutRefGuard<T>> {
// Fast path: the registry already holds a T for this ItemData
if let Some(id) = self.try_get_native_id() {
let mize = get_global_mize()?;
let mut native_values = mize.native_values.lock()
.map_err(|_| mize_err!("Failed to lock native_values"))?;
if native_values.get(&id).map(|b| b.downcast_ref::<T>().is_some()).unwrap_or(false) {
let boxed = native_values.remove(&id)
.ok_or(mize_err!("No native value with id {}", id))?;
let typed = boxed.downcast::<T>()
.map_err(|_| mize_err!("Failed to downcast native value to {}", std::any::type_name::<T>()))?;
drop(native_values);
return Ok(NativeMutRefGuard {
value: std::mem::ManuallyDrop::new(*typed),
id,
needs_reinsert: true,
});
}
}
// Fallback: let T construct itself from this ItemData (without holding the registry lock)
let value = T::from_itemdata(self.clone())?;
// Store in registry and hand out a mutable guard to it
let mize = get_global_mize()?;
let mut native_values = mize.native_values.lock()
.map_err(|_| mize_err!("Failed to lock native_values"))?;
let new_id = {
let mut next_id = mize.next_native_value_id.lock()
.map_err(|_| mize_err!("Failed to lock next_native_value_id"))?;
let new_id = *next_id;
*next_id += 1;
new_id
};
native_values.insert(new_id, Box::new(value));
let boxed = native_values.remove(&new_id)
.ok_or(mize_err!("No native value with id {}", new_id))?;
let typed = boxed.downcast::<T>()
.map_err(|_| mize_err!("Failed to downcast native value to {}", std::any::type_name::<T>()))?;
drop(native_values);
Ok(NativeMutRefGuard {
value: std::mem::ManuallyDrop::new(*typed),
id: new_id,
needs_reinsert: true,
})
}
/// Get a clone of the native value stored in this ItemData.
///
/// If the ItemData has a `_native` field (stored via `from_native()`), clones the value
/// from the global registry.
///
/// Otherwise, for supported hardcoded types (String, bool, Vec<String>, Vec<u8>,
/// integer types, float types), constructs the value directly from the CBOR content.
///
/// This is useful when you need to pass the value by ownership (e.g., as a function argument).
pub fn native_clone<T: Any + Send + Sync + Clone + 'static>(&self) -> MizeResult<T> {
// Try registry first
if let Some(id) = self.try_get_native_id() {
let mize = get_global_mize()?;
let native_values = mize.native_values.lock()
.map_err(|_| mize_err!("Failed to lock native_values"))?;
let boxed = native_values.get(&id)
.ok_or(mize_err!("No native value with id {}", id))?;
let val = boxed.downcast_ref::<T>()
.ok_or(mize_err!("Failed to downcast native value to {}", std::any::type_name::<T>()))?;
return Ok(val.clone());
}
// Fallback: construct directly from CBOR
try_construct_native::<T>(self.cbor())?
.ok_or_else(|| mize_err!(
"ItemData has no _native field and type {} is not supported for direct construction from CBOR. Value: {}",
std::any::type_name::<T>(),
self
))
}
/// Take the native value from this ItemData, removing it from the registry.
///
/// If the ItemData has a `_native` field (stored via `from_native()`), removes and
/// returns the value from the global registry.
///
/// Otherwise, for supported hardcoded types (String, bool, Vec<String>, Vec<u8>,
/// integer types, float types), constructs the value directly from the CBOR content.
pub fn native_take<T: Any + Send + Sync + 'static>(&self) -> MizeResult<T> {
// Try registry first
if let Some(id) = self.try_get_native_id() {
let mize = get_global_mize()?;
let mut native_values = mize.native_values.lock()
.map_err(|_| mize_err!("Failed to lock native_values"))?;
let boxed = native_values.remove(&id)
.ok_or(mize_err!("No native value with id {}", id))?;
let typed = boxed.downcast::<T>()
.map_err(|_| mize_err!("Failed to downcast native value to {}", std::any::type_name::<T>()))?;
return Ok(*typed);
}
// Fallback: construct directly from CBOR
try_construct_native::<T>(self.cbor())?
.ok_or_else(|| mize_err!(
"ItemData has no _native field and type {} is not supported for direct construction from CBOR. Value: {}",
std::any::type_name::<T>(),
self
))
}
/// Take a native value of type `T` from this ItemData, using [`FromItemData`] as fallback.
///
/// If the ItemData has a `_native` field (stored via `from_native()`) and the registry
/// value is a `T`, removes and returns it from the global registry.
///
/// Otherwise, constructs and returns the value via `T::from_itemdata(self)`.
pub fn native_take_from<T: FromItemData + Any + Send + Sync + 'static>(&self) -> MizeResult<T> {
// Try registry first: only take the value out if it actually is a T
if let Some(id) = self.try_get_native_id() {
let mize = get_global_mize()?;
let mut native_values = mize.native_values.lock()
.map_err(|_| mize_err!("Failed to lock native_values"))?;
if native_values.get(&id).map(|b| b.downcast_ref::<T>().is_some()).unwrap_or(false) {
let boxed = native_values.remove(&id)
.ok_or(mize_err!("No native value with id {}", id))?;
let typed = boxed.downcast::<T>()
.map_err(|_| mize_err!("Failed to downcast native value to {}", std::any::type_name::<T>()))?;
return Ok(*typed);
}
}
// Fallback: let T construct itself from this ItemData
T::from_itemdata(self.clone())
}
}
/// Guard that holds a lock on the native_values registry and provides an immutable reference to a native value.
/// Try to construct a native value of type `T` from a `CborValue`.
///
/// Supports hardcoded types:
/// - `String` from `CborValue::Text`
/// - `bool` from `CborValue::Bool`
/// - `Vec<u8>` from `CborValue::Bytes`
/// - `Vec<String>` from `CborValue::Array` of `Text`
/// - Integer types (`i8`, `i16`, `i32`, `i64`, `i128`, `isize`,
/// `u8`, `u16`, `u32`, `u64`, `u128`, `usize`) from `CborValue::Integer`
/// - Float types (`f32`, `f64`) from `CborValue::Float` or `CborValue::Integer`
///
/// Returns `Ok(Some(value))` if `T` is a supported type and conversion succeeds.
/// Returns `Ok(None)` if `T` is not a supported hardcoded type.
/// Returns `Err` if `T` is supported but the CBOR value cannot be converted.
fn try_construct_native<T: Any + Send + Sync + 'static>(cbor: &CborValue) -> MizeResult<Option<T>> {
let tid = TypeId::of::<T>();
macro_rules! try_convert {
($target:ty, $conv:expr) => {
if tid == TypeId::of::<$target>() {
let val: $target = $conv?;
// SAFETY: TypeId verified that $target == T, so the transmute is sound.
return Ok(Some(*unsafe {
std::mem::transmute::<Box<$target>, Box<T>>(Box::new(val))
}));
}
};
}
// String from Text
try_convert!(String,
cbor.as_text()
.map(|s| s.to_owned())
.ok_or_else(|| mize_err!("ItemData is not a Text value, cannot construct String"))
);
// bool from Bool
try_convert!(bool,
cbor.as_bool()
.ok_or_else(|| mize_err!("ItemData is not a Bool value, cannot construct bool"))
);
// Vec<u8> from Bytes
try_convert!(Vec<u8>,
cbor.as_bytes()
.map(|b| b.clone())
.ok_or_else(|| mize_err!("ItemData is not a Bytes value, cannot construct Vec<u8>"))
);
// Vec<String> from Array of Text
try_convert!(Vec<String>, {
let arr = cbor.as_array()
.ok_or_else(|| mize_err!("ItemData is not an Array value, cannot construct Vec<String>"))?;
arr.iter()
.map(|v| {
v.as_text()
.map(|s| s.to_owned())
.ok_or_else(|| mize_err!("Array element is not a Text value, cannot construct Vec<String>"))
})
.collect::<MizeResult<Vec<String>>>()
});
// Integer types from Integer
macro_rules! try_int {
($target:ty) => {
try_convert!($target, {
let int = cbor.as_integer()
.ok_or_else(|| mize_err!("ItemData is not an Integer value, cannot construct {}", std::any::type_name::<$target>()))?;
let i128_val: i128 = int.into();
<$target>::try_from(i128_val)
.map_err(|_| mize_err!("Integer value {} does not fit in {}", i128_val, std::any::type_name::<$target>()))
});
};
}
try_int!(i8);
try_int!(i16);
try_int!(i32);
try_int!(i64);
try_int!(i128);
try_int!(isize);
try_int!(u8);
try_int!(u16);
try_int!(u32);
try_int!(u64);
try_int!(u128);
try_int!(usize);
// Float types from Float or Integer
macro_rules! try_float {
($target:ty) => {
try_convert!($target, {
match cbor {
CborValue::Float(f) => Ok(*f as $target),
CborValue::Integer(i) => {
let i128_val: i128 = (*i).into();
Ok(i128_val as $target)
}
_ => Err(mize_err!(
"ItemData is neither Float nor Integer, cannot construct {}",
std::any::type_name::<$target>()
)),
}
});
};
}
try_float!(f32);
try_float!(f64);
// Type not supported for direct construction
Ok(None)
}
/// Guard that holds a native value taken from the registry.
///
/// The value is temporarily removed from the registry to allow the mutex lock to be dropped.
/// When the guard is dropped, the value is automatically reinserted into the registry.
pub struct NativeRefGuard<T: Any + Send + Sync + 'static> {
_guard: MutexGuard<'static, HashMap<u32, Box<dyn Any + Send + Sync>>>,
value: &'static T,
value: std::mem::ManuallyDrop<T>,
id: u32,
needs_reinsert: bool,
}
impl<T: Any + Send + Sync + 'static> std::ops::Deref for NativeRefGuard<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.value
&*self.value
}
}
/// Guard that holds a lock on the native_values registry and provides a mutable reference to a native value.
impl<T: Any + Send + Sync + 'static> Drop for NativeRefGuard<T> {
fn drop(&mut self) {
if self.needs_reinsert {
let mize = get_global_mize().expect("Failed to get global mize in NativeRefGuard drop");
let mut native_values = mize.native_values.lock().expect("Failed to lock native_values in NativeRefGuard drop");
let value = unsafe { std::mem::ManuallyDrop::take(&mut self.value) };
native_values.insert(self.id, Box::new(value));
} else {
unsafe { std::mem::ManuallyDrop::drop(&mut self.value) };
}
}
}
/// Guard that holds a mutable native value taken from the registry.
///
/// The value is temporarily removed from the registry to allow the mutex lock to be dropped.
/// When the guard is dropped, the value is automatically reinserted into the registry.
pub struct NativeMutRefGuard<T: Any + Send + Sync + 'static> {
_guard: MutexGuard<'static, HashMap<u32, Box<dyn Any + Send + Sync>>>,
value: &'static mut T,
value: std::mem::ManuallyDrop<T>,
id: u32,
needs_reinsert: bool,
}
impl<T: Any + Send + Sync + 'static> std::ops::Deref for NativeMutRefGuard<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.value
&*self.value
}
}
impl<T: Any + Send + Sync + 'static> std::ops::DerefMut for NativeMutRefGuard<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.value
&mut *self.value
}
}
impl<T: Any + Send + Sync + 'static> Drop for NativeMutRefGuard<T> {
fn drop(&mut self) {
if self.needs_reinsert {
let mize = get_global_mize().expect("Failed to get global mize in NativeMutRefGuard drop");
let mut native_values = mize.native_values.lock().expect("Failed to lock native_values in NativeMutRefGuard drop");
let value = unsafe { std::mem::ManuallyDrop::take(&mut self.value) };
native_values.insert(self.id, Box::new(value));
} else {
unsafe { std::mem::ManuallyDrop::drop(&mut self.value) };
}
}
}
@@ -561,8 +970,6 @@ pub fn data_from_string(data_string: String) -> MizeResult<ItemData> {
pub fn item_data_get_path(data: &CborValue, path: Vec<String>) -> MizeResult<&CborValue> {
let mut path_iter = path.clone().into_iter();
//println!("data: {:?}", data);
//println!("path: {:?}", path);
let path_el = match path_iter.nth(0) {
Some(val) => val,
@@ -573,8 +980,6 @@ pub fn item_data_get_path(data: &CborValue, path: Vec<String>) -> MizeResult<&Cb
match data {
CborValue::Map(map) => {
for (key, val) in map {
//println!("key: {:?}", key);
//println!("path_el: {}", path_el);
if let CborValue::Text(key_str) = key {
if key_str == &path_el {
sub_data = val;
@@ -635,12 +1040,6 @@ pub fn item_data_set_path(
//trace!("[ {} ] path: {:?}", "ARG".yellow(), path);
//trace!("[ {} ] data_to_set: {}", "ARG".yellow(), data_to_set.clone().into_item_data());
//test_println!("item_data_set_path ###########################");
//test_println!("data: {}", ItemData::from_cbor(data.clone()));
//test_println!("path: {:?}", path);
//test_println!("data_to_set: {}", ItemData::from_cbor(data_to_set.clone()));
//test_println!("###########################");
let mut path_iter = path.clone().into_iter();
let path_el = match path_iter.nth(0) {
Some(val) => val,
@@ -850,6 +1249,11 @@ impl IntoPath for &str {
self.to_owned().split("/").map(|v| v.to_owned()).collect()
}
}
impl IntoPath for usize {
fn into_path(self) -> Vec<String> {
vec![format!("{}", self)]
}
}
impl IntoPath for &[&str] {
fn into_path(self) -> Vec<String> {
@@ -984,3 +1388,281 @@ pub fn set_config_from_data(mize: &mut Mize, mut data: ItemData) -> MizeResult<(
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::set_global_mize;
use crate::instance::Mize;
use std::sync::{Mutex, MutexGuard};
/// Mutex to serialize tests that use the global Mize instance.
static TEST_MUTEX: Mutex<()> = Mutex::new(());
fn setup() -> MizeResult<MutexGuard<'static, ()>> {
let guard = TEST_MUTEX.lock().map_err(|_| mize_err!("Failed to lock test mutex"))?;
let mize = Mize::empty()?;
set_global_mize(mize)?;
Ok(guard)
}
#[test]
fn test_native_clone_string_from_cbor() -> MizeResult<()> {
let _guard = setup()?;
let data = ItemData::from_string("hello world");
let s: String = data.native_clone()?;
assert_eq!(s, "hello world");
Ok(())
}
#[test]
fn test_native_take_string_from_cbor() -> MizeResult<()> {
let _guard = setup()?;
let data = ItemData::from_string("take me");
let s: String = data.native_take()?;
assert_eq!(s, "take me");
Ok(())
}
#[test]
fn test_native_string_from_cbor() -> MizeResult<()> {
let _guard = setup()?;
let data = ItemData::from_string("guard test");
let guard = data.native::<String>()?;
assert_eq!(&*guard, "guard test");
Ok(())
}
#[test]
fn test_native_mut_string_from_cbor() -> MizeResult<()> {
let _guard = setup()?;
let data = ItemData::from_string("mutable");
{
let mut guard = data.native_mut::<String>()?;
guard.push_str(" updated");
assert_eq!(&*guard, "mutable updated");
}
// Note: each call to native/native_mut on an ItemData without _native
// constructs a fresh value from the CBOR content. The ItemData itself
// is not modified, so the original CBOR value is used each time.
let guard2 = data.native::<String>()?;
assert_eq!(&*guard2, "mutable");
Ok(())
}
#[test]
fn test_native_clone_bool_from_cbor() -> MizeResult<()> {
let _guard = setup()?;
let data = ItemData::from_cbor(CborValue::Bool(true));
let b: bool = data.native_clone()?;
assert_eq!(b, true);
Ok(())
}
#[test]
fn test_native_clone_i32_from_cbor() -> MizeResult<()> {
let _guard = setup()?;
let data = ItemData::from_cbor(CborValue::Integer(42.into()));
let i: i32 = data.native_clone()?;
assert_eq!(i, 42);
Ok(())
}
#[test]
fn test_native_clone_u64_from_cbor() -> MizeResult<()> {
let _guard = setup()?;
let data = ItemData::from_cbor(CborValue::Integer(123456789u64.into()));
let u: u64 = data.native_clone()?;
assert_eq!(u, 123456789);
Ok(())
}
#[test]
fn test_native_clone_i128_from_cbor() -> MizeResult<()> {
let _guard = setup()?;
let data = ItemData::from_cbor(CborValue::from(9999999999i128));
let i: i128 = data.native_clone()?;
assert_eq!(i, 9999999999);
Ok(())
}
#[test]
fn test_native_clone_f64_from_cbor() -> MizeResult<()> {
let _guard = setup()?;
let data = ItemData::from_cbor(CborValue::Float(3.14159));
let f: f64 = data.native_clone()?;
assert!((f - 3.14159).abs() < 0.00001);
Ok(())
}
#[test]
fn test_native_clone_f64_from_integer() -> MizeResult<()> {
let _guard = setup()?;
let data = ItemData::from_cbor(CborValue::Integer(42.into()));
let f: f64 = data.native_clone()?;
assert_eq!(f, 42.0);
Ok(())
}
#[test]
fn test_native_clone_vec_string_from_cbor() -> MizeResult<()> {
let _guard = setup()?;
let data = ItemData::from_cbor(CborValue::Array(vec![
CborValue::Text("a".to_string()),
CborValue::Text("b".to_string()),
CborValue::Text("c".to_string()),
]));
let v: Vec<String> = data.native_clone()?;
assert_eq!(v, vec!["a".to_string(), "b".to_string(), "c".to_string()]);
Ok(())
}
#[test]
fn test_native_clone_vec_u8_from_cbor() -> MizeResult<()> {
let _guard = setup()?;
let data = ItemData::from_cbor(CborValue::Bytes(vec![1, 2, 3, 4, 5]));
let v: Vec<u8> = data.native_clone()?;
assert_eq!(v, vec![1, 2, 3, 4, 5]);
Ok(())
}
#[test]
fn test_native_clone_usize_from_cbor() -> MizeResult<()> {
let _guard = setup()?;
let data = ItemData::from_cbor(CborValue::Integer(99.into()));
let u: usize = data.native_clone()?;
assert_eq!(u, 99);
Ok(())
}
#[test]
fn test_native_registry_still_works() -> MizeResult<()> {
let _guard = setup()?;
// Store a custom type in the registry
let data = ItemData::from_native("registry value".to_string())?;
let s: String = data.native_clone()?;
assert_eq!(s, "registry value");
Ok(())
}
#[test]
fn test_native_take_registry() -> MizeResult<()> {
let _guard = setup()?;
let data = ItemData::from_native(777i32)?;
let i: i32 = data.native_take()?;
assert_eq!(i, 777);
// Second take should fail (already removed from registry)
let result: MizeResult<i32> = data.native_take();
assert!(result.is_err());
Ok(())
}
struct TestNative {
value: i32,
}
impl FromItemData for TestNative {
fn from_itemdata(data: ItemData) -> MizeResult<Self> {
let value = data.get_path("value")?.value_u32()? as i32;
Ok(TestNative { value })
}
}
struct IdWrapper {
native_id: u32,
}
impl FromItemData for IdWrapper {
fn from_itemdata(data: ItemData) -> MizeResult<Self> {
Ok(IdWrapper { native_id: data.get_path("_native")?.value_u32()? })
}
}
#[test]
fn test_native_take_from_registry() -> MizeResult<()> {
let _guard = setup()?;
let data = ItemData::from_native(TestNative { value: 5 })?;
let v: TestNative = data.native_take_from()?;
assert_eq!(v.value, 5);
Ok(())
}
#[test]
fn test_native_take_from_itemdata() -> MizeResult<()> {
let _guard = setup()?;
let mut data = ItemData::new();
data.set_path("value", 42u32)?;
let v: TestNative = data.native_take_from()?;
assert_eq!(v.value, 42);
Ok(())
}
#[test]
fn test_native_take_from_wrapper_keeps_registry_value() -> MizeResult<()> {
let _guard = setup()?;
// registry holds a u32, but we take an IdWrapper which builds itself from the _native field
let data = ItemData::from_native(777u32)?;
let expected_id = data.get_path("_native")?.value_u32()?;
let w: IdWrapper = data.native_take_from()?;
assert_eq!(w.native_id, expected_id);
// the u32 was not consumed and can still be taken
let v: u32 = data.native_take()?;
assert_eq!(v, 777);
Ok(())
}
#[test]
fn test_native_from_registry() -> MizeResult<()> {
let _guard = setup()?;
let data = ItemData::from_native(TestNative { value: 9 })?;
let guard = data.native_from::<TestNative>()?;
assert_eq!((*guard).value, 9);
Ok(())
}
#[test]
fn test_native_mut_from_itemdata() -> MizeResult<()> {
let _guard = setup()?;
let mut data = ItemData::new();
data.set_path("value", 1u32)?;
{
let mut guard = data.native_mut_from::<TestNative>()?;
(*guard).value += 10;
assert_eq!((*guard).value, 11);
}
// a fresh guard is constructed from the CBOR content again
let guard2 = data.native_from::<TestNative>()?;
assert_eq!((*guard2).value, 1);
Ok(())
}
#[test]
fn test_native_clone_unsupported_type_fails() -> MizeResult<()> {
let _guard = setup()?;
// HashMap is not a supported hardcoded type
let data = ItemData::from_string("test");
let result: MizeResult<std::collections::HashMap<String, String>> = data.native_clone();
assert!(result.is_err());
Ok(())
}
#[test]
fn test_native_clone_wrong_type_fails() -> MizeResult<()> {
let _guard = setup()?;
// ItemData has Text, but we ask for bool
let data = ItemData::from_string("not a bool");
let result: MizeResult<bool> = data.native_clone();
assert!(result.is_err());
Ok(())
}
#[test]
fn test_native_clone_integer_overflow_fails() -> MizeResult<()> {
let _guard = setup()?;
// i128 value too large for u8
let data = ItemData::from_cbor(CborValue::Integer(300.into()));
let result: MizeResult<u8> = data.native_clone();
assert!(result.is_err());
Ok(())
}
}

View File

@@ -79,6 +79,7 @@ pub mod prelude {
pub use error::MizeError;
pub use crate::mize_err;
pub use error::MizeResult;
pub use error::IntoMizeResult;
pub use item::{Item, ItemData, IntoItemData};
pub use config::*;
pub use api::*;
@@ -107,7 +108,7 @@ pub use error::MizeError;
pub use error::MizeResult;
pub use instance::Mize;
pub use instance::{
MizePart,
MizePart, PartMetadata, OptMetadata, MethodMetadata, NativeMetadata,
};
pub use mize_macros::*;
use std::path::PathBuf;

View File

@@ -33,7 +33,7 @@ use crate::MizeResult;
static DEFAULT_LOG_LEVEL: LevelFilter = LevelFilter::WARN;
pub fn init_logger(cli_matches: &ArgMatches) {
println!("init_logger called");
trace!("init_logger called");
// to save log messages, before the logger is setup
let mut log_messages: Vec<(Level, String)> = Vec::new();
@@ -69,7 +69,7 @@ pub fn init_logger(cli_matches: &ArgMatches) {
// check env LOG
let mut val_mize_log = level_from_env("LOG", log_level);
if let Some(level_mize_log) = val_mize_log.0 {
println!("set log level from the Variable LOG to {}", level_mize_log);
debug!("set log level from the Variable LOG to {}", level_mize_log);
log_level = level_mize_log;
log_messages.push((
Level::TRACE,
@@ -172,7 +172,7 @@ pub fn init_logger(cli_matches: &ArgMatches) {
//MinimalTracer::register();
println!("log_level: {}", log_level);
debug!("log_level: {}", log_level);
let field_formatter = tracing_subscriber::fmt::format::debug_fn(|writer, field, value| {
if field.name() == "message" {
write!(writer, "{:?}", value)?;
@@ -207,7 +207,7 @@ fn level_from_env(
) -> (Option<LevelFilter>, Vec<(Level, String)>) {
let mut log_messages: Vec<(Level, String)> = Vec::new();
let var_value = env::var(var_name);
println!("checking env var: {}={:?}", var_name, var_value);
debug!("checking env var: {}={:?}", var_name, var_value);
match var_value {
Ok(val) => {

View File

@@ -173,8 +173,8 @@ pub fn get_part_hash(
) -> MizeResult<(String, String)> {
let mut selector_data = mize.get("self/config/selector")?.as_data_full()?;
// handle the case, when we want a module for not the system we are running on...
println!("get_module_hash: name: {}", name);
// handle the case, when we want a part for not the system we are running on...
debug!("get_part_hash: name: {}", name);
let mut name_parts = name.split(".");
if name_parts
.next()
@@ -185,7 +185,7 @@ pub fn get_part_hash(
.next()
.ok_or(mize_err!("no 1th element in the modName"))?;
let mod_name: String = name_parts.collect();
println!("get_module_hash: mod_name: {}", mod_name);
println!("get_part_hash: mod_name: {}", mod_name);
if mod_name == "" {
return Err(mize_err!("no 2nd element in the modName"));
}
@@ -317,12 +317,9 @@ pub fn load_part(mize: &mut Mize, part_name: &str, path: Option<String>) -> Mize
unsafe extern "C" fn() -> MizeResult<()>,
> = unsafe { lib.get(format!("part_{}", part_name).as_bytes())? };
let mut cur_eval_part = mize.cur_eval_part.lock()?;
*cur_eval_part = Some(mize.clone().id_new_dots(part_name)?);
println!("running part: {}", cur_eval_part.clone().unwrap());
drop(cur_eval_part);
mize.clone().set_cur_eval_part(Some(mize.clone().id_new_dots(part_name)?))?;
unsafe { func()? };
mize.clone().set_cur_eval_part(None)?;
let mut cur_eval_part = mize.cur_eval_part.lock()?;
*cur_eval_part = None;