more restored

This commit is contained in:
Sebastian Moser
2026-10-23 11:40:23 +02:00
parent 9e5ceaf0f9
commit 6bd98e7c80
10 changed files with 1452 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
//pub extern "C" fn

View File

@@ -55,6 +55,46 @@ pub use instance::{
pub use mize_macros::*;
use std::path::PathBuf;
use std::sync::{Arc, Mutex, OnceLock};
use crate::error::MizeResult;
use crate::instance::Mize;
use crate::mize_err;
/// Global Mize instance storage.
/// This is set once when the Mize instance is created and can be accessed from anywhere.
pub static GLOBAL_MIZE: OnceLock<Arc<Mutex<Mize>>> = OnceLock::new();
/// Get the global Mize instance.
/// Returns a clone of the Mize instance (which is cheap as it only clones Arcs internally).
pub fn get_global_mize() -> MizeResult<Mize> {
let arc = GLOBAL_MIZE
.get()
.ok_or_else(|| mize_err!("No global Mize instance set"))?;
let mize = arc
.lock()
.map_err(|_| mize_err!("Failed to lock global Mize"))?;
Ok(mize.clone())
}
/// Set the global Mize instance.
pub fn set_global_mize(mize: Mize) -> MizeResult<()> {
let arc = Arc::new(Mutex::new(mize));
if let Errr(_) = GLOBAL_MIZE.set(arc) {
let a = GLOBAL_MIZE.get().unwrap();
let mut b = a.lock();
*b = mize;
}
Ok(())
}
/// C API function to set the global Mize instance from a dynamically loaded part.
/// This is a hack to allow Rust parts loaded as .so files to access the global Mize instance.
#[unsafe(no_mangle)]
pub extern "C" fn mize_rust_hack_set_global(mize: Arc<Mutex<Mize>>) {
let _ = GLOBAL_MIZE.set(mize);
}
// platform specific stuff
pub mod platform {
use crate::{Mize, MizeResult};

View File

@@ -0,0 +1,8 @@
{
"tasks": {
"dev": "deno run --watch main.ts"
},
"imports": {
"@std/assert": "jsr:@std/assert@1"
}
}

View File

@@ -0,0 +1,30 @@
export async function init_mize(config) {
if ("mize" in window) {
window.mize.set("self/config", config)
return window.mize
}
let config_str = "";
if (typeof config == "string") {
config_str = config
} else if (typeof config == "object") {
config_str = JSON.stringify(config)
} else {
throw "Error: config passed to init_mize() is not of type 'object' or 'string'"
}
const { new_js_instance } = wasm_bindgen;
await wasm_bindgen(config.module_dir.mize + "/mize_bg.wasm")
window.mize = await new_js_instance(config_str)
window.mize.mod = {}
window.mize.init()
return window.mize
}

328
core/src/platform/js/mod.rs Normal file
View File

@@ -0,0 +1,328 @@
use crate::id::MizeId;
use crate::item::{Item, ItemData};
use crate::platform::js::js_sys::Function;
use core::convert::From;
use core::option::Option;
use std::panic;
use std::ptr::NonNull;
use web_sys::js_sys::{self, eval, Promise};
use web_sys::Worker;
use web_sys::{WorkerOptions, WorkerType};
use crate::core::item::IntoItemData;
use crate::error::MizeResult;
use crate::Mize;
use crate::MizeError;
use crate::mize_err;
// console_log macro
// that can be copied into other files for debugging purposes
#[cfg(feature = "target-js")]
use wasm_bindgen::prelude::*;
#[cfg(feature = "target-js")]
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
#[cfg(feature = "target-js")]
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(not(feature = "target-js"))]
macro_rules! console_log {
// Note that this is using the `log` function imported above during
// `bare_bones`
($($t:tt)*) => {};
}
//end of console_log macro
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
pub fn wasm_instance_init(mize: &mut Mize) -> MizeResult<()> {
console_log!("Hello world from wasm_instance_init!!!!!!!!!!");
#[cfg(feature = "target-obsidian")] {
////// populate values from $MIZE_CONFIG_FILES
if let Ok(config_file_paths) = obsidian_get_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 mut data = ItemData::from_toml(content.as_str())?;
crate::core::item::set_config_from_data(mize, data)?;
}
}
////// load config from MIZE_CONFIG env var
match std::env::var("MIZE_CONFIG") {
Ok(config_string) => {
let data = data_from_string(config_string)?;
crate::core::item::set_config_from_data(mize, 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")
}
},
};
}
Ok(())
}
#[cfg(feature = "target-obsidian")]
pub fn obsidian_get_env_var(name: &str) -> Option<String> {
use js_sys::{Reflect, Object};
// 1. Get the global `process` object
let global = js_sys::global();
let process = Reflect::get(&global, &JsValue::from_str("process")).ok()?;
if process.is_undefined() {
return None;
}
// 2. Get the `env` object from `process`
let env = Reflect::get(&process, &JsValue::from_str("env")).ok()?;
if env.is_undefined() {
return None;
}
// 3. Get the `MIZE_STORE` value from `env`
let mize_store = Reflect::get(&env, &JsValue::from_str("MIZE_STORE")).ok()?;
// 4. Convert the JsValue to a Rust String if it exists
mize_store.as_string()
}
pub fn load_module(mize: &mut Mize, name: &str, path: Option<String>) -> MizeResult<()> {
console_log!("loading module NOT IMPLEMENTED: {}", name);
//wasm_bindgen_futures::spawn_local(load_module_async(mize.clone(), name.to_owned(), path));
Ok(())
}
/*
async fn load_module_async(mut mize: Mize, name: String, path: Option<String>) -> () {
let name = name.as_str();
let module_url = fetch_module(&mut mize, name).unwrap();
let memory = wasm_bindgen::memory();
let mut empty_module: Box<dyn Module + Send + Sync> = Box::new(EmptyModule {});
let mut mize = mize.clone();
let empty_module_ptr = Box::into_raw(Box::new(empty_module));
let empty_module_usize = empty_module_ptr as usize;
let mize_usize = Box::into_raw(Box::new(mize)) as usize;
console_log!("testttttttttttttt in load_module_async...");
let function_str = format!(
r#"
const promise = new Promise((resolve, reject) => {{
import("{module_url}/mize_module_{name}.js").then( module => {{
const wasm_bindgen = module.get_wasm_bindgen();
const {{ wasm_get_mize_module_mme }} = wasm_bindgen;
wasm_bindgen("{module_url}/mize_module_{name}_bg.wasm", memory).then( () => {{
console.log("empty_module_usize:", empty_module_usize)
console.log("mize_usize:", mize_usize)
//resolve()
resolve(wasm_get_mize_module_mme(empty_module_usize, mize_usize))
}})
}})
}})
return promise;
"#
);
let function = Function::new_with_args("memory, empty_module_usize, mize_usize", &function_str);
let ret_value: JsValue = match function.call3(
&JsValue::null(),
&memory,
&empty_module_usize.into(),
&mize_usize.into(),
) {
Ok(val) => val,
Err(err) => {
console_log!("failed to load the wasm mize module....");
console_log!("{:?}", err);
return;
}
};
console_log!("got ret_value: {:?}", ret_value);
let ret_promise = Promise::from(ret_value);
let ret_result = match wasm_bindgen_futures::JsFuture::from(ret_promise).await {
Ok(val) => val,
Err(err) => {
console_log!("err awaiting ret_promise");
console_log!("{:?}", err);
return;
}
};
let mut filled_module = unsafe { Box::from_raw(empty_module_ptr) };
console_log!("before....");
//let a = mize.get("0/config");
console_log!("after....");
//filled_module.init(&instance);
/*
console_log!("got ret_result: {:?}", ret_result);
let ret_f64 = ret_result.as_f64().unwrap();
console_log!("got ret_f64: {:?}", ret_f64);
let ret_usize = ret_f64.floor() as usize;
let mut module: Box<dyn Module + Send + Sync> = Box::new(EmptyModule {});
unsafe {
let get_mize_module_fn_ptr = ret_usize as *const fn(&mut Box<dyn Module + Send + Sync>, Instance) -> ();
let get_mize_module_fn: fn(&mut Box<dyn Module + Send + Sync>, Instance) -> () = unsafe { std::mem::transmute(get_mize_module_fn_ptr) };
get_mize_module_fn(&mut module, instance.clone());
}
*/
}
*/
pub fn fetch_module(mize: &mut Mize, name: &str) -> MizeResult<String> {
let module_name_with_slashes = name.replace(".", "/");
if let Ok(module_path) = mize
.get(format!(
"self/config/module_dir/{}",
module_name_with_slashes
))?
.value_string()
{
return Ok(module_path);
} else {
let module_url = mize.get("self/config/module_url")?.value_string()?;
return Ok(format!("{}/{}", module_url, module_name_with_slashes));
}
}
async fn download_module(mize: &mut Mize, name: &str) -> MizeResult<js_sys::ArrayBuffer> {
use wasm_bindgen_futures::JsFuture;
use web_sys::{Request, RequestInit, RequestMode, Response};
let opts = RequestInit::new();
opts.set_method("GET");
opts.set_mode(RequestMode::Cors);
let url = format!("{}/mize_module_{}_bg.wasm", fetch_module(mize, name)?, name);
let request = Request::new_with_str_and_init(&url, &opts).unwrap();
let window = web_sys::window().unwrap();
let resp_value = JsFuture::from(window.fetch_with_request(&request))
.await
.unwrap();
let resp: Response = resp_value.dyn_into().unwrap();
let wasm_bytes: js_sys::ArrayBuffer = JsFuture::from(resp.array_buffer().unwrap())
.await
.unwrap()
.dyn_into()
.unwrap();
Ok(wasm_bytes)
}
#[wasm_bindgen]
#[derive(Clone)]
pub struct JsMize {
inner: NonNull<Mize>,
}
#[wasm_bindgen]
pub struct JsItem {
mize: NonNull<Mize>,
id: MizeId,
}
#[wasm_bindgen]
pub async fn new_js_instance(config_json_str: String) -> JsMize {
panic::set_hook(Box::new(console_error_panic_hook::hook));
let config = ItemData::from_json(config_json_str).expect("parsing of json config failed");
let mut mize = Mize::with_config(config).expect("Mize::new() failed");
let mut js_instance = JsMize {
inner: NonNull::from(Box::leak(Box::new(mize))),
};
return js_instance;
}
#[wasm_bindgen]
impl JsMize {
#[wasm_bindgen]
pub unsafe fn init(&mut self) -> MizeResult<()> {
self.inner.as_mut().init()
}
#[wasm_bindgen]
pub unsafe fn set(&mut self, id: String, value: String) -> () {
let data = value.into_item_data();
console_log!("data in set: {}", data);
self.inner.as_mut().set(id, data);
}
#[wasm_bindgen]
pub unsafe fn get_handle(&mut self, id: String) -> MizeResult<JsItem> {
let item = self.inner.as_mut().get(id)?;
Ok(JsItem {
mize: self.inner,
id: item.id(),
})
}
}
impl JsMize {
pub fn inner(&mut self) -> &mut Mize {
unsafe { self.inner.as_mut() }
}
}
#[wasm_bindgen]
impl JsItem {
#[wasm_bindgen]
pub unsafe fn value_string(&mut self) -> MizeResult<String> {
let item = self.mize.as_mut().get(self.id.clone())?;
let string = item.value_string()?;
Ok(string)
}
#[wasm_bindgen]
pub unsafe fn as_data_full(&mut self) -> MizeResult<JsValue> {
let item = self.mize.as_mut().get(self.id.clone())?;
let data_raw = item.as_data_full()?;
let jsvalue = serde_wasm_bindgen::to_value(data_raw.cbor())?;
Ok(jsvalue)
}
}
impl From<MizeError> for JsValue {
fn from(value: MizeError) -> Self {
let string = format!("{:?}", value);
string.into()
}
}

