large rewrite to make mize run on every platform

This commit is contained in:
Sebastian Moser
2024-05-18 08:31:38 +02:00
parent 75a6dca3fc
commit a16c2f9221
37 changed files with 624 additions and 970 deletions

265
src/core/error.rs Normal file
View File

@@ -0,0 +1,265 @@
use std::collections::HashMap;
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::format;
use std::string::FromUtf8Error;
use std::io;
use colored::Colorize;
use log::{trace, debug, info, warn, error};
use crate::proto::MizeMessage;
pub type MizeResult<T> = Result<T, MizeError>;
pub trait MizeResultTrait<T> {
fn critical(self) -> T;
}
pub trait IntoMizeResult<T, S> {
fn mize_result(self) -> MizeResult<T>;
fn mize_result_msg(self, msg: S) -> MizeResult<T> where S: std::fmt::Display ;
}
#[derive(Debug, Clone)]
pub struct MizeError {
pub category: Vec<String>,
pub code: u32,
pub messages: Vec<String>,
pub caused_by_msg: Option<CausedByMessage>,
pub code_location: Option<MizeCodeLocation>,
}
#[derive(Debug, Clone)]
pub struct CausedByMessage {
// peer: &Connection, // TODO: add later
msg: Box<MizeMessage>,
}
#[derive(Debug, Clone)]
pub struct MizeCodeLocation {
file: String,
line: u32,
column: u32,
}
impl Display for MizeCodeLocation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "file: {} line: {} column: {}", self.file, self.line, self.column)
}
}
impl From<&std::panic::Location <'_>> for MizeCodeLocation {
fn from(panic_location: &std::panic::Location) -> MizeCodeLocation {
let file = panic_location.file().to_string();
let line = panic_location.line();
let column = panic_location.column();
MizeCodeLocation {file, line, column}
}
}
impl MizeError {
#[track_caller]
pub fn code(code: u32) -> MizeError {
let caller_location = std::panic::Location::caller();
get_error_by_code(code, caller_location)
}
pub fn set_code(mut self, code: u32) -> MizeError {
self.code = code;
return self;
}
#[track_caller]
pub fn new() -> MizeError {
let caller_location = std::panic::Location::caller();
return MizeError {
category: Vec::new(),
code: 0,
messages: Vec::new(),
caused_by_msg: None,
code_location: Some(caller_location.into()),
}
}
pub fn msg<E>(mut self, msg: E) -> MizeError
where E: std::fmt::Display
{
self.messages.push(format!("{}", msg));
return self;
}
pub fn category<T>(mut self, category: T) -> MizeError where T: Into<String> {
self.category.push(category.into());
return self;
}
fn send(self) -> MizeError{
self
}
fn add_to_err_item(self) -> MizeError {
self
}
pub fn log(self) -> MizeError {
error!("MizeError encountered: {}", self.messages.join("\n"));
self
}
pub fn handle(self) -> MizeError {
return self.send().add_to_err_item().log();
}
pub fn critical(self){
let mut msg_iter = self.messages.iter();
match msg_iter.next() {
None => {
error!("{} MizeError with code: {}", "CRITICAL".red(), self.code);
},
Some(msg) => {
error!("{} MizeError with code: {} - {}", "CRITICAL".red(), self.code, msg);
},
}
for msg in msg_iter {
error!("{} {}", "MSG".red(), msg)
}
if let Some(location) = self.code_location {
error!("{} {}", "LOCATION".red(), location)
}
if let Some(caused_by_msg) = self.caused_by_msg {
error!("{} {:?}", "CAUSED_BY_MESSAGE".red(), caused_by_msg)
}
panic!();
}
}
/*
pub trait MizeResultExtension<T> {
fn extra_msg<E>(self, msg: E) -> Result<T, MizeError>
where E: std::fmt::Display
;
fn handle(self) -> Result<T, MizeError>;
fn is_critical(self) -> T;
}
impl<T> MizeResultExtension<T> for Result<T, MizeError> {
fn msg<E>(mut self, msg: E) -> Result<T, MizeError>
where E: std::fmt::Display
{
match self {
Err(ref mut e) => {
e.msg.push(format!("{}", msg));
return self;
},
Ok(something) => Ok(something),
}
}
fn handle(self) -> Result<T, MizeError> {
match self {
Err(e) => {
Err(e.send().add_to_err_item().write_to_stderr())
},
Ok(something) => Ok(something)
}
}
fn is_critical(self) -> T {
self.expect("Aborting because of a critical Error")
}
}
*/
/*
impl From<FromUtf8Error> for MizeError {
#[track_caller]
fn from(utf_err: FromUtf8Error) -> MizeError{
let caller_location = std::panic::Location::caller();
let mut err = MizeError::code(109).msg(format!("From std::string::FromUtf8Error: {}", utf_err));
err.code_location = Some(caller_location.into());
return err;
}
}
*/
impl<E> From<E> for MizeError where E: std::fmt::Display {
fn from(e: E) -> MizeError {
MizeError::new().msg(format!("From generic Rust error: {}", e))
}
}
impl <T> MizeResultTrait<T> for MizeResult<T> {
fn critical(self) -> T {
match self {
Ok(val) => val,
Err(err) => {
err.critical();
unreachable!()
},
}
}
}
impl<T, E, S> IntoMizeResult<T, S> for Result<T, E> where E: std::fmt::Display {
#[track_caller]
fn mize_result(self) -> MizeResult<T> {
let caller_location = std::panic::Location::caller();
match self {
Ok(val) => MizeResult::Ok(val),
Err(err) => MizeResult::Err(MizeError::new().category("misc").msg(format!("From any Error: {}", err))),
}
}
#[track_caller]
fn mize_result_msg(self, msg: S) -> MizeResult<T> where S: std::fmt::Display {
let caller_location = std::panic::Location::caller();
match self {
Ok(val) => MizeResult::Ok(val),
Err(err) => MizeResult::Err(MizeError::new().category("misc").msg(format!("{}", msg)).msg(format!("From any Error: {}", err))),
}
}
}
/*
impl<T> IntoMizeResult<T> for io::Result<T> {
#[track_caller]
fn mize_result(self) -> MizeResult<T> {
let caller_location = std::panic::Location::caller();
match self {
Ok(val) => MizeResult::Ok(val),
Err(io_err) => MizeResult::Err(MizeError::new().category("io").msg(format!("From an io::Error: {}", io_err))),
}
}
#[track_caller]
fn mize_result_msg(self, msg: dyn Into<&str>) -> MizeResult<T> {
let caller_location = std::panic::Location::caller();
match self {
Ok(val) => MizeResult::Ok(val),
Err(io_err) => MizeResult::Err(MizeError::new().category("io").msg(msg).msg(format!("From an io::Error: {}", io_err))),
}
}
}
*/
fn get_error_by_code(code: u32, caller_location: &std::panic::Location) -> MizeError {
let err = MizeError {
category: Vec::new(),
code: 0,
messages: Vec::new(),
caused_by_msg: None,
code_location: Some(caller_location.into()),
};
match code {
109 => err.set_code(109).category("decoding").msg("conversion from utf8 failed"),
108 => err.set_code(108).category("decoding").msg("serde deserialisation failed"),
_ => err,
}
}

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

