changes saved in my editor

This commit is contained in:
Sebastian Moser
2026-10-22 18:25:57 +02:00
parent 105eca862d
commit 679f5e51e5
10 changed files with 3503 additions and 2 deletions

128
core/src/config.rs Normal file
View File

@@ -0,0 +1,128 @@
use std::any::Any;
use crate::MizeResult;
use crate::c_api::get_global_mize;
use crate::id::MizeId;
use crate::item::{IntoItemData, ItemData};
use crate::Mize;
type ConfigFun = Box<dyn Fn() -> MizeResult<ItemData> + Send + Sync + 'static>;
pub struct ConfigOpt {
pub name: String,
pub val: Option<ItemData>,
pub fun: Option<ConfigFun>,
pub deps: Vec<String>,
}
#[derive(Clone)]
#[repr(C)]
pub struct ConfigOptId {
pub id: MizeId,
}
impl ConfigOptId {
pub fn default_val(self, val: ItemData) -> Self {
let mize = get_global_mize().unwrap();
let mut config_opts = mize.config_opts.lock().unwrap();
let opt = config_opts.get_mut(&self.id).unwrap();
opt.val = Some(val);
self.clone()
}
pub fn fun(self, fun: ConfigFun) -> Self {
let mize = get_global_mize().unwrap();
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 {
self.clone()
}
}
//pub trait ToItemDataHelper {
//fn into_item_data(self) -> ItemData;
//}
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();
let opt = ConfigOpt {
name: name.to_owned(),
val: None,
fun: None,
deps: Vec::new(),
};
opts.insert(id.clone(), opt);
ConfigOptId { id }
}
pub fn method<F: Fn(ItemData) -> MizeResult<ItemData>>(_name: &str, _fun: F) {
}
pub fn effect<F: Fn(ItemData) -> MizeResult<ItemData>>(_name: &str, _fun: F) {
}
pub fn thread<F: Fn(ItemData) -> MizeResult<ItemData>>(_name: &str, _fun: F) {
}
type CFun = unsafe extern "C" fn (*mut ItemData) -> *mut ItemData;
pub mod c_api {
use std::ffi::c_char;
use crate::config::{CFun, ConfigOptId};
use std::ffi::CStr;
pub extern "C" fn opt(name: *const c_char) -> ConfigOptId {
unsafe {
let c_str = CStr::from_ptr(name);
let string = c_str.to_string_lossy();
super::opt(string.as_str())
}
}
pub extern "C" fn method(name: *const c_char, c_fun: CFun) {
unsafe {
let c_str = CStr::from_ptr(name);
let string = c_str.to_string_lossy();
super::method(string.as_str(), move |args| {
let raw_ptr = Box::into_raw(Box::new(args));
let ret_ptr = c_fun(raw_ptr);
let data = *Box::from_raw(ret_ptr);
Ok(data)
})
}
}
pub extern "C" fn effect(name: *const c_char, c_fun: CFun) {
unsafe {
let c_str = CStr::from_ptr(name);
let string = c_str.to_string_lossy();
super::effect(string.as_str(), move |args| {
let raw_ptr = Box::into_raw(Box::new(args));
let ret_ptr = c_fun(raw_ptr);
let data = *Box::from_raw(ret_ptr);
Ok(data)
})
}
}
pub extern "C" fn thread(name: *const c_char, c_fun: CFun) {
unsafe {
let c_str = CStr::from_ptr(name);
let string = c_str.to_string_lossy();
super::thread(string.as_str(), move |args| {
let raw_ptr = Box::into_raw(Box::new(args));
let ret_ptr = c_fun(raw_ptr);
let data = *Box::from_raw(ret_ptr);
Ok(data)
})
}
}
}

203
core/src/id.rs Normal file
View File

@@ -0,0 +1,203 @@
use core::fmt;
use interner::{
shared::{SharedString, SharedVecString},
};
use std::hash::Hash;
use crate::{c_api::get_global_mize, error::MizeResult};
use crate::{instance::Mize, mize_err};
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct MizeId {
pub path: SharedVecString,
pub namespace: Namespace,
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct Namespace(pub SharedString);
impl Namespace {
pub fn as_string(&self) -> SharedString {
self.0.clone()
}
pub fn as_real_string(&self) -> String {
let a = self.as_string();
let b = a.to_string();
return b;
}
}
pub trait IntoMizeId {
fn to_mize_id(self, instance: &Mize) -> MizeResult<MizeId>;
}
impl MizeId {
pub fn join(&mut self, to_add: &str) -> MizeResult<MizeId> {
let path = self.path();
let mut new_path = Vec::new();
for el in path.iter() {
new_path.push(el.to_owned());
}
new_path.push(to_add.to_owned());
let mize = get_global_mize()?;
Ok(new_path.to_mize_id(&mize)?)
}
pub fn store_part(&self) -> &str {
self.path
.iter()
.nth(0)
.expect("an empty MizeId found, that should absolutely not be possible!!!!")
.as_str()
}
pub fn nth_part(&self, n: usize) -> MizeResult<&str> {
match self.path.iter().nth(n) {
Some(val) => Ok(val.as_str()),
None => Err(mize_err!("id does not have {} parts", n)),
}
}
pub fn path(&self) -> SharedVecString {
self.path.clone()
}
pub fn after_store_part(&self) -> Vec<String> {
let tmp = self.path();
let mut path_iter = <Box<[std::string::String]> as Clone>::clone(&tmp).into_iter();
path_iter.next();
let new_path: Vec<String> = path_iter.map(|v| v.to_owned()).collect();
return new_path;
}
pub fn namespace(&self) -> Namespace {
self.namespace.clone()
}
pub fn namespace_str(&self) -> &str {
&self.namespace.0
}
}
impl fmt::Display for MizeId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.path.join("/"))
}
}
impl IntoMizeId for &str {
fn to_mize_id(self, instance: &Mize) -> MizeResult<MizeId> {
instance.id_from_string(self.to_owned())
}
}
impl IntoMizeId for String {
fn to_mize_id(self, instance: &Mize) -> MizeResult<MizeId> {
instance.id_from_string(self)
}
}
impl IntoMizeId for &String {
fn to_mize_id(self, instance: &Mize) -> MizeResult<MizeId> {
instance.id_from_string(self.to_owned())
}
}
impl IntoMizeId for Vec<String> {
fn to_mize_id(self, instance: &Mize) -> MizeResult<MizeId> {
instance.id_from_vec_string(self)
}
}
impl IntoMizeId for Vec<&String> {
fn to_mize_id(self, instance: &Mize) -> MizeResult<MizeId> {
let owned = self
.into_iter()
.map(|s| s.to_owned())
.collect::<Vec<String>>();
instance.id_from_vec_string(owned)
}
}
impl IntoMizeId for &[&str] {
fn to_mize_id(self, instance: &Mize) -> MizeResult<MizeId> {
let mut vec: Vec<String> = Vec::new();
for i in self {
vec.push((*i).to_owned())
}
instance.id_from_vec_string(vec)
}
}
impl IntoMizeId for Vec<&str> {
fn to_mize_id(self, instance: &Mize) -> MizeResult<MizeId> {
let mut vec: Vec<String> = Vec::new();
for i in self {
vec.push((*i).to_owned())
}
instance.id_from_vec_string(vec)
}
}
impl IntoMizeId for MizeId {
fn to_mize_id(self, _instance: &Mize) -> MizeResult<MizeId> {
Ok(self)
}
}
// would only work with a global id_pool
// for now use Instance::new_id()
//impl<T: Into<String>> From<T> for MizeId {
//fn from(value: T) -> Self {
//let string = value.into();
//let path: Vec<String> = string.split("/").map(|v| v.to_owned()).collect();
//return MizeId { MIZE_ID_POOL.get(path) }
//}
//}
#[cfg(test)]
mod test {
use crate::instance::Mize;
use super::*;
#[test]
fn test_id() -> MizeResult<()> {
let instance = Mize::empty()?;
let id_one = instance.new_id("0/config/hi")?;
assert_eq!(instance.get_namespace()?, id_one.namespace());
assert_eq!("0", id_one.store_part());
assert_eq!(
vec!["config".to_owned(), "hi".to_owned()],
id_one.after_store_part()
);
let id_two = instance.new_id("test.ns:hello/config/hi")?;
assert_eq!(
instance.namespace_from_string("test.ns".to_owned())?,
id_two.namespace()
);
assert_eq!("hello", id_two.store_part());
assert_eq!(
vec!["config".to_owned(), "hi".to_owned()],
id_one.after_store_part()
);
let id_three = instance.new_id("0/")?;
assert_eq!(instance.get_namespace()?, id_three.namespace());
assert_eq!("0", id_three.store_part());
assert_eq!(Vec::<String>::new(), id_three.after_store_part());
let id_four = instance.new_id("test.ns:0/")?;
assert_eq!(
instance.namespace_from_string("test.ns".to_owned())?,
id_four.namespace()
);
assert_eq!("0", id_four.store_part());
assert_eq!(Vec::<String>::new(), id_four.after_store_part());
let id_fife = instance.new_id("0")?;
assert_eq!(instance.get_namespace()?, id_fife.namespace());
assert_eq!("0", id_fife.store_part());
assert_eq!(Vec::<String>::new(), id_fife.after_store_part());
//TODO: add the parsing form mize:// urls....
Ok(())
}
}