View File

View File

@@ -0,0 +1,218 @@
use ciborium::Value as CborValue;
use std::fs;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::{Path, PathBuf};
use sysinfo::{Pid, ProcessRefreshKind, RefreshKind, System};
use tracing::debug;
use crate::core::id::MizeId;
use crate::core::item::{Item, ItemData};
use crate::error::{IntoMizeResult, MizeError, MizeResult};
use crate::instance::store::IdIter;
use crate::instance::{self, Mize};
use crate::item::get_raw_from_cbor;
use crate::item::IntoItemData;
use crate::memstore::MemStore;
use crate::{core::instance::store::Store, mize_err};
use tokio::net::UnixStream;
#[derive(Clone, Debug)]
pub struct FileStore {
path: PathBuf,
}
impl FileStore {
pub fn new(path_str: &str) -> MizeResult<FileStore> {
let path = Path::new(path_str);
// create that path if it does not exist
fs::create_dir_all(Path::new(&path).join("store"))?;
// check for valid pid file
/*
if let Some(pid) = valid_pid_file(path)? {
// store already opened
return Err(mize_err!("MizeStore at path {} is already opened by process with pid {}", path.display(), pid));
} else {
// write our own pid file
fs::remove_file(path.join("pid"));
let pid = std::process::id();
let mut file = OpenOptions::new().write(true).create(true).open(path.join("pid"))?;
write!(file, "{}", pid)?;
}
*/
// init the store
if !path.join("next_id").exists() {
fs::write(path.join("next_id"), "1");
}
Ok(FileStore {
path: Path::new(&path).to_owned(),
})
}
pub fn store_is_opened(store_path: String, instance: &mut Mize) -> MizeResult<bool> {
// this was the old method....
//let valid_pid_file = valid_pid_file(Path::new(&store_path))?.is_some();
let can_connect_to_sock = match instance
.spawn_async_blocking("con_test_thread", try_connect_to_sock(store_path.clone()))
{
Ok(_) => true,
Err(_) => false,
};
debug!(
"MiZe Store at {} opened: {}",
store_path, can_connect_to_sock
);
Ok(can_connect_to_sock)
}
}
async fn try_connect_to_sock(store_path: String) -> MizeResult<()> {
match UnixStream::connect(PathBuf::from(store_path).join("sock")).await {
Err(_) => Err(mize_err!("")),
Ok(_) => Ok(()),
}
}
impl Store for FileStore {
fn new_id(&self) -> MizeResult<String> {
let mut next_id: u64 =
String::from_utf8(fs::read(self.path.join("next_id")).mres_msg(format!(
"could not read next_id at '{}'",
self.path.display()
))?)?
.parse()
.mres_msg(format!(
"could not parse next_id at '{}' to u64",
self.path.display()
))?;
let id_string = format!("{}", next_id);
next_id += 1;
fs::write(self.path.join("next_id"), format!("{}", next_id))?;
return Ok(id_string);
}
fn set(&self, id: MizeId, data: ItemData) -> MizeResult<()> {
let path = self
.path
.join("store")
.join(id.namespace_str())
.join(id.store_part());
fs::create_dir_all(self.path.join("store").join(id.namespace_str()))?;
let file = OpenOptions::new().write(true).create(true).open(path)?;
ciborium::into_writer(data.cbor(), file)?;
Ok(())
}
fn get_links(&self, item: Item) -> MizeResult<Vec<MizeId>> {
Ok(Vec::new())
}
fn get_backlinks(&self, item: Item) -> MizeResult<Vec<MizeId>> {
Ok(Vec::new())
}
fn get_value_raw(&self, id: MizeId) -> MizeResult<Vec<u8>> {
let path = self
.path
.join("store")
.join(id.namespace_str())
.join(id.store_part());
let file = OpenOptions::new().read(true).create(true).open(path)?;
let cbor_val: CborValue = ciborium::from_reader(file).mres_msg(format!(
"could not read file '{}' from FileStore",
self.path.join("store").join(id.store_part()).display()
))?;
let path = id.path();
let mut id_iter = path.iter();
id_iter.next();
let id_without_first = id_iter.collect();
Ok(get_raw_from_cbor(&cbor_val, id_without_first)?.to_owned())
}
fn get_value_data_full(&self, id: MizeId) -> MizeResult<ItemData> {
let file_path = self
.path
.join("store")
.join(id.namespace_str())
.join(id.store_part());
if !file_path.exists() {}
let file = OpenOptions::new()
.read(true)
.open(file_path.clone())
.map_err(|e| {
mize_err!(
"fsstoree: Unable to open '{}', err: {}",
file_path.display(),
e
)
})?;
let cbor_value: CborValue = ciborium::from_reader(file).mres_msg(format!(
"could not read file '{}' from FileStore",
self.path.join("store").join(id.store_part()).display()
))?;
let data: ItemData = cbor_value.into_item_data();
let tmp = id.path();
let mut path_iter = tmp.into_iter();
path_iter.next();
let new_path: Vec<String> = path_iter.map(|v| v.to_owned()).collect();
//println!("data: {}", data);
//println!("new_path: {:?}", new_path);
let ret_data = data.get_path(new_path)?;
//println!("ret_data: {}", ret_data);
return Ok(ret_data);
}
fn id_iter(&self) -> MizeResult<IdIter> {
todo!()
}
fn next_id(&self, prev_id: &str) -> MizeResult<Option<String>> {
todo!()
}
fn first_id(&self) -> MizeResult<String> {
Ok("0".to_owned())
}
}
fn valid_pid_file(path: &Path) -> MizeResult<Option<u32>> {
let pid_file_path = path.join("pid");
// if the pid file does not exist, there is no pid...
if !pid_file_path.exists() {
return Ok(None);
}
// read pid file
let pid: u32 = String::from_utf8(fs::read(&pid_file_path).mres_msg(format!(
"Could not read contents of pid file at '{}'",
pid_file_path.display()
))?)?
.parse()
.mres_msg(format!(
"Could not parse contents of pid file at '{}' to u32",
pid_file_path.display()
))?;
if Path::new("/proc").join(format!("{}", pid)).exists() {
return Ok(Some(pid));
} else {
return Ok(None);
}
}