@@ -0,0 +1,13 @@
use std::hash::Hash;
#[derive(Hash, Eq, PartialEq)]
pub struct MizeId {
path: Vec<String>
}
impl From<String> for MizeId {
fn from(value: String) -> Self {
MizeId{path: vec![value]}
}
}

View File

@@ -0,0 +1,16 @@
use log::{trace, debug, info, warn, error};
use tokio::sync::mpsc::{Sender, Receiver};
use crate::proto::MizeMessage;
use crate::error::{MizeError, MizeResult, IntoMizeResult};
pub trait Connection {
}
//#[derive(Debug)]
//pub struct Connection {
//tx: Sender<MizeMessage>,
//rx: Receiver<MizeMessage>,
//}

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

@@ -0,0 +1,71 @@
use std::collections::HashMap;
use std::fs::create_dir;
use log::{trace, debug, info, warn, error};
use uuid::Uuid;
use std::fs::File;
use colored::Colorize;
use std::path::Path;
use crate::error::{MizeError, MizeResult, IntoMizeResult, MizeResultTrait};
use crate::instance::store::Store;
use crate::instance::connection::Connection;
use crate::id::MizeId;
use crate::instance::subscription::Subscription;
use crate::item::{Item, ItemData};
use crate::memstore::MemStore;
pub mod connection;
pub mod store;
pub mod subscription;
static MSG_CHANNEL_SIZE: usize = 200;
/// The Instance type is the heart of the mize system
pub struct Instance<S: Store> {
// question vec of stores???
// would mean replication logic and such things is handeled by the instance
// i think it would be better thogh to then implement a ReplicatedStore
store: S,
peers: Vec<Box<dyn Connection>>,
subs: HashMap<MizeId, Subscription>
}
/*
pub enum RealmId {
Uuid(Uuid),
Local(Vec<String>),
Tld(Vec<String>),
}
*/
impl<S: Store> Instance<S> {
// new() opens a
pub async fn new() -> MizeResult<Instance<MemStore>> {
trace!("[ {} ] Instance::new()", "CALL".yellow());
let memstore = MemStore::new();
let instance = Instance {store: memstore, peers: Vec::new(), subs: HashMap::new()};
// platform specific init code
// TODO
return Ok(instance);
}
pub fn new_item(self) -> MizeResult<Item<S>> {
let id = self.store.new_id()?;
return Ok(Item {id, instance: self});
}
pub fn get<I: Into<MizeId>>(self, id: I) -> MizeResult<Item<S>> {
return Ok(Item {id: id.into(), instance: self});
}
pub fn set<I: Into<MizeId>, V: Into<ItemData>>(self, id: I, value: V) -> MizeResult<()> {
self.store.set(id.into(), value)
}
}