869
core/src/instance/mod.rs Normal file
View File

@@ -0,0 +1,869 @@
use async_trait::async_trait;
use colored::Colorize;
use core::fmt;
use std::time::Duration;
use flume::{bounded, unbounded, Sender};
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::{thread, vec};
use tracing::{debug, info, trace};
use crate::c_api::set_global_mize;
use crate::config::{ConfigOpt, ConfigOptId};
use crate::error::{MizeError, MizeResult};
use crate::id::{IntoMizeId, MizeId, Namespace};
use crate::instance::store::Store;
use crate::instance::subscription::Subscription;
use crate::instance::updater::Operation;
use crate::instance::updater::updater_thread;
use crate::item::{Item, ItemData};
use crate::memstore::MemStore;
use crate::mize_err;
use crate::proto::MizeMessage;
use crate::error::IntoMizeResult;
use self::connection::{ConnListener, Connection};
use self::updater::handle_operation;
#[cfg(feature = "async")]
use tokio::runtime::Handle;
#[cfg(feature = "async")]
use tokio::runtime::Runtime;
use core::future::Future;
use std::thread::JoinHandle;
pub mod connection;
pub mod msg_thread;
pub mod store;
pub mod subscription;
pub mod updater;
#[cfg(test)]
mod tests;
static MSG_CHANNEL_SIZE: usize = 200;
static BUILD_TIME_CONFIG: &str = include_str!(std::env!("MIZE_BUILD_CONFIG"));
/// The Instance type is the heart of the mize system
#[derive(Clone)]
pub struct Mize {
// a bit a lot of Mutexes isn't it???
pub(crate) store: Arc<Mutex<Box<dyn Store>>>,
connections: Arc<Mutex<Vec<Connection>>>,
next_con_id: Arc<Mutex<u64>>,
subs: Arc<Mutex<HashMap<MizeId, Vec<Subscription>>>>,
pub(crate) id_pool: Arc<Mutex<VecStringPool>>,
pub(crate) namespace_pool: Arc<Mutex<StringPool>>,
pub(crate) listeners: Arc<
Mutex<HashMap<String, Arc<dyn Fn(MizeMessage, &mut Mize) -> MizeResult<()> + Send + Sync>>>,
>,
pub parts: Arc<Mutex<HashMap<MizeId, Option<Box<dyn MizePart + Sync + Send>>>>>,
//pub(crate) platform: Arc<Mutex<Box<dyn MizePlatform>>>, // should we do that instead of all the #cfg(faturs=targt-smth) in lib.rs
part_names: Arc<Mutex<Vec<&'static str>>>, // exists, so that we know the ordering in which parts were loaded
pub(crate) config_opts: Arc<Mutex<HashMap<MizeId, ConfigOpt>>>,
// the namespace the instance operates in
pub(crate) namespace: Arc<Mutex<Namespace>>,
// the namespace of the instance itself
// TODO: set to a random uuid
pub(crate) self_namespace: Arc<Mutex<Namespace>>,
pub(crate) op_tx: Sender<Operation>,
threads: Arc<Mutex<Vec<(u32, String, Option<JoinHandle<MizeResult<()>>>)>>>,
next_thread_id: Arc<Mutex<u32>>,
give_msg_wait: Arc<Mutex<HashMap<MizeId, Vec<Sender<ItemData>>>>>,
create_msg_wait: Arc<Mutex<Option<Sender<MizeId>>>>,
pub cur_eval_part: Arc<Mutex<Option<MizeId>>>,
pub native_values: Arc<Mutex<HashMap<u32, Box<dyn Any + Send + Sync>>>>,
pub next_native_value_id: Arc<Mutex<u32>>,
#[cfg(feature = "async")]
pub runtime: Arc<Mutex<Runtime>>,
}
pub struct InstanceRef {
inner: Arc<Mutex<Mize>>,
}
pub struct PartRef {
id: MizeId,
}
impl PartRef {
pub fn into_any(self) -> Box<dyn Any> {
Box::new(self)
}
}
impl MizePart for PartRef {
fn id(&self) -> MizeId {
self.id.clone()
}
}
pub trait PartToNative {
fn from_mize_id(id: MizeId) -> Self;
}
pub struct InstanceAsync {
inner: Arc<Mutex<Mize>>,
}
pub trait MizePart {
fn id(&self) -> MizeId;
}
impl Mize {
pub fn empty() -> MizeResult<Mize> {
let id_pool = Arc::new(Mutex::new(VecStringPool::default()));
let namespace_pool_raw = StringPool::default();
let connections = Arc::new(Mutex::new(Vec::new()));
let subs = Arc::new(Mutex::new(HashMap::new()));
let (op_tx, op_rx) = unbounded();
let give_msg_wait = Arc::new(Mutex::new(HashMap::new()));
let create_msg_wait = Arc::new(Mutex::new(None));
let namespace = Arc::new(Mutex::new(Namespace(
namespace_pool_raw.get("mize.default.namespace"),
)));
let self_namespace = Arc::new(Mutex::new(Namespace(
namespace_pool_raw.get("mize.default.namespace"),
)));
let mut instance = Mize {
store: Arc::new(Mutex::new(Box::new(MemStore::new()))),
parts: Arc::new(Mutex::new(HashMap::new())),
part_names: Arc::new(Mutex::new(Vec::new())),
config_opts: Arc::new(Mutex::new(HashMap::new())),
listeners: Arc::new(Mutex::new(HashMap::new())),
connections,
subs,
id_pool,
namespace,
self_namespace,
op_tx,
namespace_pool: Arc::new(Mutex::new(namespace_pool_raw)),
threads: Arc::new(Mutex::new(Vec::new())),
next_thread_id: Arc::new(Mutex::new(0)),
next_con_id: Arc::new(Mutex::new(1)),
give_msg_wait,
create_msg_wait,
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)),
#[cfg(feature = "async")]
runtime: Arc::new(Mutex::new(
Runtime::new().mres_msg("Could not create async runtime")?,
)),
};
#[cfg(feature = "target-os")]
{
let instance_clone = instance.clone();
let op_rx_clone = op_rx.clone();
let closure = move || updater_thread(op_rx_clone, &instance_clone);
instance.spawn_background("updater_thread", closure)?;
let instance_clone_two = instance.clone();
let closure_two = move || updater_thread(op_rx, &instance_clone_two);
instance.spawn_background("updater_thread", closure_two)?;
}
// set up async update "threads" when using wasm
#[cfg(feature = "target-wasm ")]
{
let instance_clone = instance.clone();
let op_rx_clone = op_rx.clone();
wasm_bindgen_futures::spawn_local(updater_thread_async(op_rx_clone, instance_clone));
let instance_clone_two = instance.clone();
wasm_bindgen_futures::spawn_local(updater_thread_async(op_rx, instance_clone_two));
}
// will move the msg stuff into it's own thread
// like this it can deadlock.... if a msg waits on an operation to complete
//let msg_instance_clone = instance.clone();
//let msg_closure = move || msg_thread(op_rx, instance_clone);
//instance.spawn("msg_thread", closure)?;
#[cfg(feature = "target-wasm ")]
console_log!("before loading build time config");
// load the config from build time
let config = ItemData::from_toml(BUILD_TIME_CONFIG)?;
instance.set_blocking("0", config);
#[cfg(feature = "target-wasm ")]
console_log!("after loading build time config");
return Ok(instance);
}
pub fn new() -> MizeResult<Mize> {
trace!("[ {} ] Instance::new()", "CALL".yellow());
let mut instance = Mize::empty()?;
instance.init()?;
debug!(
"instance inited with config: {}",
instance.get("0/config")?.as_data_full()?
);
return Ok(instance);
}
pub fn id_new_dots(&mut self, name: &str) -> MizeResult<MizeId> {
let parts = name.split(".").map(|v| v.to_owned()).collect::<Vec<String>>();
self.new_id(parts)
}
pub fn get_part(&mut self, name: &str) -> MizeResult<PartRef> {
let id = self.id_new_dots(name)?;
Ok(PartRef { id })
}
pub fn has_part(&mut self, name: &str) -> MizeResult<bool> {
let id = self.id_new_dots(name)?;
Ok(self.parts.lock().unwrap().contains_key(&id))
}
pub fn add_listener(
&mut self,
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));
Ok(())
}
fn part_names(&mut self) -> Vec<&'static str> {
self.part_names.lock().unwrap().clone()
}
pub fn init(&mut self) -> MizeResult<()> {
// set global mize
set_global_mize(self.clone())?;
// platform specific init code
crate::platform::instance_init(self)?;
// end of platform specific init code
// load the modules, as specified in the parts config
match self
.get_config("parts")
{
Ok(parts_to_load) => {
for part_name in parts_to_load.cbor().as_array().ok_or("config parts was not an array")? {
let part_name = part_name.as_text().ok_or("part name was not a string")?;
if part_name.trim().is_empty() || self.has_part(part_name)? {
continue;
}
self.load_part(part_name)?;
}
}
Err(_err) => {
println!("hooooo");
// no parts to load
}
}
debug!("MIZE INIT DONE");
Ok(())
}
pub fn with_config(config: ItemData) -> MizeResult<Mize> {
trace!("[ {} ] Instance::with_config()", "CALL".yellow());
trace!("config: {}", config);
println!("here ....");
let mut mize = Mize::empty()?;
let mut opts = mize.config_opts.lock().unwrap();
let mut config = config.clone();
for mut path in config.get_paths_recursive()? {
if path.starts_with("/") {
path.remove(0);
}
let path_split = path
.split("/")
.map(|s| s.to_owned())
.collect::<Vec<String>>();
let path_string = path_split.join(".");
if let Ok(val) = config.get_path(path_split) {
let opt = ConfigOpt {
name: path.clone(),
val: Some(val),
fun: None,
deps: Vec::new(),
};
opts.insert(mize.clone().id_new_dots(&path_string)?, opt);
}
}
drop(opts);
mize.init()?;
Ok(mize)
}
pub fn migrate_to_store(&self, new_store: Box<dyn Store>) -> MizeResult<()> {
info!("MIGRATING");
let mut old_store = self.store.lock()?;
let id = self.id_from_string("0".to_owned())?;
let inst_data = old_store.get_value_data_full(id.clone())?;
new_store.set(id, inst_data.to_owned())?;
for id in old_store.id_iter()? {
let data = old_store.get_value_data_full(self.id_from_string(id?)?)?;
let id_of_new_store = new_store.new_id()?;
new_store.set(self.id_from_string(id_of_new_store)?, data.to_owned())?;
}
*old_store = new_store;
Ok(())
}
pub fn new_item(&self) -> MizeResult<Item> {
if self.get_namespace()? != self.get_self_namespace()? {
// need to send create msg and wait for it
let connection = self.get_connection_by_ns(self.get_namespace()?)?;
let msg = MizeMessage::new_create(connection.id);
connection.send(msg)?;
let (tx, rx) = bounded::<MizeId>(1);
let mut msg_wait_inner = self.create_msg_wait.lock()?;
*msg_wait_inner = Some(tx);
drop(msg_wait_inner);
let id = rx.recv()?;
println!("new_item namespace: {:?}", id.namespace());
return Ok(Item::new(id, self));
}
let store_inner = self.store.lock()?;
let id = self.id_from_string(store_inner.new_id()?)?;
return Ok(Item::new(id, self));
}
pub fn get<I: IntoMizeId>(&self, id: I) -> MizeResult<Item> {
let id = id.to_mize_id(self)?;
return Ok(Item::new(id, &self));
}
pub fn set<I: IntoMizeId, V: Into<ItemData>>(&self, id: I, value: V) -> MizeResult<()> {
let id = id.to_mize_id(self)?;
self.op_tx.send(Operation::Set(id, value.into(), None));
Ok(())
}
pub fn set_blocking<I: IntoMizeId, V: Into<ItemData>>(
&self,
id: I,
value: V,
) -> MizeResult<()> {
handle_operation(
&mut Operation::Set(id.to_mize_id(self)?, value.into(), None),
self,
)?;
Ok(())
}
pub fn sub<I: IntoMizeId>(&self, id: I, sub: Subscription) -> MizeResult<()> {
let mut subs_inner = self.subs.lock()?;
let id = id.to_mize_id(self)?;
match subs_inner.get_mut(&id) {
Some(vec) => {
vec.push(sub);
}
None => {
subs_inner.insert(id.clone(), vec![sub]);
}
}
// if we are not the owner of this item, send a sub msg to them
if !self.we_are_namespace()? {
let con = self.get_connection_by_ns(id.namespace())?;
let msg = MizeMessage::new_sub(id, con.id);
con.send(msg)?;
}
Ok(())
}
pub fn new_id<T: IntoMizeId>(&self, value: T) -> MizeResult<MizeId> {
value.to_mize_id(self)
}
pub fn id_from_string(&self, string: String) -> MizeResult<MizeId> {
let vec_string: Vec<String> = string
.split("/")
.map(|v| v.to_owned())
.filter(|el| el.as_str() != "")
.collect();
self.id_from_vec_string(vec_string)
}
pub fn id_from_vec_string(&self, mut vec_string: Vec<String>) -> MizeResult<MizeId> {
let id_pool_inner = self.id_pool.lock()?;
let namespace_inner = self.namespace.lock()?;
let first_el = vec_string
.first_mut()
.ok_or(mize_err!("MizeId was empty"))?;
let id = if first_el.contains(":") {
// first el is a namespace + store_part
let new_first_el = first_el.clone();
let vec: Vec<&str> = new_first_el.split(":").collect();
let ns_part = vec
.iter()
.nth(0)
.ok_or(mize_err!("should really not happen"))?;
let store_part = vec.iter().nth(1).ok_or(mize_err!(
"mizeid was like 'namespace:/hi', why are you doing that"
))?;
*first_el = store_part.to_owned().to_owned();
MizeId {
path: id_pool_inner.get(vec_string),
namespace: self.namespace_from_string(ns_part.to_owned().to_owned())?,
}
} else {
MizeId {
path: id_pool_inner.get(vec_string),
namespace: namespace_inner.clone(),
}
};
trace!("new MizeId made: {:?}", id);
Ok(id)
}
pub fn load_part(&mut self, name: &str) -> MizeResult<()> {
tracing::debug!("loading part: {name}");
crate::platform::load_part(self, name, None)
}
pub fn fetch_part(&mut self, name: &str) -> MizeResult<String> {
// platform specific way to fetch a module
crate::platform::fetch_part(self, name)
}
pub fn load_part_at(&mut self, name: &str, path: String) -> MizeResult<()> {
// platform specific init code
crate::platform::load_part(self, name, Some(path))
}
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));
Ok(namespace)
}
pub fn set_namespace(&self, ns: Namespace) -> MizeResult<()> {
let mut namespace_inner = self.namespace.lock()?;
*namespace_inner = ns;
Ok(())
}
pub fn get_namespace(&self) -> MizeResult<Namespace> {
let namespace_inner = self.namespace.lock()?;
return Ok(namespace_inner.clone());
}
pub fn get_self_namespace(&self) -> MizeResult<Namespace> {
let self_namespace_inner = self.self_namespace.lock()?;
return Ok(self_namespace_inner.clone());
}
pub fn we_are_namespace(&self) -> MizeResult<bool> {
Ok(self.get_namespace()? == self.get_self_namespace()?)
}
pub fn add_conn_listener<T: ConnListener + 'static>(&mut self, listener: T) -> MizeResult<()> {
let instance_clone = self.clone();
self.spawn_background("some_listener", move || listener.listen(instance_clone));
Ok(())
}
pub fn new_connection(&self, tx: Sender<MizeMessage>) -> MizeResult<u64> {
let mut conn_inner = self.connections.lock()?;
let mut next_con_id = self.next_con_id.lock()?;
let old_next_con_id = *next_con_id;
let connection = Connection {
id: next_con_id.to_owned(),
tx,
ns: None,
};
conn_inner.push(connection);
*next_con_id += 1;
Ok(old_next_con_id)
}
pub fn new_connection_join_namespace(&self, tx: Sender<MizeMessage>) -> MizeResult<u64> {
let conn_id = self.new_connection(tx)?;
let ns_of_peer_str = self
.get(format!(
"inst/con_by_id/{}/peer/self/config/namespace",
conn_id
))?
.value_string()?;
let ns_of_peer = self.namespace_from_string(ns_of_peer_str)?;
self.connection_set_namespace(conn_id, ns_of_peer.clone());
self.set_namespace(ns_of_peer);
Ok(conn_id)
}
pub fn connection_set_namespace(&self, conn_id: u64, namespace: Namespace) -> MizeResult<()> {
let mut connection = self.get_connection(conn_id)?;
connection.ns = Some(namespace);
self.set_connection(conn_id, connection);
Ok(())
}
pub fn got_msg(&self, msg: MizeMessage) -> MizeResult<()> {
Ok(self.op_tx.send(Operation::Msg(msg))?)
}
pub fn report_err(&self, err: MizeError) {
#[cfg(feature = "target-js")] unsafe {
use wasm_bindgen::prelude::wasm_bindgen;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
macro_rules! console_log {
// Note that this is using the `log` function imported above during
// `bare_bones`
($($t:tt)*) => ({ log(&format_args!($($t)*).to_string())})
}
console_log!("reported_err: {:?}", err);
}
err.log();
}
fn set_connection(&self, conn_id: u64, new_connection: Connection) -> MizeResult<()> {
let mut conn_inner = self.connections.lock()?;
for connection in conn_inner.iter_mut() {
if connection.id == conn_id {
*connection = new_connection;
return Ok(());
}
}
return Err(mize_err!(
"Connection with id {} not known to instance",
conn_id
));
}
pub fn get_connection(&self, conn_id: u64) -> MizeResult<Connection> {
let conn_inner = self.connections.lock()?;
for connection in conn_inner.iter() {
if connection.id == conn_id {
return Ok(connection.clone());
}
}
return Err(mize_err!(
"Connection with id {} not known to instance",
conn_id
));
}
pub fn get_connection_by_ns(&self, ns: Namespace) -> MizeResult<Connection> {
let conn_inner = self.connections.lock()?;
for connection in conn_inner.iter() {
if connection.ns == Some(ns.clone()) {
return Ok(connection.clone());
}
}
return Err(mize_err!(
"Connection with namespace {} not known to instance",
ns.as_string()
));
}
pub fn spawn_and_wait(
&mut self,
name: &str,
func: impl FnOnce() -> MizeResult<()> + Send + 'static,
) -> MizeResult<()> {
let mize_clone = self.clone();
let mut threads_inner = self.threads.lock()?;
let mut next_thread_id = self.next_thread_id.lock()?;
let my_thread_id_no_mutex_guard = *next_thread_id;
let _thread_mutex = self.threads.clone();
let name_to_move = name.to_owned();
let to_spawn = move || -> MizeResult<()> {
debug!("spawning thread: {}", name_to_move);
let _my_thread_id = my_thread_id_no_mutex_guard;
if let Err(err) = func() {
mize_clone.report_err(err);
}
//let mut threads_inner = thread_mutex.lock()?;
/*
*threads_inner = threads_inner
.clone()
.into_iter()
.filter(|el| match el {
(my_thread_id, _) => false,
(_, _) => true,
})
.collect();
*/
debug!("thread '{}' stopped", name_to_move);
Ok(())
};
*next_thread_id += 1;
#[cfg(feature = "target-os")]
let handle = thread::spawn(move || to_spawn());
#[cfg(feature = "target-os")]
threads_inner.push((*next_thread_id, name.to_owned(), Some(handle)));
#[cfg(feature = "target-wasm ")]
{
//console_log!("in instance::spawn with wasm target")
}
//NOT WELL SUPPORTED
//crate::platform::wasm::wasm_spawn(to_spawn)?;
Ok(())
}
pub fn spawn_background(
&mut self,
name: &str,
func: impl FnOnce() -> MizeResult<()> + Send + 'static,
) -> MizeResult<()> {
let mut threads_inner = self.threads.lock()?;
let mut next_thread_id = self.next_thread_id.lock()?;
let my_thread_id_no_mutex_guard = *next_thread_id;
let _thread_mutex = self.threads.clone();
let name_to_move = name.to_owned();
let to_spawn = move || -> () {
debug!("spawning thread: {}", name_to_move);
let _my_thread_id = my_thread_id_no_mutex_guard;
let res = func();
if let Err(e) = res {
e.log();
}
//let mut threads_inner = thread_mutex.lock()?;
/*
*threads_inner = threads_inner
.clone()
.into_iter()
.filter(|el| match el {
(my_thread_id, _) => false,
(_, _) => true,
})
.collect();
*/
debug!("thread '{}' stopped", name_to_move);
};
*next_thread_id += 1;
#[cfg(feature = "target-os")]
thread::spawn(move || to_spawn());
threads_inner.push((*next_thread_id, name.to_owned(), None));
#[cfg(feature = "target-js")]
{
//console_log!("in instance::spawn with wasm target")
}
//NOT WELL SUPPORTED
//crate::platform::wasm::wasm_spawn(to_spawn)?;
Ok(())
}
pub fn give_msg_wait(&self, id: MizeId) -> MizeResult<ItemData> {
let mut give_msg_wait_inner = self.give_msg_wait.lock()?;
let (tx, rx) = bounded::<ItemData>(1);
let vec = match give_msg_wait_inner.get_mut(&id) {
Some(vec) => vec,
None => {
give_msg_wait_inner.insert(id.clone(), Vec::new());
give_msg_wait_inner.get_mut(&id).unwrap()
}
};
vec.push(tx);
// so that another thread can also give_msg_wait(), while we wait in the recv() of rx
drop(vec);
drop(give_msg_wait_inner);
let data = rx.recv()?;
return Ok(data);
}
#[cfg(feature = "async")]
pub fn spawn_async<F: Future<Output = impl Send + 'static> + Send + 'static>(
&mut self,
name: &str,
func: F,
) -> MizeResult<()> {
let mut threads_inner = self.threads.lock()?;
let mut next_thread_id = self.next_thread_id.lock()?;
threads_inner.push((*next_thread_id, name.to_owned(), None));
let runtime_inner = self.runtime.lock().unwrap();
runtime_inner.spawn(func);
*next_thread_id += 1;
Ok(())
}
#[cfg(feature = "async")]
pub fn async_get_handle(&self) -> Handle {
let runtime_inner = self.runtime.lock().unwrap();
let handle = runtime_inner.handle().to_owned();
handle
}
#[cfg(feature = "async")]
pub fn spawn_async_blocking<
F: Future<Output = impl Send + Sync + 'static> + Send + Sync + 'static,
>(
&mut self,
name: &str,
func: F,
) -> F::Output {
let mut threads_inner = self
.threads
.lock()
.expect("mutex lock failed in spawn_async_blocking");
let mut next_thread_id = self
.next_thread_id
.lock()
.expect("mutex lock failed in spawn_async_blocking");
threads_inner.push((*next_thread_id, name.to_owned(), None));
let runtime_inner = self.runtime.lock().unwrap();
let handle = runtime_inner.handle().to_owned();
*next_thread_id += 1;
drop(runtime_inner);
drop(threads_inner);
drop(next_thread_id);
handle.enter();
let result = handle.block_on(func);
return result;
}
pub fn wait(&self) {
info!("Instance main thread waiting");
loop {
thread::sleep(Duration::from_secs(100000))
}
}
pub fn report_error(err: MizeError) {
err.log();
}
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));
}
// Return cached value
if let Some(val) = opt.val.clone() {
return Ok(val);
}
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 parts = self.parts.lock()?;
let _part = parts.get_mut(&part_id).ok_or(mize_err!("part {part_id} is not loaded"))?;
println!("would be dyn_call ing from part, {:?}, {method}, {:?}", part_id, args);
Ok(ItemData::new())
}
pub fn get_part_gen<T: MizePart + Send + Sync + 'static>(
&mut self,
name: &str,
) -> MizeResult<T> {
let dyn_part = self.get_part(name)?;
let concrete_part = dyn_part
.into_any()
.downcast::<T>()
.map_err(|_| mize_err!("failed to downcast"))?;
Ok(*concrete_part)
}
pub fn id_path(&self, id: &MizeId) -> MizeResult<String> {
Ok(id.path().join("/"))
}
}
impl fmt::Debug for Mize {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Mize Instance with subs: {:?}", self.subs,)
}
}
impl Default for Mize {
fn default() -> Self {
Self::empty().unwrap()
}
}