View File

@@ -0,0 +1,353 @@
use clap::{crate_version, Arg, Command};
use clap::{ArgAction, ArgMatches};
use std::collections::HashMap;
use std::path::PathBuf;
use colored::Colorize;
use core::fmt;
use std::env;
use std::fmt::Write;
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;
use std::sync::Mutex;
use tokio::sync::mpsc;
use tracing::level_filters::LevelFilter;
use tracing::Event;
use tracing::{debug, error, info, trace, warn};
use tracing::{field::Visit, Id, Subscriber};
use tracing::{span_enabled, Instrument, Level};
use tracing_core::Field;
use tracing_subscriber::filter::EnvFilter;
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 crate::id::MizeId;
use crate::item::ItemData;
use crate::MizeError;
use crate::MizeResult;
static DEFAULT_LOG_LEVEL: LevelFilter = LevelFilter::WARN;
pub fn init_logger(cli_matches: &ArgMatches) {
println!("init_logger called");
// to save log messages, before the logger is setup
let mut log_messages: Vec<(Level, String)> = Vec::new();
log_messages.push((Level::INFO, "Starting Mize".to_owned()));
// builder with DEFAULT_LOG_LEVEL
let mut builder = tracing_subscriber::fmt::Subscriber::builder();
// set loglevel
let mut log_level = DEFAULT_LOG_LEVEL;
log_messages.push((
Level::TRACE,
format!(
"set log level from DEFAULT_LOG_LEVEL to {}",
DEFAULT_LOG_LEVEL
),
));
// check env RUST_LOG
let mut val_rust_log = level_from_env("RUST_LOG", log_level);
if let Some(level_rust_log) = val_rust_log.0 {
log_level = level_rust_log;
log_messages.push((
Level::TRACE,
format!(
"set log level from the Variable RUST_LOG to {}",
level_rust_log
),
));
}
log_messages.append(&mut val_rust_log.1);
// check env LOG
let mut val_mize_log = level_from_env("LOG", log_level);
if let Some(level_mize_log) = val_mize_log.0 {
println!("set log level from the Variable LOG to {}", level_mize_log);
log_level = level_mize_log;
log_messages.push((
Level::TRACE,
format!(
"set log level from the Variable MIZE_LOG to {}",
level_mize_log
),
));
}
log_messages.append(&mut val_mize_log.1);
// check env MIZE_LOG
let mut val_mize_log = level_from_env("MIZE_LOG", log_level);
if let Some(level_mize_log) = val_mize_log.0 {
log_level = level_mize_log;
log_messages.push((
Level::TRACE,
format!(
"set log level from the Variable MIZE_LOG to {}",
level_mize_log
),
));
}
log_messages.append(&mut val_mize_log.1);
// check -vvvvv flags
let v_flag_count = cli_matches.get_count("verbose");
if v_flag_count > 0 {
let default_log_level_as_num = match DEFAULT_LOG_LEVEL {
LevelFilter::OFF => 0,
LevelFilter::ERROR => 1,
LevelFilter::WARN => 2,
LevelFilter::INFO => 3,
LevelFilter::DEBUG => 4,
LevelFilter::TRACE => 5,
};
let new_log_level_num = default_log_level_as_num + v_flag_count;
log_level = match new_log_level_num {
0 => LevelFilter::OFF,
1 => LevelFilter::ERROR,
2 => LevelFilter::WARN,
3 => LevelFilter::INFO,
4 => LevelFilter::DEBUG,
5 => LevelFilter::TRACE,
_ => {
log_messages.push((
Level::TRACE,
format!(
"the number of verbose (-v) flags passed ({}) exeeds the maximumg log_level",
v_flag_count
),
));
LevelFilter::TRACE
}
};
log_messages.push((
Level::TRACE,
format!("set log level from the verbose flags to {}", log_level),
));
}
// check --log-level arg
if let Some(log_level_arg_string) = cli_matches.get_one::<String>("log-level") {
let log_level_from_arg = match log_level_arg_string.to_owned().to_lowercase().as_str() {
"none" => LevelFilter::OFF,
"trace" => LevelFilter::TRACE,
"debug" => LevelFilter::DEBUG,
"info" => LevelFilter::INFO,
"warn" => LevelFilter::WARN,
"error" => LevelFilter::ERROR,
_ => {
log_messages.push((Level::WARN, format!(
"The argument to --log-level \"{}\" is not a valid log-level. loglevel stays at: {}"
, log_level_arg_string
, log_level
)));
log_level
}
};
log_level = log_level_from_arg;
log_messages.push((
Level::TRACE,
format!(
"set log level from the --log-level arg to {}",
log_level_from_arg
),
));
}
// actually set the log_level on the builder
log_messages.push((Level::INFO, format!("loglevel is now {}", log_level)));
// filter loging
//let mut my_env_filter = tracing_subscriber::filter::EnvFilter::new("")
//.add_directive("[get_raw_from_cbor]=debug".parse().expect("error parsing log directive"))
//.add_directive("[MemStore::get_value_raw]=trace".parse().expect("error parsing log directive"))
//.add_directive("trace".parse().unwrap())
//.add_directive("[get_raw_from_cbor]=debug".parse().expect("error parsing log directive"))
//;
//MinimalTracer::register();
println!("log_level: {}", log_level);
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();
// write out all log messages from log_messages
for (level, msg) in log_messages {
match level {
Level::ERROR => error!("{}", msg),
Level::WARN => warn!("{}", msg),
Level::INFO => info!("{}", msg),
Level::DEBUG => debug!("{}", msg),
Level::TRACE => trace!("{}", msg),
}
}
}
fn level_from_env(
var_name: &str,
log_level: LevelFilter,
) -> (Option<LevelFilter>, Vec<(Level, String)>) {
let mut log_messages: Vec<(Level, String)> = Vec::new();
let var_value = env::var(var_name);
println!("checking env var: {}={:?}", var_name, var_value);
match var_value {
Ok(val) => {
// set loglevel acording to RUST_LOG
let level = match val.to_lowercase().as_str() {
"none" => LevelFilter::OFF,
"trace" => LevelFilter::TRACE,
"debug" => LevelFilter::DEBUG,
"info" => LevelFilter::INFO,
"warn" => LevelFilter::WARN,
"error" => LevelFilter::ERROR,
_ => {
log_messages.push((Level::WARN, format!(
"The value of Environment Variable {}: \"{}\" is not a valid log-level. loglevel stays at: {}"
, var_name
, val
, log_level
)));
log_level
}
};
return (Some(level), log_messages);
}
Err(env::VarError::NotPresent) => {}
Err(env::VarError::NotUnicode(os_string)) => {
let os_string_as_hex: String = os_string
.clone()
.into_encoded_bytes()
.iter()
.map(|b| format!("{:02x}", b).to_string())
.collect::<Vec<String>>()
.join("");
log_messages.push((Level::DEBUG, format!(
"value of Environment Variable {} was not a unicode valid string. Therefore ignoring it.
\n value with replacement char U+FFFD: {}
\n the WTF-8 raw bytes as hex: {}"
, var_name
, os_string.to_string_lossy(), os_string_as_hex
)));
}
};
return (None, log_messages);
}
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();
}
}
}
struct LogFilter {
target: Option<String>,
level: Option<Level>,
}
fn string_to_level(string: &str) -> Option<Level> {
match string.to_lowercase().as_str() {
"info" => Some(Level::INFO),
"debug" => Some(Level::DEBUG),
"warn" | "warning" => Some(Level::WARN),
"trace" => Some(Level::TRACE),
"error" => Some(Level::ERROR),
_ => None,
}
}