View File

@@ -0,0 +1,27 @@
use crate::error::MizeResult;
use crate::item::{Item, ItemData};
use crate::id::MizeId;
pub trait Store {
// should have the ability to be multi threaded, if the underlying implementation supports
// multithreaded IO operations
// for now all refs to a store hold a Mutex but this mutex is part of where the MizeStore trait
// is implemented, so that there can be a multithreaded implementation
//fn get(self, id: MizeId) -> MizeResult<Item<Self>> where Self: Sized;
fn set<T: Into<ItemData>>(self, id: MizeId, data: T) -> MizeResult<()>;
// in the future should implement transactions, ....
// funcs to do with links, backlinks
fn get_links(self, item: Item<Self>) -> MizeResult<Vec<MizeId>> where Self: Sized;
fn get_backlinks(self, item: Item<Self>) -> MizeResult<Vec<MizeId>> where Self: Sized;
fn new_id(&self) -> MizeResult<MizeId>;
}
/// references an item that is stored in a store
pub trait StoreRef {
// do we need this or is this basically an item
}

View File

@@ -0,0 +1,3 @@
pub struct Subscription;

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

@@ -0,0 +1,262 @@
use log::{trace, debug, info, warn, error};
use std::{collections::HashMap, path::PathBuf, io::Cursor, fmt::Display};
use std::fs::File;
use tokio::sync::Mutex;
use std::rc::Rc;
use crate::error::{MizeError, MizeResult, IntoMizeResult};
use crate::instance::Instance;
use crate::instance::store::Store;
use crate::id::MizeId;
use cbor::Cbor as CborValue;
// a item always has to do with a Instance, which takes care of how it is updated
pub struct Item<S: Store + Sized> {
pub id: MizeId,
pub instance: Instance<S>
}
// 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
pub type ItemData = CborValue;
impl<S: Store> Item<S> {
pub fn id(self) -> MizeId {
self.id
}
pub fn value_raw(self) -> MizeResult<Vec<u8>> {
// this will call from the instance which gets the value from the store
Ok(Vec::new())
}
}
/*
#[derive(Debug)]
pub enum Item {
Cbor(ItemCbor),
Folder(ItemFolder),
Ref(ItemRef),
}
#[derive(Clone, Debug)]
pub struct ItemCbor {
pub inner: CborValue,
pub id: MizeId,
}
#[derive(Debug)]
pub struct ItemFolder {
pub path: PathBuf,
pub id: MizeId,
}
#[derive(Debug)]
pub struct ItemRef {
pub store: Mutex<Rc<Itemstore>>,
pub id: MizeId,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct MizeId {
pub main: u64,
pub path: Option<Vec<String>>
}
pub struct MizeType {
}
//////////////////////////////////////////////////////////////////////////////////////////////
impl Display for MizeId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(path) = &self.path {
write!(f, "{}/{}", self.main, path.join("/"))
} else {
write!(f, "{}", self.main)
}
}
}
impl MizeId {
pub fn main(main: u64) -> MizeId {
MizeId { main, path: None }
}
pub fn join(self, segment: &str) -> MizeId {
let mut new = self.clone();
match new.path {
None => {
new.path = Some(vec![segment.to_owned()]);
}
Some(ref mut val) => {
val.push(segment.to_owned());
}
}
return new;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////
impl ItemCbor {
fn value_as_file(&self) -> MizeResult<File> {
/* sadly does not work like that
let cbor_c = self.inner.get("v").ok_or(MizeError::new()
.msg(format!("The ItemCbor ({}) does not have a \"c\" field ... so can't get it as a file", self.id()?)))?;
match cbor_c {
Cbor::Bytes(val) => {
return Ok(Cursor::new(val.clone()).into());
},
_ => {
return Err(MizeError::new().msg(format!("The value fo item ({}) is not of type Cbor::Bytes", self.id()?)));
},
}
*/
return File::open(self.value_as_path()?).mize_result_msg("Could not open file");
}
fn value_as_path(&self) -> MizeResult<PathBuf> {
todo!()
}
/* an old way of getting the id this
fn id(self) -> MizeResult<MizeId> {
let cbor_item = self.inner.get("item".to_owned()).ok_or(MizeError::new()
.msg("ItemCbor had no path: \"item\""))?;
let cbor_id = match cbor_item {
Cbor::Map(map) => {
map.get("id").ok_or(MizeError::new().msg("ItemCbor had no path \"item/id\""))
},
_ => {
return Err(MizeError::new().msg("ItemCbor path \"item\" was not of type Cbor::Map"));
},
};
let id = match cbor_id {
Cbor::Unsigned(val) => val.into_u64(),
_ => {
return Err(MizeError::new().msg("ItemCbor path \"item/id\" was not of type Cbor::Unsigned"));
},
};
return Ok(MizeId { inner: id });
}
// */
fn id(&self) -> MizeResult<MizeId> {
Ok(self.id.clone())
}
fn as_cbor(&self) -> MizeResult<ItemCbor> {
Ok(self.clone())
}
fn write_to_path(&self, path: PathBuf) -> MizeResult<()> {
todo!()
//let cbor_content =
//std::fs::write(path.join(format!("{}", self.id)), );
}
}
impl ItemFolder {
fn value_as_file(&self) -> MizeResult<File> {
let file = File::open(self.path.join("v"))
.mize_result_msg(format!("Could not open the value file of item ({})", self.id()?));
return file;
}
fn value_as_path(&self) -> MizeResult<PathBuf> {
return Ok(self.path.clone());
}
fn id(&self) -> MizeResult<MizeId> {
Ok(self.id.clone())
}
fn as_cbor(&self) -> MizeResult<ItemCbor> {
todo!()
}
fn write_to_path(&self, path: PathBuf) -> MizeResult<()> {
Ok(())
}
}
impl ItemRef {
fn value_as_file(&self) -> MizeResult<File> {
todo!()
}
fn value_as_path(&self) -> MizeResult<PathBuf> {
todo!()
}
fn id(&self) -> MizeResult<MizeId> {
return Ok(self.id.clone());
}
fn as_cbor(&self) -> MizeResult<ItemCbor> {
todo!()
}
async fn write_to_path(&self, path: PathBuf) -> MizeResult<()> {
let store = self.store.lock().await;
for (key, value) in self.map.iter() {
// TODO right here!!!!
}
todo!()
}
}
impl Item {
pub fn value_as_file(&self) -> MizeResult<File> {
match self {
Item::Cbor(val) => val.value_as_file(),
Item::Ref(val) => val.value_as_file(),
Item::Folder(val) => val.value_as_file(),
}
}
pub fn value_as_path(&self) -> MizeResult<PathBuf> {
match self {
Item::Cbor(val) => val.value_as_path(),
Item::Ref(val) => val.value_as_path(),
Item::Folder(val) => val.value_as_path(),
}
}
pub fn id(&self) -> MizeResult<MizeId> {
match self {
Item::Cbor(val) => val.id(),
Item::Ref(val) => val.id(),
Item::Folder(val) => val.id(),
}
}
pub fn as_cbor(&self) -> MizeResult<ItemCbor> {
match self {
Item::Cbor(val) => val.as_cbor(),
Item::Ref(val) => val.as_cbor(),
Item::Folder(val) => val.as_cbor(),
}
}
pub fn write_to_path(&self, path: PathBuf) -> MizeResult<()> {
match self {
Item::Cbor(val) => val.write_to_path(path),
Item::Ref(val) => val.write_to_path(path),
Item::Folder(val) => val.write_to_path(path),
}
}
pub fn load_json(json_str: &str) -> MizeResult<Item> {
todo!()
}
}
*/