981
core/src/item.rs Normal file
View File

@@ -0,0 +1,981 @@
use crate::c_api::get_global_mize;
use crate::config::{ConfigOpt};
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::collections::HashMap;
use std::fmt::Debug;
use std::sync::MutexGuard;
use std::io;
use std::fmt::Display;
use std::i128;
use tracing::{debug, instrument, trace, warn};
use crate::error::{IntoMizeResult, MizeError, MizeResult};
use crate::id::MizeId;
use crate::instance::connection::value_raw_con_by_id;
use crate::instance::Mize;
use crate::mize_err;
use crate::proto::MizeMessage;
use ciborium::Value as CborValue;
// a item always has to do with a Instance, which takes care of how it is updated
#[derive(Debug, Clone)]
pub struct Item<'a> {
id: MizeId,
pub instance: &'a Mize,
}
// without an Instance it is not an item, but only the "data of an item"
// and this type for now is just an alias to CborValue
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ItemData(pub CborValue);
impl Item<'_> {
pub fn id(&self) -> MizeId {
self.id.clone()
}
pub fn new(id: MizeId, instance: &Mize) -> Item {
Item { id, instance }
}
pub fn value_raw(&self) -> MizeResult<Vec<u8>> {
let data = self.as_data_full()?;
let raw = get_raw_from_cbor(data.cbor(), vec![])?;
return Ok(raw.to_owned());
}
pub fn value_string(&self) -> MizeResult<String> {
let raw = self.value_raw()?;
String::from_utf8(raw.clone())
.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();
// the instance we are
if id.store_part() == "self" {
debug!("getting item '{}' from self", self.id());
match id.nth_part(1)? {
"con_by_id" => {
return value_raw_con_by_id(&mut self.clone());
}
"namespace" => {
let namespace_inner = self.instance.namespace.lock()?;
return Ok(ItemData::from_string(namespace_inner.as_real_string()));
}
"self_namespace" => {
let namespace_inner = self.instance.self_namespace.lock()?;
return Ok(ItemData::from_string(namespace_inner.as_real_string()));
}
"config" => {
let rest_path = <Box<[std::string::String]> as Clone>::clone(&id
.path)
.into_iter()
.skip(2)
.map(|v| v.to_owned())
.collect::<Vec<String>>()
.join(".");
let mut mize = self.instance.to_owned();
return Ok(mize.get_config(rest_path.as_str())?);
}
_ => {}
}
return Err(mize_err!(
"a /self path, but the next element in the path '{:?}' is not valid",
id.nth_part(1)
));
}
let self_namespace_inner = self.instance.self_namespace.lock()?;
if self.id().namespace() == self_namespace_inner.to_owned() {
debug!("getting item '{}' from store", self.id());
if id.store_part() == "inst" {
match id.nth_part(1)? {
"con_by_id" => {
return value_raw_con_by_id(&mut self.clone());
}
"namespace" => {
let namespace_inner = self.instance.namespace.lock()?;
return Ok(ItemData::from_string(namespace_inner.as_real_string()));
}
"self_namespace" => {
let namespace_inner = self.instance.self_namespace.lock()?;
return Ok(ItemData::from_string(namespace_inner.as_real_string()));
}
_ => {}
}
}
let store_inner = self.instance.store.lock()?;
return store_inner.get_value_data_full(self.id());
} else {
let connection = self.instance.get_connection_by_ns(self.id().namespace())?;
debug!(
"getting item '{}' from connection '{}' from store",
self.id(),
connection.id
);
let msg = MizeMessage::new_get(self.id(), connection.id);
connection.send(msg);
let data = self.instance.give_msg_wait(self.id())?;
return Ok(data);
}
}
#[instrument(name = "fn.ItemData::merge")]
pub fn merge<V: Into<ItemData> + Debug>(&mut self, value: V) -> MizeResult<()> {
let mut data = self.as_data_full()?;
//let mut data = data_full.get_path(id_path_without_store_part)?;
trace!("item::merge data: {:?}", data);
trace!("item::merge id: {:?}", self.id());
let new_data = value.into();
data.merge(new_data);
trace!("item::merge new_data: {:?}", data);
// handle the case of /self/*
if self.id().store_part() == "self" {
let rest_path = <Box<[std::string::String]> as Clone>::clone(&self
.id()
.path)
.into_iter()
.skip(1)
.map(|v| v.to_owned())
.collect::<Vec<String>>()
.join("/");
let id_for_store = self
.instance
.new_id("0".to_owned() + "/" + rest_path.as_str())?;
let store_inner = self.instance.store.lock()?;
store_inner.set(id_for_store, data)?;
} else if self.instance.we_are_namespace()? {
let store_inner = self.instance.store.lock()?;
store_inner.set(self.id(), data)?;
} else {
let namespace = self.id().namespace();
let connection = self.instance.get_connection_by_ns(namespace)?;
let msg = MizeMessage::new_update_request(self.id(), data, connection.id);
connection.send(msg)?;
}
Ok(())
}
}
impl<'a> Display for Item<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
//let item_data = self.as_data_full().map_err(|_| std::fmt::Error)?;
write!(f, "Item with id: {}", self.id());
return Ok(());
}
}
impl ItemData {
pub fn new() -> ItemData {
ItemData(CborValue::Null)
}
pub fn empty() -> ItemData {
ItemData(CborValue::Null)
}
pub fn from_toml(toml_string: &str) -> MizeResult<ItemData> {
let toml_deserializer = toml::Deserializer::new(&toml_string);
let value = CborValue::deserialize(toml_deserializer).mres_msg(format!(
"Could not deserialize the toml string: {}",
&toml_string
))?;
return Ok(value.into_item_data());
}
pub fn to_toml(&self) -> MizeResult<String> {
let mut out_string = String::new();
let toml_serializer = toml::Serializer::new(&mut out_string);
CborValue::serialize(&self.cbor(), toml_serializer)?;
Ok(out_string)
}
pub fn from_serializable<T: Serialize>(val: T) -> MizeResult<ItemData> {
let mut buffer = Vec::new();
into_writer(&val, &mut buffer)?;
let cbor_val: CborValue = from_reader(buffer.as_slice())?;
Ok(ItemData(cbor_val))
}
/// Convert any native Rust value to ItemData...
/// Stores the value in the global Mize registry and returns an ItemData with _native: <id>
pub fn from_native<T: Any + Send + Sync + 'static>(val: T) -> MizeResult<ItemData> {
let mize = get_global_mize()?;
let mut native_values = mize.native_values.lock().map_err(|_| mize_err!("Failed to lock native_values"))?;
let mut next_id = mize.next_native_value_id.lock().map_err(|_| mize_err!("Failed to lock next_native_value_id"))?;
let id = *next_id;
*next_id += 1;
native_values.insert(id, Box::new(val));
let mut data = ItemData::new();
data.set_path("_native", id.to_string())?;
Ok(data)
}
pub fn value_string(self) -> MizeResult<String> {
match self.cbor() {
CborValue::Text(string) => Ok(string.to_owned()),
_ => Err(mize_err!("this ItemData has no string value")),
}
}
pub fn value_bool(self) -> MizeResult<bool> {
match self.cbor() {
CborValue::Bool(val) => Ok(*val),
_ => Err(mize_err!("this ItemData has no string value")),
}
}
pub fn as_bool(self) -> Option<bool> {
match self.cbor() {
CborValue::Bool(val) => Some(*val),
_ => None,
}
}
pub fn from_string<S: Into<String>>(into_string: S) -> ItemData {
let string = into_string.into();
let data = CborValue::Text(string);
return ItemData(data);
}
pub fn to_json(self) -> MizeResult<String> {
let mut result: Vec<u8> = Vec::new();
self.0
.serialize(&mut serde_json::Serializer::new(&mut result))?;
Ok(String::from_utf8(result)?)
}
pub fn from_json(json_str: String) -> MizeResult<ItemData> {
let mut json_deserializer = serde_json::Deserializer::from_str(json_str.as_str());
let value = CborValue::deserialize(&mut json_deserializer).mres_msg(format!(
"could not deserialize the json string: {}",
json_str
))?;
return Ok(value.into_item_data());
}
pub fn merge(&mut self, other: ItemData) {
item_data_merge(&mut self.0, &other.0);
}
pub fn cbor(&self) -> &CborValue {
&self.0
}
pub fn from_cbor(cbor: CborValue) -> ItemData {
ItemData(cbor)
}
pub fn sort_keys(&mut self) -> MizeResult<()> {
item_data_sort_keys(&mut self.0)
}
pub fn null() -> CborValue {
CborValue::Null
}
pub fn parse<T: Into<String>>(value: T) -> ItemData {
let value_str = value.into();
if value_str == "false" {
return CborValue::Bool(false).into_item_data();
}
if value_str == "false" {
return CborValue::Bool(true).into_item_data();
}
if let Ok(int) = value_str.parse::<i128>() {
return int.into_item_data();
}
return CborValue::Text(value_str).into_item_data();
}
pub fn set_path<P: IntoPath, D: IntoItemData>(&mut self, path: P, value: D) -> MizeResult<()> {
//trace!("[ {} ] ItemData::set_path()", "CALL".yellow());
let path = path.into_path();
let value = value.into_item_data();
item_data_set_path(&mut self.0, path, &value.0)
}
pub fn get_path<P: IntoPath>(&self, path: P) -> MizeResult<ItemData> {
let path = path.into_path();
Ok(item_data_get_path(&self.0, path)?
.to_owned()
.into_item_data())
}
pub fn get_paths_recursive(&mut self) -> MizeResult<Vec<String>> {
let mut paths = Vec::new();
let mut path_accu = Vec::new();
get_paths(&mut self.0, &mut paths, &mut path_accu, true)?;
Ok(paths)
}
pub fn get_paths(&mut self) -> MizeResult<Vec<String>> {
let mut paths = Vec::new();
let mut path_accu = Vec::new();
get_paths(&mut self.0, &mut paths, &mut path_accu, false)?;
Ok(paths)
}
pub fn arr_nth(&self, index: usize) -> MizeResult<ItemData> {
let array = self
.0
.as_array()
.ok_or(mize_err!("ItemData is not an array, cannot get element at index {}", index))?;
let value = array
.get(index)
.ok_or(mize_err!("array index {} out of bounds (length: {})", index, array.len()))?;
Ok(ItemData::from_cbor(value.clone()))
}
/// 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.
pub fn native<T: Any + Send + Sync + 'static>(&self) -> MizeResult<NativeRefGuard<T>> {
let mize_arc = crate::c_api::GLOBAL_MIZE.get().ok_or(mize_err!("No global Mize"))?;
let mize = mize_arc.lock().map_err(|_| mize_err!("Failed to lock 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)
.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);
Ok(NativeRefGuard {
_guard: unsafe { std::mem::transmute(guard) },
value: unsafe { &*ref_t_ptr },
})
}
/// 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.
pub fn native_mut<T: Any + Send + Sync + 'static>(&self) -> MizeResult<NativeMutRefGuard<T>> {
let mize_arc = crate::c_api::GLOBAL_MIZE.get().ok_or(mize_err!("No global Mize"))?;
let mize = mize_arc.lock().map_err(|_| mize_err!("Failed to lock 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 },
})
}
/// 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_arc = crate::c_api::GLOBAL_MIZE.get().ok_or(mize_err!("No global Mize"))?;
let mize = mize_arc.lock().map_err(|_| mize_err!("Failed to lock 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.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)
}
}
/// Guard that holds a lock on the native_values registry and provides an immutable reference to a native value.
pub struct NativeRefGuard<T: Any + Send + Sync + 'static> {
_guard: MutexGuard<'static, HashMap<u32, Box<dyn Any + Send + Sync>>>,
value: &'static T,
}
impl<T: Any + Send + Sync + 'static> std::ops::Deref for NativeRefGuard<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.value
}
}
/// Guard that holds a lock on the native_values registry and provides a mutable reference to a native value.
pub struct NativeMutRefGuard<T: Any + Send + Sync + 'static> {
_guard: MutexGuard<'static, HashMap<u32, Box<dyn Any + Send + Sync>>>,
value: &'static mut T,
}
impl<T: Any + Send + Sync + 'static> std::ops::Deref for NativeMutRefGuard<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.value
}
}
impl<T: Any + Send + Sync + 'static> std::ops::DerefMut for NativeMutRefGuard<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.value
}
}
fn get_paths(
val: &mut CborValue,
paths: &mut Vec<String>,
path_accu: &mut Vec<String>,
recurse: bool,
) -> MizeResult<()> {
let map = val
.as_map_mut()
.ok_or(mize_err!("ItemData is not map... when calling get_paths"))?;
for (key, val) in map {
let text = key
.as_text()
.ok_or(mize_err!("ItemData::get_paths but key is not of type text"))?
.to_owned();
if recurse && val.is_map() {
let mut path_accu = path_accu.clone();
path_accu.push(text);
get_paths(val, paths, &mut path_accu, recurse)?;
} else {
let path = path_accu.join("/") + "/" + text.as_str();
paths.push(path);
}
}
Ok(())
}
impl Default for ItemData {
fn default() -> Self {
ItemData::new()
}
}
pub fn data_from_string(data_string: String) -> MizeResult<ItemData> {
let mut data = ItemData::new();
// in case it's just a string
if !data_string.contains("=") {
let cbor = CborValue::Text(data_string);
return Ok(ItemData::from_cbor(cbor));
}
for option in data_string.split(":") {
//trace!("data_from_string: option: {}", option);
if option == "".to_owned() {
continue;
}
let path = option
.split("=")
.nth(0)
.ok_or(MizeError::new().msg(format!(
"Failed to parse Option: option '{}' has an empty path (thing before =)",
option
)))?;
//trace!("data_from_string: path: {}", path);
let value = option
.split("=")
.nth(1)
.ok_or(MizeError::new().msg(format!(
"Failed to parse Option: option '{}' has an empty value (thing after =)",
option
)))?;
//trace!("data_from_string: value: {}", value);
let mut path_vec = Vec::new();
path_vec.extend(path.split("."));
//trace!("data_from_string: path_vec: {:?}", path_vec);
data.set_path(path_vec, ItemData::parse(value))?;
}
trace!("data_from_string: data: {}", data);
return Ok(data);
}
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,
None => return Ok(data), // our base case
};
let mut sub_data = &CborValue::Null;
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;
}
}
}
}
val => {
return Err(MizeError::new()
.msg(format!(
"Failed to get path '{:?}' from ItemData, the data at '{}' is not a map",
path, path_el
))
.msg(format!("{:?} is: {:?}", path_el, val)));
}
};
item_data_get_path(sub_data, path_iter.collect())
}
pub fn item_data_sort_keys(data: &mut CborValue) -> MizeResult<()> {
// for a list, sort each item of the list
if data.is_array() {
for el in data.as_array_mut().unwrap().iter_mut() {
item_data_sort_keys(el)?
}
}
// sort the keys of a map
if data.is_map() {
let map: &mut Vec<(CborValue, CborValue)> = data.as_map_mut().unwrap();
//map.sort_by_key(|el| el.0);
map.sort_by(|a, b| {
match a.0.partial_cmp(&b.0) {
Some(val) => val,
None => {
// just say the two values are equal, if they can't be ordered
std::cmp::Ordering::Equal
}
}
});
// also sort every sub value
for (_key, val) in map {
item_data_sort_keys(val)?
}
}
Ok(())
}
pub fn item_data_set_path(
data: &mut CborValue,
path: Vec<String>,
data_to_set: &CborValue,
) -> MizeResult<()> {
//trace!("[ {} ] item_data_set_path()", "CALL".yellow());
//trace!("[ {} ] data: {}", "ARG".yellow(), data.clone().into_item_data());
//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,
None => {
//trace!("item_data_set_path: base_case");
// our base case
*data = data_to_set.to_owned();
return Ok(());
}
};
match data {
CborValue::Map(map) => {
for (key, val) in &mut *map {
if let CborValue::Text(key_str) = key {
if key_str == &path_el {
//trace!("item_data_set_path: setting key {key_str} in map with recursive call");
return item_data_set_path(val, path_iter.collect(), data_to_set);
}
}
}
// if we get here, that means, the key (which is the variable path_el) does not exist
// in the map
// so add it... but also with a recursive call which is needed, in case path_iter
// longer than 1
//trace!("item_data_set_path: adding key {path_el} to map");
let mut inner_value = CborValue::Null;
item_data_set_path(&mut inner_value, path_iter.collect(), data_to_set);
map.push((CborValue::Text(path_el.clone()), inner_value));
return Ok(());
}
CborValue::Null => {
//trace!("item_data_set_path: CborValue::Null case");
let mut inner_value = CborValue::Null;
item_data_set_path(&mut inner_value, path_iter.collect(), data_to_set);
let map_vec = vec![(CborValue::Text(path_el), inner_value)];
*data = CborValue::Map(map_vec);
return Ok(());
}
val => {
return Err(MizeError::new()
.msg(format!(
"Failed to get path '{:?}' from ItemData, the data at '{}' is not a map",
path, path_el
))
.msg(format!("{:?} is: {:?}", path_el, val)));
}
};
}
pub fn item_data_merge(merge_into: &mut CborValue, other: &CborValue) {
// needs to be recursive
match (merge_into, other) {
// if both are maps
// merge the keys/vals in the maps and call recursively on them
(CborValue::Map(merge_into_map), CborValue::Map(other_map)) => {
// go through other_map and merge those keys/values to merge_into
for (other_key, other_val) in other_map {
// so find that same vale in merge_into, if exists recursively merge, else just add to
// vec
let mut found_value = CborValue::Null;
let mut value_found = false;
let mut new_value_for_merge_into: Vec<(CborValue, CborValue)> = Vec::new(); // here we collect the
// new contents of the
// map, to assign later
// to merge_into_map
for (merge_into_key, merge_into_val) in merge_into_map.clone() {
if merge_into_key == *other_key {
value_found = true;
found_value = merge_into_val.to_owned();
// if we found it, also don't add it to new_value_for_merge_into
} else {
//if not found push to new_value_for_merge_into
new_value_for_merge_into
.push((merge_into_key.to_owned(), merge_into_val.to_owned()));
}
}
if value_found {
// we found it so merge and asign
item_data_merge(&mut found_value, other_val);
// to asign push to new_value_for_merge_into, because the old was removed already
new_value_for_merge_into.push((other_key.to_owned(), found_value));
} else {
// if not found, just add the other_key/val to new_value_for_merge_into
new_value_for_merge_into.push((other_key.to_owned(), other_val.to_owned()));
}
// finally, we need to asign the new_value_for_merge_into into merge_into
*merge_into_map = new_value_for_merge_into;
}
}
// if other is Null, don't asign
// this is gonna have repercussions.... because it's not clean behaviour
(_merge_into, CborValue::Null) => {}
// in any other case we just want to set merge_into to other
// also this is the base case
(merge_into, other) => {
*merge_into = other.to_owned();
}
}
}
#[instrument(name = "fn.get_raw_from_cbor")]
pub fn get_raw_from_cbor<'a>(value: &'a CborValue, path: Vec<&String>) -> MizeResult<&'a [u8]> {
trace!("[ {} ] value: {:?}", "ARG".yellow(), value);
trace!("[ {} ] path: {:?}", "ARG".yellow(), path);
let mut path_iter = path.clone().into_iter();
let path_el =
match path_iter.nth(0) {
Some(val) => val,
None => {
// our base case
let _ret_val =
match value {
CborValue::Bytes(vec) => {
trace!("[ {} ] ret value: {:?}", "RET".yellow(), &vec[..]);
return Ok(&vec[..]);
}
CborValue::Text(string) => {
trace!("[ {} ] ret value: {:?}", "RET".yellow(), string.as_bytes());
return Ok(string.as_bytes());
}
other => {
return Err(mize_err!(
"path '{}' is empty and the value: '{:?}' is neither Bytes nor Text",
path.iter().map(|p| p.as_str()).collect::<Vec<_>>().join("."),
other
));
}
};
}
};
match value {
CborValue::Bytes(vec) => {
trace!("[ {} ] ret value: {:?}", "RET".yellow(), &vec[..]);
return Ok(&vec[..]);
}
CborValue::Text(string) => {
trace!("[ {} ] ret value: {:?}", "RET".yellow(), string.as_bytes());
return Ok(string.as_bytes());
}
CborValue::Map(map) => {
let mut inner_val: &CborValue = &CborValue::Null;
let mut inner_val_found = false;
// find the inner_val at path
for (key, val) in map.into_iter() {
if let CborValue::Text(key_text) = key {
if key_text == path_el {
inner_val = val;
inner_val_found = true;
}
}
}
// if there is no value at that path
if inner_val_found == false {
return Err(MizeError::new()
// jesus christ, this is a pfusch...
.msg(format!(
"get_raw_from_cbor: Path '{}' not Found",
path.clone()
.into_iter()
.map(|v| v.to_owned())
.collect::<Vec<String>>()
.join("/")
)));
}
//path_iter.next();
let inner_path = path_iter.collect();
let ret_value = get_raw_from_cbor(inner_val, inner_path);
trace!("[ {} ] ret value: {:?}", "RET".yellow(), ret_value);
return ret_value;
}
_ => Err(MizeError::new().msg(format!(
"get_raw_from_cbor: value is not a map, text or bytes ... value: {:?}",
value
))),
}
}
trait IntoPath {
fn into_path(self) -> Vec<String>;
}
pub trait IntoItemData {
fn into_item_data(self) -> ItemData;
}
impl IntoPath for &str {
fn into_path(self) -> Vec<String> {
if !self.contains('/') {
return self.split(".").map(|v| v.to_owned()).collect();
}
self.to_owned().split("/").map(|v| v.to_owned()).collect()
}
}
impl IntoPath for &[&str] {
fn into_path(self) -> Vec<String> {
self.into_iter().map(|v| (*v).to_owned()).collect()
}
}
impl IntoPath for Vec<String> {
fn into_path(self) -> Vec<String> {
self
}
}
impl IntoPath for Vec<&str> {
fn into_path(self) -> Vec<String> {
self.into_iter().map(|v| v.to_owned()).collect()
}
}
impl IntoItemData for &str {
fn into_item_data(self) -> ItemData {
ItemData(self.into())
}
}
impl IntoItemData for String {
fn into_item_data(self) -> ItemData {
ItemData(self.into())
}
}
impl IntoItemData for &String {
fn into_item_data(self) -> ItemData {
ItemData(self.to_owned().into())
}
}
impl IntoItemData for Vec<String> {
fn into_item_data(self) -> ItemData {
let array = self
.into_iter()
.map(|v| v.into_item_data().cbor().to_owned())
.collect::<Vec<_>>();
ItemData(CborValue::Array(array))
}
}
impl IntoItemData for CborValue {
fn into_item_data(self) -> ItemData {
ItemData(self)
}
}
impl IntoItemData for ItemData {
fn into_item_data(self) -> ItemData {
self
}
}
impl IntoItemData for i128 {
fn into_item_data(self) -> ItemData {
ItemData(self.into())
}
}
impl IntoItemData for u32 {
fn into_item_data(self) -> ItemData {
ItemData(self.into())
}
}
impl IntoItemData for bool {
fn into_item_data(self) -> ItemData {
ItemData(self.into())
}
}
impl fmt::Display for ItemData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ItemData: ");
let display_writer = DisplayWriter(f);
self.0
.serialize(&mut serde_json::Serializer::pretty(display_writer))
.map_err(|_serde_err| std::fmt::Error)
}
}
// thanks to: https://stackoverflow.com/a/61768916
struct DisplayWriter<'a, 'b>(&'a mut fmt::Formatter<'b>);
impl<'a, 'b> io::Write for DisplayWriter<'a, 'b> {
fn write(&mut self, bytes: &[u8]) -> std::result::Result<usize, std::io::Error> {
self.0
.write_str(&String::from_utf8_lossy(bytes))
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?;
Ok(bytes.len())
}
fn flush(&mut self) -> std::result::Result<(), std::io::Error> {
todo!()
}
}
impl PartialEq for ItemData {
fn eq(&self, other: &Self) -> bool {
let mut buf: Vec<u8> = Vec::new();
let mut buf_other: Vec<u8> = Vec::new();
ciborium::into_writer(&self.0, &mut buf);
ciborium::into_writer(&other.0, &mut buf_other);
buf == buf_other
}
}
impl Eq for ItemData {}
pub fn set_config_from_data(mize: &mut Mize, mut data: ItemData) -> MizeResult<()> {
let mut config_opts = mize.config_opts.lock().unwrap();
for path in data.get_paths_recursive()? {
let mut conf_name = path.replace("/", ".");
if conf_name.starts_with(".") {
conf_name.remove(0);
}
let val = data.get_path(path.split("/").filter(|s| s != &"").collect::<Vec<&str>>())?;
match config_opts.get_mut(&mize.clone().id_new_dots(&conf_name)?) {
Some(opt) => {
opt.val = Some(val);
}
None => {
config_opts.insert(
mize.clone().id_new_dots(&conf_name)?,
ConfigOpt {
name: conf_name,
val: Some(val),
fun: None,
deps: Vec::new(),
},
);
}
};
}
Ok(())
}

