dyn calling works + vibed dioxus ui components + some mo provision prototyping
This commit is contained in:
886
core/src/item.rs
886
core/src/item.rs
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user