changes saved in my editor
This commit is contained in:
14
AGENTS.md
14
AGENTS.md
@@ -23,9 +23,13 @@ All code written for, with or as part as mize (except for the small core) is wri
|
|||||||
|
|
||||||
To understand the main components of the architecture you can refer to `docs/architecture.md`.
|
To understand the main components of the architecture you can refer to `docs/architecture.md`.
|
||||||
|
|
||||||
## Require clarification and plan approval before making code changes
|
Any proc macros should go into `./macros`.
|
||||||
|
the core mize library is in `./core`
|
||||||
|
any mize parts are in `./parts` or in their own repositories.
|
||||||
|
|
||||||
Before making any code changes other than the changelog, you must follow this two-step process:
|
## Require clarification and plan approval before making architectural code changes
|
||||||
|
|
||||||
|
Before making any code changes which could be considered to influence the architekture of the application, you must follow this two-step process:
|
||||||
|
|
||||||
### Step 1: Ask Clarifying Questions
|
### Step 1: Ask Clarifying Questions
|
||||||
|
|
||||||
@@ -34,6 +38,7 @@ Before making any code changes other than the changelog, you must follow this tw
|
|||||||
- Clarify any ambiguous requirements or edge cases
|
- Clarify any ambiguous requirements or edge cases
|
||||||
- Ask about preferred approaches if multiple solutions exist
|
- Ask about preferred approaches if multiple solutions exist
|
||||||
- Confirm the expected behavior and user experience
|
- Confirm the expected behavior and user experience
|
||||||
|
- Ask the clarifying questions by numbering them, so the user can number their replies.
|
||||||
|
|
||||||
### Step 2: Present Implementation Plan
|
### Step 2: Present Implementation Plan
|
||||||
|
|
||||||
@@ -45,6 +50,11 @@ Before making any code changes other than the changelog, you must follow this tw
|
|||||||
- Estimate the complexity and scope of changes
|
- Estimate the complexity and scope of changes
|
||||||
- **Wait for explicit user approval** before proceeding with any code changes
|
- **Wait for explicit user approval** before proceeding with any code changes
|
||||||
|
|
||||||
|
### What are considered architectural changes
|
||||||
|
TODO: add better examples.
|
||||||
|
|
||||||
|
eg: adding a seccond line displaying a different value from the DB is not considered architectural. as it was achievable by only modifying the existing function and rsx! code.
|
||||||
|
|
||||||
### Approval Requirements
|
### Approval Requirements
|
||||||
|
|
||||||
- User must explicitly approve the plan with words like "yes", "approved", "proceed", "go ahead", or similar
|
- User must explicitly approve the plan with words like "yes", "approved", "proceed", "go ahead", or similar
|
||||||
|
|||||||
128
core/src/config.rs
Normal file
128
core/src/config.rs
Normal 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
203
core/src/id.rs
Normal 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
869
core/src/instance/mod.rs
Normal 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
981
core/src/item.rs
Normal 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
470
core/src/platform/os/mod.rs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
697
parts/auth/src/client.rs
Normal file
697
parts/auth/src/client.rs
Normal file
@@ -0,0 +1,697 @@
|
|||||||
|
use chrono::{DateTime, TimeDelta, Utc};
|
||||||
|
use mize::config::OptVal;
|
||||||
|
use mize::config::thread;
|
||||||
|
use mize::include_native;
|
||||||
|
use mize::prelude::*;
|
||||||
|
use mize::out;
|
||||||
|
use mize::{
|
||||||
|
item::ItemData, mize_err, mize_part, Mize, MizeError, MizePart, MizePartCreate, MizeResult,
|
||||||
|
};
|
||||||
|
use openidconnect::{
|
||||||
|
ClientId, DeviceAuthorizationResponse, EmptyExtraDeviceAuthorizationFields, IssuerUrl,
|
||||||
|
OAuth2TokenResponse, RefreshToken, Scope, TokenResponse,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::{path::PathBuf, thread};
|
||||||
|
use tracing::{info, warn};
|
||||||
|
use mize_part_cli::CliPartState;
|
||||||
|
|
||||||
|
// console_log macro
|
||||||
|
// that can be copied into other files for debugging purposes
|
||||||
|
#[cfg(feature = "target-obsidian")]
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "target-obsidian")]
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
#[wasm_bindgen(js_namespace = console)]
|
||||||
|
fn log(s: &str);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "target-obsidian")]
|
||||||
|
macro_rules! console_log {
|
||||||
|
// Note that this is using the `log` function imported above during
|
||||||
|
// `bare_bones`
|
||||||
|
($($t:tt)*) => (unsafe { log(&format_args!($($t)*).to_string())})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "target-os")]
|
||||||
|
use mize_part_cli::clap::Command;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
#[mize_part]
|
||||||
|
pub struct ClientAuth {
|
||||||
|
mize: Mize,
|
||||||
|
secrets: Option<ClientSecrets>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct ClientSecrets {
|
||||||
|
id_token: String,
|
||||||
|
access_token: String,
|
||||||
|
refresh_token: String,
|
||||||
|
expiry: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MizePartCreate for ClientAuth {}
|
||||||
|
|
||||||
|
impl MizePart for ClientAuth {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"client_auth"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "target-js")]
|
||||||
|
pub fn store_secrets_js(mize: &Mize, secrets: &ClientSecrets) -> MizeResult<()> {
|
||||||
|
use web_sys::window;
|
||||||
|
|
||||||
|
// Serialize struct to JSON string
|
||||||
|
let serialized = serde_json::to_string(&secrets)?;
|
||||||
|
|
||||||
|
// Get browser window object
|
||||||
|
let window = window().ok_or_else(|| mize_err!("No global window found"))?;
|
||||||
|
|
||||||
|
// Access local storage
|
||||||
|
let storage = window
|
||||||
|
.local_storage().map_err(|_| mize_err!("couldn't get local_storage"))?
|
||||||
|
.ok_or_else(|| mize_err!("LocalStorage is not available"))?;
|
||||||
|
|
||||||
|
// Store the JSON string
|
||||||
|
storage.set_item("mize_client_secrets", &serialized).map_err(|_| mize_err!("couldn't set item in local_storage"))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl ClientAuth {
|
||||||
|
pub fn secrets(&self) -> Option<&ClientSecrets> {
|
||||||
|
self.secrets.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id_token(&self) -> MizeResult<&str> {
|
||||||
|
if self.secrets.is_none() {
|
||||||
|
return Err(mize_err!("Client not logged in"));
|
||||||
|
}
|
||||||
|
Ok(&self.secrets.as_ref().unwrap().id_token)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn access_token(&self) -> MizeResult<&str> {
|
||||||
|
if self.secrets.is_none() {
|
||||||
|
return Err(mize_err!("Client not logged in"));
|
||||||
|
}
|
||||||
|
Ok(&self.secrets.as_ref().unwrap().access_token)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh_token(&self) -> MizeResult<&str> {
|
||||||
|
if self.secrets.is_none() {
|
||||||
|
return Err(mize_err!("Client not logged in"));
|
||||||
|
}
|
||||||
|
Ok(&self.secrets.as_ref().unwrap().refresh_token)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh(&mut self) -> MizeResult<()> {
|
||||||
|
#[cfg(feature = "target-js")] {
|
||||||
|
console_log!("refresh is not implemented yet on wasm");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "target-os")] {
|
||||||
|
let secrets = self.secrets.clone().ok_or(mize_err!(
|
||||||
|
"no client secrets, meaning client_auth.refresh() thread can't use the refresh token"
|
||||||
|
))?;
|
||||||
|
let new_secrets = self.mize.spawn_async_blocking(
|
||||||
|
"auth.refresh_flow",
|
||||||
|
refresh_flow(self.mize.clone(), secrets),
|
||||||
|
)?;
|
||||||
|
store_secrets(&mut self.mize, &new_secrets)?;
|
||||||
|
self.secrets = Some(new_secrets);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn login(&mut self) -> MizeResult<()> {
|
||||||
|
#[cfg(feature = "target-os")] {
|
||||||
|
if self.secrets().is_none() {
|
||||||
|
let secrets = self
|
||||||
|
.mize
|
||||||
|
.spawn_async_blocking("auth.login_flow", auth_flow(self.mize.clone()))?;
|
||||||
|
store_secrets(&mut self.mize, &secrets)?;
|
||||||
|
self.secrets = Some(secrets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature = "target-js")] {
|
||||||
|
if self.secrets().is_none() {
|
||||||
|
let mut mize_clone = self.mize.clone();
|
||||||
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
|
console_log!("before auth_flow");
|
||||||
|
let secrets = match auth_flow(mize_clone.clone()).await {
|
||||||
|
Ok(secrets) => secrets,
|
||||||
|
Err(e) => {
|
||||||
|
mize_clone.report_err(e);
|
||||||
|
return ();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
console_log!("after auth_flow");
|
||||||
|
if let Err(e) = store_secrets_js(&mut mize_clone, &secrets) {
|
||||||
|
mize_clone.report_err(e);
|
||||||
|
return ();
|
||||||
|
}
|
||||||
|
let mut auth = match mize_clone.get_part_native::<ClientAuth>("client_auth") {
|
||||||
|
Ok(auth) => auth,
|
||||||
|
Err(e) => {
|
||||||
|
mize_clone.report_err(e);
|
||||||
|
return ();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auth.secrets = Some(secrets);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn part_auth() -> MizeResult<()> {
|
||||||
|
|
||||||
|
opt("cred")
|
||||||
|
.fun(|| {
|
||||||
|
let mize = get_global_mize()?;
|
||||||
|
let secrets = do_login()?;
|
||||||
|
ItemData::from_serializable(secrets)
|
||||||
|
});
|
||||||
|
|
||||||
|
let mize = get_global_mize()?;
|
||||||
|
|
||||||
|
// start refresh tread if configured
|
||||||
|
if mize
|
||||||
|
.get_config("auth.client.refresh_thread")
|
||||||
|
.unwrap_or(ItemData::new())
|
||||||
|
.value_bool()
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
thread("auth.refresh_thread", start_refresh_thread());
|
||||||
|
}
|
||||||
|
|
||||||
|
let cli = get_part_gen::<generated::CliPart>("cli");
|
||||||
|
|
||||||
|
|
||||||
|
// cli command for os
|
||||||
|
#[cfg(feature = "target-os")] {
|
||||||
|
println!("hasdfaslkjfasldkfj");
|
||||||
|
let cli_dyn = mize.get_part("cli")?;
|
||||||
|
println!("hasdfaslkjfasldkfj three ");
|
||||||
|
let mut cli = mize_part_cli::downcast_cli_part(cli_dyn)?;
|
||||||
|
println!("hasdfaslkjfasldkfj two ");
|
||||||
|
//let mut cli = mize.get_part_native::<CliPart>("cli")?;
|
||||||
|
cli.set_command("ppc");
|
||||||
|
cli.subcommand(Command::new("login"), move |_, mut mize| {
|
||||||
|
let mut auth = mize.get_part_native::<ClientAuth>("client_auth")?;
|
||||||
|
let secrets = if auth.secrets.is_some() {
|
||||||
|
match mize.spawn_async_blocking(
|
||||||
|
"client_auth",
|
||||||
|
refresh_flow(mize.clone(), auth.secrets.clone().unwrap()),
|
||||||
|
) {
|
||||||
|
Ok(secrets) => secrets,
|
||||||
|
Err(err) => {
|
||||||
|
out!("err refreshing secrets: {:?}", err);
|
||||||
|
mize.spawn_async_blocking("client_auth", auth_flow(mize_clone.clone()))?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mize.spawn_async_blocking("client_auth", auth_flow(mize_clone.clone()))?
|
||||||
|
};
|
||||||
|
store_secrets(&mut mize, &secrets)?;
|
||||||
|
auth.secrets = Some(secrets);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// register command for obsidian
|
||||||
|
#[cfg(feature = "target-obsidian")] {
|
||||||
|
use crate::MartsCommand;
|
||||||
|
console_log!("register obsidian command...");
|
||||||
|
|
||||||
|
let obsidian = mize.get_part_native::<crate::ObsidianPart>("obsidian")?;
|
||||||
|
obsidian.add_command(MartsCommand {
|
||||||
|
id: "mize-login".to_string(),
|
||||||
|
name: "Mize Login".to_string(),
|
||||||
|
callback: Box::new(|mut mize| {
|
||||||
|
let mut auth = mize.get_part_native::<crate::ClientAuth>("client_auth")?;
|
||||||
|
let res = auth.login();
|
||||||
|
console_log!("auth.login done: {:?}", res);
|
||||||
|
res
|
||||||
|
}),
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
auth_part.secrets = secrets;
|
||||||
|
|
||||||
|
mize.register_part(Box::new(auth_part))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_refresh_thread() -> MizeResult<()> {
|
||||||
|
|
||||||
|
// background thread for os targets
|
||||||
|
#[cfg(feature = "target-os")] {
|
||||||
|
let mut mize_clone = self.mize.clone();
|
||||||
|
self.mize.spawn_background("auth.refresh", move || loop {
|
||||||
|
info!("hi from auth.refresh thread...");
|
||||||
|
thread::sleep(Duration::from_secs(33)); // TERRIBLE hack
|
||||||
|
let auth = mize_clone.get_part_native::<ClientAuth>("client_auth")?;
|
||||||
|
let expiry = auth.secrets.as_ref().unwrap().expiry.clone();
|
||||||
|
info!("auth.refresh thread: now: {}", Utc::now());
|
||||||
|
info!("auth.refresh thread: expiry: {}", expiry);
|
||||||
|
let to_sleep = expiry - Utc::now() - TimeDelta::minutes(20);
|
||||||
|
/*
|
||||||
|
if to_sleep < TimeDelta::from_std(Duration::from_secs(0))? {
|
||||||
|
if let Err(e) = auth.refresh() {
|
||||||
|
e.log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
drop(auth);
|
||||||
|
|
||||||
|
info!("auth.refresh: thread going to sleep for: {}", to_sleep);
|
||||||
|
thread::sleep(to_sleep.to_std()?);
|
||||||
|
|
||||||
|
info!("refresh_thread: sleeping done");
|
||||||
|
let mut auth = mize_clone.get_part_native::<ClientAuth>("auth")?;
|
||||||
|
auth.refresh()?;
|
||||||
|
drop(auth);
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// spawn an async background task doing the refresh
|
||||||
|
#[cfg(feature = "target-js")] {
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
use wasm_bindgen_futures::JsFuture;
|
||||||
|
|
||||||
|
// Get the window object
|
||||||
|
let window = web_sys::window().expect("no global `window` exists");
|
||||||
|
|
||||||
|
// Clone mize for the closure
|
||||||
|
let mize_clone = self.mize.clone();
|
||||||
|
|
||||||
|
// Create a closure that will be called periodically
|
||||||
|
let refresh_closure = Closure::wrap(Box::new(move || {
|
||||||
|
let mut mize_clone = mize_clone.clone();
|
||||||
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
|
let mut auth = mize_clone.get_part_native::<ClientAuth>("client_auth");
|
||||||
|
match auth {
|
||||||
|
Ok(mut auth_part) => {
|
||||||
|
if let Some(secrets) = auth_part.secrets() {
|
||||||
|
let now = Utc::now();
|
||||||
|
let expiry = secrets.expiry;
|
||||||
|
let time_until_expiry = expiry - now;
|
||||||
|
|
||||||
|
// Refresh if token will expire in less than 20 minutes
|
||||||
|
if time_until_expiry < TimeDelta::minutes(20) {
|
||||||
|
if let Err(e) = auth_part.refresh() {
|
||||||
|
console_log!("Error refreshing token: {:?}", e);
|
||||||
|
} else {
|
||||||
|
console_log!("Token refreshed successfully");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
console_log!("Error getting auth part: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}) as Box<dyn FnMut()>);
|
||||||
|
|
||||||
|
// Set up interval to run every 50 minutes (300000 ms)
|
||||||
|
let interval_ms = 3000000;
|
||||||
|
let _ = window.set_interval_with_callback_and_timeout_and_arguments_0(
|
||||||
|
refresh_closure.as_ref().unchecked_ref(),
|
||||||
|
interval_ms,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Leak the closure to prevent it from being dropped
|
||||||
|
refresh_closure.forget();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_login() -> MizeResult<ClientSecrets> {
|
||||||
|
let mut mize = get_global_mize()?;
|
||||||
|
let mut secrets;
|
||||||
|
if let Ok(val) = load_secrets(&mut mize) {
|
||||||
|
if val.clone().unwrap().expiry < Utc::now() {
|
||||||
|
// refresh if secrets are expired
|
||||||
|
secrets = mize.spawn_async_blocking("auth.refresh", refresh_flow(mize, val))?;
|
||||||
|
} else {
|
||||||
|
secrets = val;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
secrets = mize.spawn_async_blocking("auth.login", auth_flow(mize))?;
|
||||||
|
}
|
||||||
|
Ok(secrets)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_secrets(mize: &mut Mize) -> MizeResult<ClientSecrets> {
|
||||||
|
#[cfg(feature = "target-os")] {
|
||||||
|
let mize_dir = mize.get_config("data_dir")?.value_string()?;
|
||||||
|
let secrets_file_path = PathBuf::from(mize_dir).join("client_secrets.json");
|
||||||
|
|
||||||
|
let secrets_string = std::fs::read_to_string(secrets_file_path)?;
|
||||||
|
let secrets: ClientSecrets = serde_json::from_str(&secrets_string)?;
|
||||||
|
return Ok(secrets);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "target-js")] {
|
||||||
|
use web_sys::window;
|
||||||
|
|
||||||
|
// Get browser window object
|
||||||
|
let window = window().ok_or_else(|| mize_err!("No global window found"))?;
|
||||||
|
|
||||||
|
// Access local storage
|
||||||
|
let storage = window
|
||||||
|
.local_storage().map_err(|_| mize_err!("couldn't get local_storage"))?
|
||||||
|
.ok_or_else(|| mize_err!("LocalStorage is not available"))?;
|
||||||
|
|
||||||
|
// Retrieve the string item
|
||||||
|
return match storage.get_item("mize_client_secrets").map_err(|_| mize_err!("couldn't get item from local_storage"))? {
|
||||||
|
Some(json_str) => {
|
||||||
|
// Deserialize JSON string back into Client struct
|
||||||
|
let secrets: ClientSecrets = serde_json::from_str(json_str.as_str())?;
|
||||||
|
Ok(secrets)
|
||||||
|
}
|
||||||
|
None => Err(mize_err!("no secrets")), // Key does not exist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store_secrets(mize: &mut Mize, secrets: &ClientSecrets) -> MizeResult<()> {
|
||||||
|
|
||||||
|
#[cfg(feature = "target-os")] {
|
||||||
|
let mize_dir = mize.get_config("store")?.value_string()?;
|
||||||
|
let secrets_file_path = PathBuf::from(mize_dir.clone())
|
||||||
|
.join("misc")
|
||||||
|
.join("client_secrets.json");
|
||||||
|
std::fs::create_dir_all(PathBuf::from(mize_dir).join("misc"))?;
|
||||||
|
|
||||||
|
let secrets_string = serde_json::to_string(secrets)?;
|
||||||
|
tracing::info!("secrets_file_path: {}", secrets_file_path.display());
|
||||||
|
//std::fs::write(secrets_file_path, secrets_string)?;
|
||||||
|
OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.open(secrets_file_path)?
|
||||||
|
.write_all(&secrets_string.as_bytes())?;
|
||||||
|
|
||||||
|
// update the spacetimedb_token in XDG_CONFIG_HOME/spacetime/cli.toml config file if configured to do so
|
||||||
|
if mize
|
||||||
|
.get_config("auth.client.update_spacetime_token")
|
||||||
|
.unwrap_or(ItemData::empty())
|
||||||
|
.as_bool()
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
update_spacetimedb_token(&secrets.id_token.as_str())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
use openidconnect::reqwest::Client as HttpClient;
|
||||||
|
use openidconnect::DeviceAuthorizationUrl;
|
||||||
|
use std::time::Duration;
|
||||||
|
//use wasm_bindgen::JsCast;
|
||||||
|
|
||||||
|
|
||||||
|
async fn refresh_flow(mut mize: Mize, old_secrets: ClientSecrets) -> MizeResult<ClientSecrets> {
|
||||||
|
out!("Refresh flow...");
|
||||||
|
let issuer = mize.get_config("auth.client.issuer")?.value_string()?;
|
||||||
|
let client_id = mize.get_config("auth.client.client_id")?.value_string()?;
|
||||||
|
|
||||||
|
let issuer = IssuerUrl::new(issuer)?;
|
||||||
|
let client_id = ClientId::new(client_id);
|
||||||
|
|
||||||
|
let http_client = HttpClient::new();
|
||||||
|
|
||||||
|
// Teach openidconnect-rs about the device authorization url.
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
struct MyExtraProviderMetadata {
|
||||||
|
device_authorization_endpoint: String,
|
||||||
|
}
|
||||||
|
use openidconnect::core::{
|
||||||
|
CoreAuthDisplay, CoreClaimName, CoreClaimType, CoreClient, CoreClientAuthMethod,
|
||||||
|
CoreGrantType, CoreJsonWebKey, CoreJweContentEncryptionAlgorithm,
|
||||||
|
CoreJweKeyManagementAlgorithm, CoreResponseMode, CoreResponseType,
|
||||||
|
CoreSubjectIdentifierType,
|
||||||
|
};
|
||||||
|
use openidconnect::AdditionalProviderMetadata;
|
||||||
|
use openidconnect::ProviderMetadata;
|
||||||
|
impl AdditionalProviderMetadata for MyExtraProviderMetadata {}
|
||||||
|
type MyProviderMetadata = ProviderMetadata<
|
||||||
|
MyExtraProviderMetadata,
|
||||||
|
CoreAuthDisplay,
|
||||||
|
CoreClientAuthMethod,
|
||||||
|
CoreClaimName,
|
||||||
|
CoreClaimType,
|
||||||
|
CoreGrantType,
|
||||||
|
CoreJweContentEncryptionAlgorithm,
|
||||||
|
CoreJweKeyManagementAlgorithm,
|
||||||
|
CoreJsonWebKey,
|
||||||
|
CoreResponseMode,
|
||||||
|
CoreResponseType,
|
||||||
|
CoreSubjectIdentifierType,
|
||||||
|
>;
|
||||||
|
|
||||||
|
// Discover provider metadata
|
||||||
|
let provider_metadata: MyProviderMetadata =
|
||||||
|
MyProviderMetadata::discover_async(issuer, &http_client).await?;
|
||||||
|
|
||||||
|
let device_auth_url = provider_metadata
|
||||||
|
.additional_metadata()
|
||||||
|
.device_authorization_endpoint
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
let client = CoreClient::from_provider_metadata(
|
||||||
|
provider_metadata,
|
||||||
|
client_id,
|
||||||
|
None, // public client
|
||||||
|
)
|
||||||
|
.set_device_authorization_url(DeviceAuthorizationUrl::new(device_auth_url)?);
|
||||||
|
|
||||||
|
let refresh_token = RefreshToken::new(old_secrets.refresh_token);
|
||||||
|
let res = client
|
||||||
|
.exchange_refresh_token(&refresh_token)?
|
||||||
|
.request_async(&http_client)
|
||||||
|
.await?;
|
||||||
|
let expires_in = res.expires_in().ok_or(mize_err!(
|
||||||
|
"no expiry information was in token response of the refresh flow"
|
||||||
|
))?;
|
||||||
|
|
||||||
|
return Ok(ClientSecrets {
|
||||||
|
id_token: res.id_token().unwrap().to_string(),
|
||||||
|
access_token: res.access_token().secret().to_owned(),
|
||||||
|
refresh_token: res.refresh_token().unwrap().secret().to_owned(),
|
||||||
|
expiry: Utc::now() + expires_in,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn auth_flow(mut mize: Mize) -> MizeResult<ClientSecrets> {
|
||||||
|
let issuer = mize.get_config("auth.client.issuer")?.value_string()?;
|
||||||
|
let client_id = mize.get_config("auth.client.client_id")?.value_string()?;
|
||||||
|
|
||||||
|
let issuer = IssuerUrl::new(issuer)?;
|
||||||
|
let client_id = ClientId::new(client_id);
|
||||||
|
|
||||||
|
let http_client = HttpClient::new();
|
||||||
|
|
||||||
|
// Teach openidconnect-rs about the device authorization url.
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
struct MyExtraProviderMetadata {
|
||||||
|
device_authorization_endpoint: String,
|
||||||
|
}
|
||||||
|
use openidconnect::core::{
|
||||||
|
CoreAuthDisplay, CoreClaimName, CoreClaimType, CoreClient, CoreClientAuthMethod,
|
||||||
|
CoreGrantType, CoreJsonWebKey, CoreJweContentEncryptionAlgorithm,
|
||||||
|
CoreJweKeyManagementAlgorithm, CoreResponseMode, CoreResponseType,
|
||||||
|
CoreSubjectIdentifierType,
|
||||||
|
};
|
||||||
|
use openidconnect::AdditionalProviderMetadata;
|
||||||
|
use openidconnect::ProviderMetadata;
|
||||||
|
impl AdditionalProviderMetadata for MyExtraProviderMetadata {}
|
||||||
|
type MyProviderMetadata = ProviderMetadata<
|
||||||
|
MyExtraProviderMetadata,
|
||||||
|
CoreAuthDisplay,
|
||||||
|
CoreClientAuthMethod,
|
||||||
|
CoreClaimName,
|
||||||
|
CoreClaimType,
|
||||||
|
CoreGrantType,
|
||||||
|
CoreJweContentEncryptionAlgorithm,
|
||||||
|
CoreJweKeyManagementAlgorithm,
|
||||||
|
CoreJsonWebKey,
|
||||||
|
CoreResponseMode,
|
||||||
|
CoreResponseType,
|
||||||
|
CoreSubjectIdentifierType,
|
||||||
|
>;
|
||||||
|
|
||||||
|
// Discover provider metadata
|
||||||
|
let provider_metadata: MyProviderMetadata =
|
||||||
|
MyProviderMetadata::discover_async(issuer, &http_client).await?;
|
||||||
|
|
||||||
|
let device_auth_url = provider_metadata
|
||||||
|
.additional_metadata()
|
||||||
|
.device_authorization_endpoint
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
let client = CoreClient::from_provider_metadata(
|
||||||
|
provider_metadata,
|
||||||
|
client_id,
|
||||||
|
None, // public client
|
||||||
|
)
|
||||||
|
.set_device_authorization_url(DeviceAuthorizationUrl::new(device_auth_url)?);
|
||||||
|
|
||||||
|
// ===== DEVICE AUTH REQUEST =====
|
||||||
|
let details: Result<DeviceAuthorizationResponse<EmptyExtraDeviceAuthorizationFields>, _> =
|
||||||
|
client
|
||||||
|
.exchange_device_code()
|
||||||
|
.add_scope(Scope::new("openid".into()))
|
||||||
|
.add_scope(Scope::new("profile".into()))
|
||||||
|
.add_scope(Scope::new("offline_access".into()))
|
||||||
|
.request_async(&http_client)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let details = match details {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(err) => {
|
||||||
|
out!("err: {:?}", err);
|
||||||
|
out!("err: {}", err);
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "target-obsidian")] {
|
||||||
|
console_log!("details: {:?}", details);
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use js_sys::{Array, Function, JsString, Reflect};
|
||||||
|
use mize::error::IntoMizeResultJsValue;
|
||||||
|
use wasm_bindgen::convert::TryFromJsValue;
|
||||||
|
|
||||||
|
let obsidian = mize.get_part_native::<crate::ObsidianPart>("obsidian")?;
|
||||||
|
let show_device_code = Reflect::get(&obsidian.plugin(), &JsValue::from_str("showDeviceCode")).mres()?;
|
||||||
|
let args = Array::new();
|
||||||
|
args.push(&JsValue::from_str(details.user_code().secret().as_str()));
|
||||||
|
args.push(&JsValue::from_str(details.verification_uri_complete().unwrap().secret().as_str()));
|
||||||
|
|
||||||
|
Reflect::apply(&show_device_code.dyn_into::<Function>().mres()?, obsidian.plugin(), &args).mres()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "target-os")] {
|
||||||
|
out!(
|
||||||
|
"If your browser does not open automatically, open this URL in it:\n{}\n",
|
||||||
|
details.verification_uri_complete().unwrap().secret()
|
||||||
|
);
|
||||||
|
out!(
|
||||||
|
"Or enter this PPC login code:\n{}\n",
|
||||||
|
details.user_code().secret()
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(_) = details.verification_uri_complete() {
|
||||||
|
let _ = webbrowser::open(details.verification_uri_complete().unwrap().secret());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let token_response = client
|
||||||
|
.exchange_device_access_token(&details)?
|
||||||
|
.request_async(
|
||||||
|
&http_client,
|
||||||
|
async |dur: Duration| {
|
||||||
|
#[cfg(feature = "target-os")] {
|
||||||
|
tokio::time::sleep(dur).await;
|
||||||
|
}
|
||||||
|
#[cfg(feature = "target-js")] {
|
||||||
|
console_log!("sleeping");
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
use wasm_bindgen_futures::JsFuture;
|
||||||
|
let promise = js_sys::Promise::new(&mut |resolve, _| {
|
||||||
|
web_sys::window()
|
||||||
|
.unwrap()
|
||||||
|
.set_timeout_with_callback_and_timeout_and_arguments_0(&resolve, dur.as_millis() as i32)
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Await the JS Promise like a Rust Future
|
||||||
|
let _ = JsFuture::from(promise).await;
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
|
||||||
|
// ===== TOKENS =====
|
||||||
|
let id_token = token_response
|
||||||
|
.extra_fields()
|
||||||
|
.id_token()
|
||||||
|
.ok_or(mize_err!("no id_token"))?;
|
||||||
|
|
||||||
|
let refresh = token_response
|
||||||
|
.refresh_token()
|
||||||
|
.ok_or(mize_err!("no refresh token present"))?;
|
||||||
|
|
||||||
|
let expires_in = token_response.expires_in().ok_or(mize_err!(
|
||||||
|
"no expiry information was in token response of the refresh flow"
|
||||||
|
))?;
|
||||||
|
|
||||||
|
Ok(ClientSecrets {
|
||||||
|
id_token: id_token.to_string(),
|
||||||
|
access_token: token_response.access_token().secret().to_string(),
|
||||||
|
refresh_token: refresh.secret().to_owned(),
|
||||||
|
expiry: Utc::now() + expires_in,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_spacetimedb_token(new_token: &str) -> MizeResult<()> {
|
||||||
|
// Resolve config directory
|
||||||
|
let config_dir = match std::env::var("XDG_CONFIG_HOME") {
|
||||||
|
Ok(val) => PathBuf::from(val),
|
||||||
|
Err(_) => {
|
||||||
|
let home = std::env::var("HOME")?;
|
||||||
|
std::path::PathBuf::from(home).join(".config")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let file_path = config_dir.join("spacetime").join("cli.toml");
|
||||||
|
|
||||||
|
if !file_path.exists() {
|
||||||
|
warn!("spacetime config file does not exist, not updating token there");
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Read existing file (or create empty document if it doesn't exist)
|
||||||
|
let content = std::fs::read_to_string(&file_path).unwrap_or_default();
|
||||||
|
let mut data = ItemData::from_toml(content.as_str())?;
|
||||||
|
|
||||||
|
// Update the field
|
||||||
|
data.set_path("spacetimedb_token", new_token)?;
|
||||||
|
|
||||||
|
// Write back
|
||||||
|
std::fs::write(&file_path, data.to_toml()?)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
10
parts/cli/src/lib.rs
Normal file
10
parts/cli/src/lib.rs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
use mize_macros::{include_native_simple, part_impl};
|
||||||
|
use clap::Command;
|
||||||
|
|
||||||
|
#[part_impl]
|
||||||
|
pub fn part() -> mize::Instance {
|
||||||
|
// Register the command with the new macro syntax
|
||||||
|
include_native_simple!("Command", clap::Command, [new]);
|
||||||
|
|
||||||
|
mize::Instance::new()
|
||||||
|
}
|
||||||
70
parts/mize.dev/src/generated.rs
Normal file
70
parts/mize.dev/src/generated.rs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
use mize::prelude::*;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct CliPart {
|
||||||
|
id: MizeId
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MizePart for CliPart {
|
||||||
|
fn id(&self) -> MizeId {
|
||||||
|
self.id.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CliPart {
|
||||||
|
pub fn get(name: &str) -> MizeResult<CliPart> {
|
||||||
|
let mut mize = get_global_mize()?;
|
||||||
|
let part = mize.get_part(name)?;
|
||||||
|
Ok(CliPart { id: part.id() })
|
||||||
|
}
|
||||||
|
pub fn run(&self, cmdline: Vec<String>) -> MizeResult<()> {
|
||||||
|
let cmdline = cmdline.into_item_data();
|
||||||
|
let mut args = ItemData::new();
|
||||||
|
args.set_path("0", cmdline)?;
|
||||||
|
dyn_call(self.id.clone(), "run", args)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn set_command(&self, name: &str) -> MizeResult<()> {
|
||||||
|
let name = name.into_item_data();
|
||||||
|
let mut args = ItemData::new();
|
||||||
|
args.set_path("0", name)?;
|
||||||
|
dyn_call(self.id.clone(), "set_command", args)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn subcommand<T: Fn(ArgMatches) -> MizeResult<()> + Send + Sync + 'static>(&self, subcmd: Command, action: T) -> MizeResult<()> {
|
||||||
|
let mut subcmd_arg = ItemData::new();
|
||||||
|
subcmd_arg.set_path("_native", subcmd.native_id)?;
|
||||||
|
let mut action_arg = ItemData::from_native(action)?;
|
||||||
|
let mut args = ItemData::new();
|
||||||
|
args.set_path("0", subcmd_arg)?;
|
||||||
|
args.set_path("1", action_arg)?;
|
||||||
|
dyn_call(self.id.clone(), "subcommand", args)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the clap::Command native stuff
|
||||||
|
pub struct Command {
|
||||||
|
native_id: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ArgMatches {
|
||||||
|
native_id: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Action {
|
||||||
|
native_id: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Command {
|
||||||
|
pub fn new(name: &str) -> MizeResult<Command> {
|
||||||
|
let name = name.into_item_data();
|
||||||
|
let mut args = ItemData::new();
|
||||||
|
args.set_path("0", name)?;
|
||||||
|
let mut mize = get_global_mize()?;
|
||||||
|
let id = mize.id_new_dots("cli.Command")?;
|
||||||
|
let data = dyn_call(id.clone(), "new", args)?;
|
||||||
|
let native_id: u32 = data.get_path("_native")?.native_take()?;
|
||||||
|
Ok(Command { native_id })
|
||||||
|
}
|
||||||
|
}
|
||||||
63
parts/mize.dev/src/lib.rs
Normal file
63
parts/mize.dev/src/lib.rs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
use std::fs::create_dir_all;
|
||||||
|
use mize::prelude::*;
|
||||||
|
use cmd_lib::run_cmd as sh;
|
||||||
|
use mize::tracing::info;
|
||||||
|
|
||||||
|
// a static version of what would be generated.
|
||||||
|
// as we need mize.dev to generate anything.
|
||||||
|
mod generated;
|
||||||
|
use generated::*;
|
||||||
|
|
||||||
|
// spacetime gen command: sh!(RUST_LOG=error RUST_BACKTRACE=0 spacetime generate --build-options="--lint-dir=" -p $SRC/mize/marts/spacetime -o $SRC/mize/marts/generated/src/spacetime --lang rust)?;
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
extern "C" fn part_dev() -> MizeResult<()> {
|
||||||
|
let mut mize = get_global_mize()?;
|
||||||
|
let mut cli = generated::CliPart::get("cli")?;
|
||||||
|
cli.set_command("dev");
|
||||||
|
|
||||||
|
cli.subcommand(Command::new("test")?, |_| {println!("hello from test command"); Ok(())})?;
|
||||||
|
//cli.subcommand(Command::new("publish").alias("p"), publish_mize)?;
|
||||||
|
//cli.subcommand(Command::new("genBindings").alias("b"), gen_bindings)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn publish_mize(_ : &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
||||||
|
let mize_src = mize.get_config("mize.src")?.value_string()?;
|
||||||
|
let cargo_dir = std::env::home_dir().unwrap().join(".cargo");
|
||||||
|
|
||||||
|
let targets = vec![
|
||||||
|
"x86_64-pc-windows-gnu",
|
||||||
|
"aarch64-pc-windows-gnullvm",
|
||||||
|
|
||||||
|
"x86_64-unknown-linux-gnu",
|
||||||
|
"aarch64-unknown-linux-gnu",
|
||||||
|
"armv7-unknown-linux-gnueabihf",
|
||||||
|
"arm-unknown-linux-gnueabi",
|
||||||
|
|
||||||
|
"aarch64-apple-darwin",
|
||||||
|
"x86_64-apple-darwin",
|
||||||
|
];
|
||||||
|
|
||||||
|
for target_name in targets {
|
||||||
|
let docker_image = match target_name {
|
||||||
|
"aarch64-apple-darwin" => "darwin",
|
||||||
|
"x86_64-apple-darwin" => "darwin",
|
||||||
|
_ => "base",
|
||||||
|
};
|
||||||
|
sh!(docker build -t mize-build-$docker_image $mize_src/parts/mize.dev/targets/$docker_image)?;
|
||||||
|
let res = sh!(docker run -it --rm -v $cargo_dir/registry:/usr/local/cargo/registry -v $cargo_dir/git:/usr/local/cargo/git -v $mize_src:/src -v $mize_src/target:/src/target mize-build-$docker_image cargo build --target $target_name --release);
|
||||||
|
if res.is_err() {
|
||||||
|
info!("build for {target_name} failed...");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gen_bindings(_ : &ArgMatches, mize: Mize) -> MizeResult<()> {
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user