470
core/src/platform/os/mod.rs Normal file
View File

@@ -0,0 +1,470 @@
use clap::ArgMatches;
use colored::Colorize;
use core::fmt;
use flate2::bufread::GzDecoder;
use sha2::{Digest, Sha256};
use std::env::VarError;
use std::fmt::Write;
use std::fs;
use std::fs::File;
use std::path::Path;
use std::path::PathBuf;
use std::time::UNIX_EPOCH;
use tar::Archive;
use tracing::Event;
use tracing::{debug, info, trace, warn};
use tracing::field::Visit;
use tracing::Level;
use tracing_core::Field;
use tracing_subscriber::fmt::format;
use tracing_subscriber::fmt::FmtContext;
use tracing_subscriber::fmt::FormatEvent;
use tracing_subscriber::fmt::FormatFields;
use tracing_subscriber::fmt::FormattedFields;
use tracing_subscriber::registry::LookupSpan;
use std::sync::Arc;
use std::sync::Mutex;
use crate::c_api::GLOBAL_MIZE;
use crate::config::ConfigOpt;
use crate::error::{IntoMizeResult, MizeResult};
use crate::instance::Mize;
use crate::item::{data_from_string, IntoItemData, ItemData};
use crate::mize_err;
use self::fsstore::FileStore;
pub mod fsstore;
pub mod logging;
#[cfg(target_family = "unix")]
mod unix_socket;
#[cfg(target_family = "unix")]
use crate::platform::os::unix_socket::UnixListener;
//mod web;
pub fn os_instance_init(mize: &mut Mize) -> MizeResult<()> {
// this is the code, that runs to initialize an Instance on a system with an os present.
// setup loging to stderr if enabled
if mize
.get_config("log.stderr.enable")
.unwrap_or(ItemData::new())
.value_bool()
.unwrap_or(false)
{
use tracing::Level;
let level_string = mize
.get_config("log.level")?
.value_string()
.unwrap_or("info".to_string());
let log_level = match level_string.to_lowercase().as_str() {
"info" => Level::INFO,
"debug" => Level::DEBUG,
"warn" | "warning" => Level::WARN,
"trace" => Level::TRACE,
"error" => Level::ERROR,
_ => Level::INFO, // the default log level
};
println!("enabling loggging to stderr with log_level: {}", log_level);
// builder
let builder = tracing_subscriber::fmt::Subscriber::builder();
let field_formatter = tracing_subscriber::fmt::format::debug_fn(|writer, field, value| {
if field.name() == "message" {
write!(writer, "{:?}", value)?;
} else {
//write!(writer, "{}={:?}", field, value)?;
}
Ok(())
});
builder
//.with_env_filter(my_env_filter)
.without_time()
.event_format(MyFormatter)
.fmt_fields(field_formatter)
.with_max_level(log_level)
.init();
}
////// if a config.store_path is set, upgrade to the filestore there
let store_path = match mize.get_config("store")?.value_string() {
Ok(path) => path,
Err(_e) => {
// the default store_path: $HOME/.mize
let home_dir = if let Some(dir) = std::env::home_dir() {
dir
} else {
return Err(mize_err!(
"could not get home_dir and store_path is not set in the config"
));
};
let store_path_buff = home_dir.join(".mize");
let store_path = store_path_buff.display().to_string();
mize.set_blocking(
"self/config/store_path",
store_path.clone().into_item_data(),
)?;
let store_path = mize.get("self/config/store_path")?.value_string()?;
println!("store_path: {}", store_path);
store_path
}
};
if FileStore::store_is_opened(store_path.to_owned(), mize)? {
// if the store is already opened, connect to the instance, that opened it and join
// it's namespace
#[cfg(target_family = "unix")]
{
info!("CONNECTING");
use crate::platform::os::unix_socket;
unix_socket::connect(mize, store_path.into())?;
return Ok(());
}
#[cfg(target_family = "windows")]
{
error!("CONNECTING... would connect, but that is not implemented on windows yet");
}
} else {
// else open it ourselves
let file_store = FileStore::new(store_path.as_str())?;
mize.migrate_to_store(Box::new(file_store))?;
let path = Path::new(&store_path).to_owned();
#[cfg(target_family = "unix")]
{
mize.add_conn_listener(UnixListener::new(path)?)?;
}
#[cfg(target_family = "windows")]
{
warn!("would add a Listener on a local socket, but that is not yet implemented for windows");
}
}
Ok(())
}
pub fn seconds_since_modification(path: &Path) -> MizeResult<u64> {
let metadata = fs::metadata(path)?;
let modified = metadata.modified()?;
let duration = modified.duration_since(UNIX_EPOCH)?;
Ok(duration.as_secs())
}
pub fn get_part_hash(
mize: &mut Mize,
name: &str,
options: ItemData,
) -> 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);
let mut name_parts = name.split(".");
if name_parts
.next()
.ok_or(mize_err!("no 0th element in the modName"))?
== "cross"
{
let system = name_parts
.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);
if mod_name == "" {
return Err(mize_err!("no 2nd element in the modName"));
}
selector_data.set_path("system", system)?;
selector_data.set_path("modName", mod_name)?;
} else {
selector_data.set_path("modName", name)?;
}
selector_data.merge(options);
// important to produce the same hash for the sama selector data
selector_data.sort_keys();
let selector_str = selector_data.to_json()?;
let mut hasher = Sha256::new();
hasher.update(selector_str.clone().as_bytes());
let result = hasher.finalize();
let mut hex: String = result
.iter()
.map(|b| format!("{:02x}", b).to_string())
.collect::<Vec<String>>()
.join("");
hex.truncate(32);
return Ok((hex, selector_str));
}
pub fn fetch_part(mize: &mut Mize, part_name: &str) -> MizeResult<String> {
let (part_hash, part_selector) = get_part_hash(mize, part_name, ItemData::new())?;
let part_url = "https://mize.works/mize/dist/{part_hash}-{part_name}.tar.gz";
let store_path = mize.get_config("store")?.value_string()?;
let parts_path = format!("{store_path}/parts");
let target_dir = format!("{parts_path}/{part_hash}-{part_name}");
let tmp_file_path_gz = format!("{parts_path}/{part_hash}-{part_name}.tar.gz");
let tmp_gz_file = if !PathBuf::from(tmp_file_path_gz.clone()).exists() {
let mut tmp_gz_file = File::create(&tmp_file_path_gz)?;
debug!("downloading part '{}' with hash: {}", part_name, part_hash);
let response = http_req::request::get(part_url, &mut tmp_gz_file)?;
let code = response.status_code();
if code != 200.into() {
return Err(mize_err!(
"failed to get part '{}' with hash '{}' and selector: {}",
part_name,
part_hash,
part_selector
)
.msg(format!(
"the http request to fetch the part returned a status code of: {}",
code
)));
}
tmp_gz_file
} else {
std::fs::File::open(tmp_file_path_gz.clone())?
};
let tar = GzDecoder::new(std::io::BufReader::new(tmp_gz_file));
let mut archive = Archive::new(tar);
//archive.set_mask(umask::Mode::parse("rwxrwxrwx")?.into());
std::fs::create_dir_all(target_dir.as_str())?;
archive.unpack(target_dir.as_str())?;
fs::remove_file(&tmp_file_path_gz)?;
Ok(format!("{parts_path}/{part_hash}-{part_name}"))
}
fn get_part_path(mize: &mut Mize, part_name: &str, _path: Option<String>) -> MizeResult<String> {
// check if $name.src is set
if let Ok(itm) = mize.get_config(format!("{part_name}.src").as_str()) {
let part_src = itm.value_string().map_err(|e| e.msg(format!("opt {part_name}.src was not a string")))?;
// will only work for rust for now
let part_path = format!("{part_src}/target/debug/libmize_part_{part_name}.so");
return Ok(part_path);
}
// check if $name.out is set
if let Ok(itm) = mize.get_config(format!("{part_name}.out").as_str()) {
let part_out = itm.value_string().map_err(|e| e.msg(format!("opt {part_name}.out was not a string")))?;
let part_path = format!("{part_out}/lib/libmize_part_{part_name}.so");
return Ok(part_path);
}
// check if we find the part inside mize.src
if let Ok(itm) = mize.get_config("mize.src") {
let mize_src = itm.value_string().map_err(|e| e.msg("opt mize.src was not a string"))?;
let part_path = format!("{mize_src}/target/debug/libmize_part_{part_name}.so");
return Ok(part_path);
}
// fetch the part from https://mize.works
let out_path = fetch_part(mize, part_name)?;
return Ok(format!("{out_path}/lib/libmize_part_{part_name}.so"));
//Err(mize_err!("part {part_name} not found"))
}
pub fn load_part(mize: &mut Mize, part_name: &str, path: Option<String>) -> MizeResult<()> {
let part_path = get_part_path(mize, part_name, path)?;
let lib = unsafe { libloading::Library::new(part_path)? };
unsafe {
if let Ok(rust_hack_func) = lib.get::<libloading::Symbol<unsafe extern "C" fn(Arc<Mutex<Mize>>)>>("mize_rust_hack_set_global".as_bytes()) {
rust_hack_func(GLOBAL_MIZE.get().unwrap().clone())
};
}
let func: libloading::Symbol<
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);
unsafe { func()? };
let mut cur_eval_part = mize.cur_eval_part.lock()?;
*cur_eval_part = None;
// dropping the lib, would (i suspect) free all memory, of the library's code, which would
// make the part's vtable point into empty memory
std::mem::forget(lib);
Ok(())
}
pub fn config_from_cli_args(matches: &ArgMatches) -> MizeResult<ItemData> {
let mut config = ItemData::new();
////// populate values from $MIZE_CONFIG_FILES
if let Ok(config_file_paths) = std::env::var("MIZE_CONFIG_FILES") {
for config_file_path in config_file_paths.split(":") {
println!("reading config file: {config_file_path}");
let content = std::fs::read_to_string(config_file_path)?;
let data = ItemData::from_toml(content.as_str())?;
config.merge(data);
}
}
////// load config from MIZE_CONFIG env var
match std::env::var("MIZE_CONFIG") {
Ok(config_string) => {
let data = data_from_string(config_string)?;
config.merge(data);
}
Err(var_err) => match var_err {
VarError::NotPresent => {
debug!("env var MIZE_CONFIG NOT present")
}
VarError::NotUnicode(_) => {
warn!("env var MIZE_CONFIG is not Unicode, so not reading it")
}
},
};
if let Some(config_file_path) = matches.get_one::<String>("config") {
config.merge(config_from_file(config_file_path.to_string())?);
trace!("config after --config arg: {}", config);
}
if let Some(config_string) = matches.get_one::<String>("opt") {
config.merge(data_from_string(config_string.to_string())?);
trace!("config after --opt arg: {}", config);
}
return Ok(config);
}
pub fn config_from_file(file_path: String) -> MizeResult<ItemData> {
let toml_string = fs::read_to_string(&file_path)
.mres_msg(format!("Could not read file {} as mize config", &file_path))?;
let item_data = ItemData::from_toml(toml_string.as_str())?;
return Ok(item_data);
}
struct MyFormatter;
impl<S, N> FormatEvent<S, N> for MyFormatter
where
S: tracing::Subscriber + for<'a> LookupSpan<'a>,
N: for<'a> FormatFields<'a> + 'static,
{
fn format_event(
&self,
ctx: &FmtContext<'_, S, N>,
mut writer: format::Writer<'_>,
event: &Event<'_>,
) -> fmt::Result {
// Format values from the event's's metadata:
let metadata = event.metadata();
// set format
//builder.format(|buf, record| match record.level() {});
let level_str = match metadata.level() {
&Level::TRACE => format!("[ {} ]", "TRACE".yellow()),
&Level::DEBUG => format!("[ {} ]", "DEBUG".green()),
&Level::INFO => format!("[ {} ]", "INFO".blue()),
&Level::WARN => format!("[ {} ]", "WARN".truecolor(245, 164, 66)),
&Level::ERROR => format!("[ {} ]", "ERROR".red()),
};
// with target
// write!(&mut writer, "{} {}: ", level_str, metadata.target())?;
write!(&mut writer, "{} ", level_str)?;
// Format all the spans in the event's span context.
if let Some(scope) = ctx.event_scope() {
for span in scope.from_root() {
write!(writer, "{}", span.name())?;
// `FormattedFields` is a formatted representation of the span's
// fields, which is stored in its extensions by the `fmt` layer's
// `new_span` method. The fields will have been formatted
// by the same field formatter that's provided to the event
// formatter in the `FmtContext`.
let ext = span.extensions();
let fields = &ext
.get::<FormattedFields<N>>()
.expect("will never be `None`");
// Skip formatting the fields if the span had no fields.
if !fields.is_empty() {
//write!(writer, "{{{}}}", fields)?;
}
write!(writer, ": ")?;
}
}
// Write fields on the event
ctx.field_format().format_fields(writer.by_ref(), event)?;
writeln!(writer);
Ok(())
}
}
// thanks: https://stackoverflow.com/a/75546824
pub struct StringVisitor<'a> {
string: &'a mut String,
}
impl<'a> StringVisitor<'a> {
pub(crate) fn new(string: &'a mut String) -> Self {
StringVisitor { string }
}
}
impl<'a> Visit for StringVisitor<'a> {
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
if field.name() == "message" {
write!(self.string, "{value:?} ").unwrap();
} else {
write!(self.string, "{} = {:?}; ", field.name(), value).unwrap();
}
}
}