View File

@@ -0,0 +1,233 @@
use ciborium::Value as CborValue;
use flume::{unbounded, Receiver, Sender};
use std::fs;
use std::path::{Path, PathBuf};
use tokio::io::{AsyncReadExt, AsyncWriteExt, Interest};
use tokio::net::unix::{OwnedReadHalf, OwnedWriteHalf};
use tokio::net::{UnixListener as TokioUnixListener, UnixStream};
use tracing::{debug, info, warn};
use crate::error::{IntoMizeResult, MizeError, MizeResult};
use crate::instance::connection::{ConnListener, Connection};
use crate::instance::{self, Mize};
use crate::proto::{self, MizeMessage};
pub struct UnixListener {
sock_path: PathBuf,
}
impl UnixListener {
pub fn new(store_path: PathBuf) -> MizeResult<UnixListener> {
Ok(UnixListener {
sock_path: store_path.join("sock").to_owned(),
})
}
}
pub fn connect(instance: &mut Mize, store_path: PathBuf) -> MizeResult<()> {
let conn_id = instance.spawn_async_blocking(
"unix_connection",
connect_async(instance.clone(), store_path),
)?;
let ns_of_peer_str = instance
.get(format!(
"inst/con_by_id/{}/peer/self/config/namespace",
conn_id
))?
.value_string()?;
let ns_of_peer = instance.namespace_from_string(ns_of_peer_str)?;
info!(
"connect_async ... ns_of_peer: {}",
ns_of_peer.clone().as_string()
);
instance.connection_set_namespace(conn_id, ns_of_peer.clone());
instance.set_namespace(ns_of_peer);
Ok(())
}
async fn connect_async(mut instance: Mize, store_path: PathBuf) -> MizeResult<u64> {
let mut stream = UnixStream::connect(store_path.join("sock")).await?;
let (unix_read, unix_write) = stream.into_split();
let (send_tx, send_rx) = unbounded::<MizeMessage>();
let conn_id = instance.new_connection(send_tx)?;
let cloned_instance = instance.clone();
instance.spawn_background("incomming", move || {
let result = unix_incomming(unix_read, cloned_instance, conn_id);
// if unix incomming fails, close the connection
if let Err(err) = result {
warn!("Connection closing because of EOF");
}
Ok(())
});
let outgoing_cloned_instance = instance.clone();
instance.spawn_background("outgoing", move || {
let result = unix_outgoing(unix_write, send_rx, outgoing_cloned_instance, conn_id);
// if writing fails, close this connection
if let Err(err) = result {
warn!("Connection closing because of EOF");
}
Ok(())
});
return Ok(conn_id);
}
impl ConnListener for UnixListener {
fn listen(self, mut instance: Mize) -> MizeResult<()> {
instance.spawn_async("unix listen async", unix_listen(self, instance.clone()));
Ok(())
}
}
async fn unix_listen(listener: UnixListener, mut instance: Mize) -> MizeResult<()> {
// remove the file at sock_path if it already exists
fs::remove_file(&listener.sock_path);
let listener = TokioUnixListener::bind(&listener.sock_path).mres_msg(format!(
"Could not bind to unix socket at '{}'",
listener.sock_path.display()
))?;
loop {
let (mut unix_sock, addr) = listener
.accept()
.await
.mres_msg("Error while accepting Unix sock connection")?;
info!("new connection");
let (send_tx, send_rx) = unbounded::<MizeMessage>();
let (unix_read, unix_write) = unix_sock.into_split();
let conn_id = instance.new_connection(send_tx)?;
let cloned_instance = instance.clone();
instance.spawn_background("incomming", move || {
let result = unix_incomming(unix_read, cloned_instance, conn_id);
// if unix incomming fails, close the connection
if let Err(err) = result {
warn!("Connection closing because of EOF");
}
Ok(())
});
let outgoing_cloned_instance = instance.clone();
instance.spawn_background("outgoing", move || {
let result = unix_outgoing(unix_write, send_rx, outgoing_cloned_instance, conn_id);
// if writing fails, close this connection
if let Err(err) = result {
warn!("Connection closing because of EOF");
}
Ok(())
});
}
}
fn unix_outgoing(
mut unix_write: OwnedWriteHalf,
send_rx: Receiver<MizeMessage>,
mut instance: Mize,
conn_id: u64,
) -> MizeResult<()> {
for msg in send_rx {
debug!("unix outgoing got msg: {}", msg);
let adapter = MyCiboriumWriter {
inner: &mut unix_write,
instance: &mut instance,
};
let value = msg.value();
ciborium::into_writer(&value, adapter)?
}
Ok(())
}
pub fn unix_incomming(
mut unix_read: OwnedReadHalf,
mut instance: Mize,
conn_id: u64,
) -> MizeResult<()> {
loop {
//println!("unix incoming loop...");
let adapter = MyCiboriumReader {
inner: &mut unix_read,
instance: &mut instance,
};
let value: CborValue = ciborium::from_reader(adapter)?;
if let CborValue::Integer(_) = value {
break;
}
let msg = MizeMessage::new(value, conn_id);
debug!("unix incoming got msg: {}", msg);
instance.got_msg(msg);
}
Ok(())
}
struct MyCiboriumWriter<'a> {
inner: &'a mut OwnedWriteHalf,
instance: &'a mut Mize,
}
impl<'a> ciborium_io::Write for MyCiboriumWriter<'a> {
type Error = MizeError;
fn write_all(&mut self, data: &[u8]) -> Result<(), Self::Error> {
//println!("CALL: write_all");
let runtime = self.instance.runtime.lock()?;
//println!("after runtime");
let num_read = runtime.block_on(self.inner.write_all(data))?;
Ok(())
}
fn flush(&mut self) -> Result<(), Self::Error> {
//println!("CALL flush");
let handle = self.instance.async_get_handle();
let result = handle.block_on(self.inner.flush());
//println!("flush result: {:?}", result);
Ok(())
}
}
struct MyCiboriumReader<'a> {
inner: &'a mut OwnedReadHalf,
instance: &'a mut Mize,
}
async fn test(test: &mut OwnedReadHalf, buf: &mut [u8]) {
//println!("hello from async...");
//println!("len: {}", buf.len());
let num_read = test.read_u8().await;
//println!("num_read: {:?}", num_read);
//test.read_exact(buf).await;
//println!("hello from async... twoooooooooooooo");
}
impl<'a> ciborium_io::Read for MyCiboriumReader<'a> {
type Error = MizeError;
fn read_exact(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
//println!("data before: {:?}", data);
//let runtime = self.instance.runtime.lock()?;
//println!("are we here after runtime lock()");
//runtime.block_on(test(self.inner, data));
//println!("are we hereeeeeeeeeeeeeeeeeeee");
//return Ok(());
//println!("incoming before block_on");
let handle = self.instance.async_get_handle();
let num_read = handle.block_on(self.inner.read_exact(data))?;
//println!("incoming num_read: {:?}", num_read);
//println!("data after: {:?}", data);
// if we read 0 bytes, that means the reader stream closed and we should terminate the
// connection
//if num_read == 0 {
//return Err(MizeError::new().msg("ReceiverStream closed"));
//}
Ok(())
}
}