3
src/core/lib.rs Normal file
View File

@@ -0,0 +1,3 @@
#![ allow( warnings ) ]

35
src/core/memstore.rs Normal file
View File

@@ -0,0 +1,35 @@
use std::collections::HashMap;
use cbor::Cbor as CborValue;
use crate::id::MizeId;
use crate::instance::store::Store;
use crate::error::MizeResult;
use crate::item::{Item, ItemData};
pub struct MemStore {
map: HashMap<MizeId, CborValue>,
next_id: u64,
}
impl Store for MemStore {
fn set<T: Into<ItemData>>(mut self, id: MizeId, data: T) -> MizeResult<()> {
self.map.insert(id, data.into());
return Ok(())
}
fn get_links(self, item: Item<Self>) -> MizeResult<Vec<MizeId>> {
Ok(Vec::new())
}
fn get_backlinks(self, item: Item<Self>) -> MizeResult<Vec<MizeId>> {
Ok(Vec::new())
}
fn new_id(&self) -> MizeResult<MizeId> {
let id_string = format!("{}", self.next_id);
return Ok(id_string.into());
}
}
impl MemStore {
pub fn new() -> MemStore {
return MemStore { map: HashMap::new(), next_id: 0 };
}
}

6
src/core/proto/mod.rs Normal file
View File

@@ -0,0 +1,6 @@
use cbor::Cbor as CborValue;
#[derive(Debug, Clone)]
pub struct MizeMessage {
value: CborValue,
}

0
src/core/types/mod.rs Normal file
View File