239
core/src/platform/os/web.rs Normal file
View File

@@ -0,0 +1,239 @@
static SERVER_PORT: u16 = 3000;
static API_ENDPOINT: &str = "@mize";
pub fn run() -> Result<Web, MizeError> {
/*
* - Run a Webserver
*
*
*
*/
// I will deal with renders later. cli only for now
// let renders =
// for render in &*renders {
// match render.render_type {
// RenderType::JsonUi => {},
// RenderType::WebComponent => {
// let url_path = String::from("/api/render/") + &render.name + "/webroot";
// let file_path = mutexes.mize_folder.clone() + "/mr/" + &render.folder[..] + "/" + &render.webroot.clone()[..];
// let serve_dir = get_service(ServeDir::new(&file_path)).handle_error(handle_error);
// app = app.nest_service(&url_path, serve_dir);
// },
// }
// }
// let serve_client = get_service(ServeDir::new("js-client/src")).handle_error(handle_error);
let mut app = Router::new()
.route(format!("/{}/socket", API_ENDPOINT), get(websocket_handler))
// .route(format!("/{}/render/:id", API_ENDPOINT), get(get_render_main))
// .route(format!("/{}/file", API_ENDPOINT), get(get_file_handler))
// .merge(SpaRouter::new(format!("/{}/client", API_ENDPOINT), "js-client/src").index_file("main.html"))
// .nest_service(format!("/{}/client", API_ENDPOINT), serve_client)
.with_state(main)
.fallback(render_item);
let addr = SocketAddr::from(([0, 0, 0, 0], SERVER_PORT));
// tracing::debug!("listening on {}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.expect(format!("Unable to bind axum::Server to {}", addr));
}
pub struct Web {
}
impl Web {
}
async fn handle_error(_err: std::io::Error) -> impl IntoResponse {
MizeError::new(0).msg("internal server error").handle()
(StatusCode::INTERNAL_SERVER_ERROR, "Something went wrong...")
}
async fn render_item(uri: Uri) -> impl IntoResponse {
Html(fs::read_to_string("js-client/src/main.html").unwrap())
}
//#[axum_macros::debug_handler]
//async fn get_render_main(extract::Path(id): extract::Path<String>, State(mutexes): State<Mutexes>) -> http::Response<String> {
// let renders = &*mutexes.renders.lock().await;
// let render = renders.iter().filter(|&render| render.name == id).nth(0)
// .unwrap_or(renders.iter().filter(|&render| render.name == "mize-mmejs").nth(0).expect("mize-mmejs"));
// let file_path = mutexes.mize_folder.clone() + "/mr/" + &render.folder + "/" + &render.main;
// match render.render_type {
// RenderType::JsonUi => {
// return Response::builder()
// .header("content-type", "application/json")
// .status(StatusCode::OK)
// .body(fs::read_to_string(file_path).unwrap()).unwrap();
// },
// RenderType::WebComponent => {
// return Response::builder()
// .header("content-type", "application/javascript")
// .status(StatusCode::OK)
// .body(fs::read_to_string(file_path).unwrap()).unwrap();
// },
// }
//}
async fn websocket_handler(
ws: WebSocketUpgrade,
State(main): State<&Main>,
headers: HeaderMap,
) -> impl IntoResponse {
ws.on_upgrade(|socket| handle_websocket_connection(socket, main, headers))
}
async fn handle_websocket_connection(
socket: WebSocket,
main: &Main,
headers: HeaderMap,
){
//TODO: auth
//socket_tx.send() ... send out the socket
//socket_rx.read() ... read a message from the socket
let (mut socket_tx, mut socket_rx) = socket.split();
// msg_tx.send() ... send a MizeMessage to a Peer. should be in a Peer struct
// msg_rx.read() ... read MizeMessages that should be sent to the Peer that this ws connection is to
let (msg_tx, msg_rx): (Sender<proto::MizeMessage>, Receiver<proto::MizeMessage>) = mpsc::channel(SOCKET_CHANNEL_SIZE);
// why is this actually
let mut msg_rx = ReceiverStream::new(msg_rx);
// check if mize-module header exists
// let origin = if let Some(mod_header_val) = headers.get("mize-module"){
// let mut mods = mutexes.modules.lock().await;
// let mut client_id = mutexes.next_free_client_id.lock().await;
// let mut module = Module{
// tx: msg_tx.clone(),
// client_id: *client_id,
// name: String::from("tmp"),
// kind: ModuleKind::Extern(),
// registered_types: Vec::new(),
// };
// *client_id += 1;
// if let Ok(name) = mod_header_val.to_str() {
// module.name = name.to_string();
// } else {
// let err = MizeError::new(113)
// .extra_msg("The mize-module Header could not be decoded into a String.");
// msg_tx.send(proto::MizeMessage::Json(err.to_json_message())).await;
// return;
// };
// println!("module {} connected", module.name);
// mods.push(module.clone());
// drop(mods);
// Peer::Module(module)
// } else {
// };
// Forwarding messages
tokio::spawn(async move {
while let Some(msg) = msg_rx.next().await {
match msg {
proto::MizeMessage::Json(json_msg) => {
if let Ok(msg) = serde_json::to_string(&json_msg) {
let result = socket_tx.send(Message::Text(msg.clone())).await;
if let Err(e) = result {
println!("Client no longer Connected: {:?}", e);
remove_peer(myorigin.clone(), mymutexes.clone()).await;
} else {
println!("SENDING: {}", msg);
}
} else {
let err: MizeError = MizeError::new(11).extra_msg("error while serializing a json message").handle();
if let Ok(text) = serde_json::to_string(&err.to_json()){
socket_tx.send(Message::Text(text));
} else {
MizeError::new(11).extra_msg("not even being able to serialize an error, while failing to serializing a message").handle();
}
}
}
proto::MizeMessage::Bin(bin_msg) => {
socket_tx.send(Message::Binary(bin_msg.raw)).await;
}
proto::MizeMessage::Pong() => {
socket_tx.send(Message::Pong(Vec::new())).await;
}
proto::MizeMessage::Close((num, reason)) => {
println!("SENDING Close message");
socket_tx.send(Message::Close(Some(CloseFrame{code: num, reason: Cow::Owned(reason)}))).await;
}
}
}
});
// Reading messages
while let Some(result) = socket_rx.next().await {
let msg = if let Ok(msg) = result {msg} else {
let err = MizeError::new(115).extra_msg("TCP Connection Closed");
remove_peer(origin.clone(), mutexes.clone());
return;
};
match msg {
Message::Binary(b) => {
println!("Recieved a Binary Message. Those are not implemented yet.")
},
Message::Text(text) => {
println!("GOT: {}", text);
let json_msg: proto::JsonMessage = serde_json::from_str(&text).expect("error parsing json");
if let Err(err) = proto::handle_json_msg(json_msg, origin.clone(), mutexes.clone()).await {
let err_msg: MizeMessage = err.handle().into();
origin.send(err_msg).await;
};
},
Message::Close(inner) => {
match inner {
Some(CloseFrame{code, reason}) => {
origin.send(MizeMessage::Close((code, reason.into_owned()))).await;
},
None => {
origin.send(MizeMessage::Close((1000, "You didn't send a Close Reason".to_owned()))).await;
}
};
println!("Close from Client");
remove_peer(origin.clone(), mutexes.clone()).await;
}
Message::Ping(_) => {
msg_tx.send(MizeMessage::Pong());
},
Message::Pong(_) => {
let err = MizeError::new(11)
.extra_msg("unhandeld WebSocket-Message type: Pong")
.handle();
msg_tx.send(proto::MizeMessage::Json(err.to_json_message())).await;
},
};
};
}