towards the next try of mize...

This commit is contained in:
Sebastian Moser
2024-02-04 21:15:18 +01:00
parent 512ca994e2
commit 831125bdf0
24 changed files with 3445 additions and 13 deletions

2
.gitignore vendored
View File

@@ -1,6 +1,6 @@
/target /target
/mize-folder /folder
# I put testing stuff for this projekt that I don't want to push to github into a folder called gitignore # I put testing stuff for this projekt that I don't want to push to github into a folder called gitignore
/gitignore /gitignore

1571
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -4,20 +4,27 @@ version = "0.1.1"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
#axum = { version = "0.6.1", features = ["ws", "headers"] } clap = { version = "4.4.11", features = ["cargo"] }
#axum-extra = { version = "0.4.2", features = ["spa"] } home = "0.5.9"
#axum-macros = "0.3.0" serde = { version = "1.0.152", features = ["derive"] }
#colored = "2.0.0" serde_json = "1.0.91"
#derive_more = "0.99.17" axum = { version = "0.6.1", features = ["ws", "headers"] }
#futures-util = "0.3.24" axum-extra = { version = "0.4.2", features = ["spa"] }
axum-macros = "0.3.0"
colored = "2.0.0"
derive_more = "0.99.17"
futures-util = "0.3.24"
#itertools = "0.10.5" #itertools = "0.10.5"
#lazy_static = "1.4.0" #lazy_static = "1.4.0"
#rmp-serde = "1.1.1" #rmp-serde = "1.1.1"
#rmpv = { version = "1.0.0", features = ["serde", "with-serde"] } #rmpv = { version = "1.0.0", features = ["serde", "with-serde"] }
#serde = "1.0.152" tokio = { version = "1.21.0", features = ["full", "net"] }
#serde_json = "1.0.91" tokio-stream = "0.1.9"
#tokio = {version = "1.21.0", features = ["full"]} toml = "0.5.10"
#tokio-stream = "0.1.9"
#toml = "0.5.10"
#tower-http = "0.3.5" #tower-http = "0.3.5"
#uuid = "1.3.0" uuid = "1.3.0"
log = "0.4.20"
env_logger = "0.10.1"
daemonize = "0.5.0"
nix = { version = "0.27.1", features = ["signal"] }
serde_cbor = "0.11.2"

61
flake.lock generated Normal file
View File

@@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1701680307,
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1703637592,
"narHash": "sha256-8MXjxU0RfFfzl57Zy3OfXCITS0qWDNLzlBAdwxGZwfY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "cfc3698c31b1fb9cdcf10f36c9643460264d0ca8",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

17
flake.nix Normal file
View File

@@ -0,0 +1,17 @@
{
inputs = {
# nixpkgs 35.11 still contains rust 1.73
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { flake-utils, nixpkgs, ... }@inputs: flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; in {
#packages.default =
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [ cargo rustc ];
};
});
}

4
src/cli/call.rs Normal file
View File

@@ -0,0 +1,4 @@
use clap::ArgMatches;
pub async fn call(sub_matches: &ArgMatches) {
}

63
src/cli/daemon.rs Normal file
View File

@@ -0,0 +1,63 @@
use std::path::PathBuf;
use clap::ArgMatches;
use home::home_dir;
use std::fs::File;
use log::{trace, debug, info, warn, error};
use nix::sys::signal::{self, Signal};
use crate::error::{MizeResultTrait, IntoMizeResult, MizeError};
use crate::instance::Instance;
pub async fn daemon(sub_matches: &ArgMatches) {
let instance_path = match sub_matches.get_one::<String>("store") {
Some(a) => PathBuf::from(a),
None => {
let mut home_dir = home_dir()
.ok_or(MizeError::new().category("io").category("env").msg("could not get the home directory, where the mize store is by default")).critical();
home_dir.push(".mize");
home_dir
},
};
let instance = Instance::new(instance_path.clone()).await.critical();
info!("Opened Instance at \"{}\"", instance_path.clone().display());
match sub_matches.subcommand() {
// stop the daemon
Some(("stop", _)) => {
if instance.daemon_is_running().await.critical() {
//instance.stop_daemon();
println!("Todo")
} else {
warn!("Daemon is already not running.");
};
},
Some(("status", _)) => {
if instance.daemon_is_running().await.critical() {
eprintln!("Daemon is running");
} else {
eprintln!("Daemon is not running");
};
},
Some(("kill", _)) => {
signal::kill(instance.get_daemon_pid().await.critical(), Signal::SIGTERM).mize_result_msg("Could not kill daemon").critical();
},
Some((cmd, _)) => {
error!("Unknown Command: {}", cmd);
}
// just start the daemon
None => {
if !instance.daemon_is_running().await.critical() {
debug!("Daemon is not running. starting...");
instance.start_deamon().await.critical();
info!("Daemon Started");
};
},
}
}

23
src/cli/get.rs Normal file
View File

@@ -0,0 +1,23 @@
use std::path::PathBuf;
use clap::ArgMatches;
use home::home_dir;
use crate::error::{MizeResultTrait, IntoMizeResult, MizeError};
use crate::instance::Instance;
pub async fn get(sub_matches: &ArgMatches) {
let instance_folder_path = match sub_matches.get_one::<String>("store") {
Some(a) => PathBuf::from(a),
None => {
let mut home_dir = home_dir()
.ok_or(MizeError::new().category("io").category("env").msg("could not get the home directory, where the mize store is by default")).critical();
home_dir.push(".mize");
home_dir
},
};
let instance = Instance::new(instance_folder_path);
}

16
src/cli/mod.rs Normal file
View File

@@ -0,0 +1,16 @@
pub mod daemon;
pub mod serve;
pub mod get;
pub mod set;
pub mod mount;
pub mod print;
pub mod call;
pub use daemon::daemon;
pub use serve::serve;
pub use get::get;
pub use set::set;
pub use mount::mount;
pub use print::print;
pub use call::call;

4
src/cli/mount.rs Normal file
View File

@@ -0,0 +1,4 @@
use clap::ArgMatches;
pub async fn mount(sub_matches: &ArgMatches) {
}

4
src/cli/print.rs Normal file
View File

@@ -0,0 +1,4 @@
use clap::ArgMatches;
pub async fn print(sub_matches: &ArgMatches) {
}

4
src/cli/serve.rs Normal file
View File

@@ -0,0 +1,4 @@
use clap::ArgMatches;
pub async fn serve(sub_matches: &ArgMatches) {
}

4
src/cli/set.rs Normal file
View File

@@ -0,0 +1,4 @@
use clap::ArgMatches;
pub async fn set(sub_matches: &ArgMatches) {
}

271
src/error.rs Normal file
View File

@@ -0,0 +1,271 @@
use std::collections::HashMap;
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::format;
use std::string::FromUtf8Error;
use toml::Value;
use colored::Colorize;
use std::io;
use log::{trace, debug, info, warn, error};
use serde_json::Value as JsonValue;
use toml;
use crate::instance::Connection;
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,
}
}

View File

@@ -0,0 +1,74 @@
use log::{trace, debug, info, warn, error};
use tokio::{sync::mpsc::{Sender, Receiver}, net::UnixStream};
use tokio::net::unix::{OwnedReadHalf, OwnedWriteHalf};
use tokio::io::{self, Interest};
use crate::proto::MizeMessage;
use crate::error::{MizeError, MizeResult, IntoMizeResult};
#[derive(Debug)]
pub struct Connection {
tx: Sender<MizeMessage>,
rx: Receiver<MizeMessage>,
}
impl Connection {
pub fn from_unix(unix_sock: UnixStream) -> Connection {
let (recieve_tx, recieve_rx) = tokio::sync::mpsc::channel(crate::instance::UNIX_SOCKET_CHANNEL_SIZE);
let (send_tx, send_rx) = tokio::sync::mpsc::channel(crate::instance::UNIX_SOCKET_CHANNEL_SIZE);
let (unix_read, unix_write) = unix_sock.into_split();
// TODO: error handling and close con on error
tokio::spawn(unix_send(unix_write, send_rx));
tokio::spawn(unix_recieve(unix_read, recieve_tx));
return Connection { tx: send_tx, rx: recieve_rx }
}
}
async fn unix_send(unix_write: OwnedWriteHalf, send_rx: Receiver<MizeMessage>) -> MizeResult<()> {
loop {
let ready = unix_write.ready(Interest::WRITABLE).await.mize_result_msg("hi")?;
if ready.is_writable() {
// Try to write data, this may still fail with `WouldBlock`
// if the readiness event is a false positive.
match unix_write.try_write(b"hello world") {
Ok(n) => {
println!("wrote {} bytes to unix sock", n);
}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
continue;
}
Err(e) => {
return Err(e.into());
}
}
}
}
}
async fn unix_recieve(unix_read: OwnedReadHalf, recieve_tx: Sender<MizeMessage>) -> MizeResult<()> {
loop {
let ready = unix_read.ready(Interest::READABLE).await.mize_result_msg("hi")?;
if ready.is_readable() {
let mut data = vec![0; 1024];
// Try to read data, this may still fail with `WouldBlock`
// if the readiness event is a false positive.
match unix_read.try_read(&mut data) {
Ok(n) => {
println!("read {} bytes from unix sock", n);
}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
continue;
}
Err(e) => {
return Err(e.into());
}
}
}
}
}

213
src/instance/mod.rs Normal file
View File

@@ -0,0 +1,213 @@
use std::collections::HashMap;
use std::path::PathBuf;
use std::fs::create_dir;
use colored::Colorize;
use log::{trace, debug, info, warn, error};
use tokio::net::{UnixListener, UnixStream};
use uuid::Uuid;
use std::fs::File;
use std::path::Path;
use daemonize::Daemonize;
use nix::unistd::Pid;
use crate::error::{MizeError, MizeResult, IntoMizeResult, MizeResultTrait};
use crate::itemstore::{Itemstore, self};
use crate::item::{Item, MizeId, ItemGeneral};
pub mod peer;
pub mod connection;
pub use connection::Connection;
static UNIX_SOCKET_CHANNEL_SIZE: usize = 200;
static WEB_SOCKET_CHANNEL_SIZE: usize = 200;
static TCP_SOCKET_CHANNEL_SIZE: usize = 200;
static UPDATE_CHANNEL_SIZE: usize = 200;
pub struct Instance {
store: Itemstore,
peers: Vec<Connection>,
realm_instance: Option<(RealmId, Vec<Connection>)>,
}
pub enum RealmId {
Uuid(Uuid),
Local(Vec<String>),
Tld(Vec<String>),
}
impl Instance {
pub async fn new(instance_path: PathBuf) -> MizeResult<Instance> {
trace!("[ {} ] Instance::new()", "CALL".yellow());
trace!("[ {} ] instance_path: {:?}", "ARG".yellow(), instance_path);
// if path does not exist, create it
if instance_path.exists() == false {
debug!("instance path \"{}\" does not exist, so creating it", instance_path.clone().display());
File::create(&instance_path)
.mize_result_msg(format!("Could not create Instance at: {}", &instance_path.display())).critical();
}
// if a daemon is running, return instance with memory store
// with realm_instance at that daemon
let mut instance = match find_running_ademon(instance_path.clone()).await {
Ok(con) => {
Instance {
store: Itemstore::Memory(itemstore::new_memory_store()?),
peers: vec![con],
realm_instance: None,
}
},
// else open the itemstore
Err(err) => {
Instance {
store: Itemstore::Folder(itemstore::new_folder_store(instance_path)?),
peers: vec![],
realm_instance: None,
}
},
};
if !instance.store.has_item(MizeId::main(1)).await? {
// create first item ... which is this instance
let mut instance_item = ItemGeneral {
id: MizeId::main(1),
kind: vec!["instance".to_owned()],
value: Vec::new(),
map: HashMap::new(),
};
instance_item.map.insert("daemon".to_owned(), MizeId::main(1).join("daemon"));
instance.store.set_item(Item::General(instance_item)).await?;
let mut daemon_item = ItemGeneral {
id: MizeId::main(1).join("daemon"),
kind: vec!["instance/daemon".to_owned()],
value: Vec::new(),
map: HashMap::new(),
};
daemon_item.map.insert("log".to_owned(), MizeId::main(1).join("daemon").join("log"));
daemon_item.map.insert("pid".to_owned(), MizeId::main(1).join("daemon").join("pid"));
instance.store.set_item(Item::General(daemon_item)).await?;
let log_item = ItemGeneral {
id: MizeId::main(1).join("daemon").join("log"),
kind: vec!["blob".to_owned()],
value: Vec::new(),
map: HashMap::new(),
};
instance.store.set_item(Item::General(log_item)).await?;
let pid_item = ItemGeneral {
id: MizeId::main(1).join("daemon").join("pid"),
kind: vec!["int".to_owned()],
value: Vec::new(),
map: HashMap::new(),
};
instance.store.set_item(Item::General(pid_item)).await?;
};
return Ok(instance);
}
pub async fn start_deamon(mut self) -> MizeResult<()> {
trace!("[ {} ] Instance::start_deamon()", "CALL".yellow());
// daemonize
let stdout = self.get_item(MizeId::main(1).join("daemon").join("log")).await?.value_as_file()?;
let pid_file = self.get_item(MizeId::main(1).join("daemon").join("pid")).await?.value_as_path()?;
let daemonize = Daemonize::new()
.pid_file(pid_file)
//.chown_pid_file(true) // is optional, see `Daemonize` documentation
//.working_directory("/tmp") // for default behaviour.
//.user("nobody")
//.group("daemon") // Group name
//.group(2) // or group id.
//.umask(0o777) // Set umask, `0o027` by default.
.stdout(stdout); // Redirect stdout to `/tmp/daemon.out`.
//.privileged_action(|| "Executed before drop privileges");
daemonize.start().mize_result_msg("Error daemonizing")?;
info!("Succesfuly Daemonized");
// listen on unix socket
let sock_path = self.get_item(MizeId::main(1).join("daemon").join("sock")).await?.value_as_path()?;
let listener = UnixListener::bind(&sock_path)
.mize_result_msg(format!("Could not bind to unix Socket at: {}", sock_path.display())).critical();
loop {
match listener.accept().await {
Ok((unix_sock, _addr)) => {
self.peers.push(Connection::from_unix(unix_sock))
},
Err(e) => { MizeError::new().msg("New Peer failed").msg(format!("{}", e)).log(); },
};
}
}
pub async fn get_daemon_pid(self) -> MizeResult<Pid> {
trace!("[ {} ] Instance::get_daemon_pid()", "CALL".yellow());
todo!()
}
pub async fn daemon_is_running(&self) -> MizeResult<bool> {
trace!("[ {} ] Instance::daemon_is_running()", "CALL".yellow());
let sock_path = self.get_item(MizeId::main(1).join("daemon").join("sock")).await?.value_as_path()?;
match find_running_ademon(sock_path).await {
Ok(_) => Ok(true),
Err(_) => Ok(false),
}
}
// item funcs
pub async fn get_item(&self, id: MizeId) -> MizeResult<Item> {
self.store.get_item(id).await
}
pub async fn set_item(&mut self, item: Item) -> MizeResult<()> {
self.store.set_item(item).await
}
pub async fn create_item(&mut self) -> MizeResult<MizeId> {
self.store.create_item().await
}
//pub fn update_item(self) -> MizeResult<Item> {
//todo!()
//}
//pub fn on_item_update(self) -> MizeResult<Item> {
//todo!()
//}
}
async fn find_running_ademon(path: PathBuf) -> MizeResult<Connection> {
match UnixStream::connect(path).await {
Ok(val) => Ok(Connection::from_unix(val)),
Err(err) => Err(MizeError::new().msg("No Daemon found to connect")),
}
}
fn handle_connection() {
todo!()
}
/*
fn get_instance_type() -> MizeResult<Item> {
let mut types_item = Item::new("types");
// instance
let instance_str = include_str!("../../types/mize/instance/item.json");
let instance = Item::new("type").load_json(instance_str)?;
types_item.set("instance", instance);
return Ok(types_item);
}
*/

1
src/instance/peer.rs Normal file
View File

@@ -0,0 +1 @@

239
src/instance/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;
},
};
};
}

234
src/item.rs Normal file
View File

@@ -0,0 +1,234 @@
use log::{trace, debug, info, warn, error};
use std::{collections::HashMap, path::PathBuf, io::Cursor, fmt::Display};
use serde_cbor::Value as CborValue;
use std::fs::File;
use tokio::sync::Mutex;
use crate::error::{MizeError, MizeResult, IntoMizeResult};
#[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!()
}
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!()
}
}

267
src/itemstore.rs Normal file
View File

@@ -0,0 +1,267 @@
use log::{trace, debug, info, warn, error};
use std::fs::{self, File};
use std::path::{PathBuf, Path};
use std::collections::HashMap;
use std::io::{Read, Write};
use colored::Colorize;
use crate::item::{Item, MizeId, ItemCbor, ItemDescriptor};
use crate::error::{MizeError, MizeResult, IntoMizeResult};
pub enum Itemstore {
Folder(FolderItemstore),
Memory(MemoryItemstore),
}
pub struct FolderItemstore {
path: PathBuf,
next_id: u64,
}
pub struct MemoryItemstore {
inner: HashMap<MizeId, Item>,
next_id: u64,
}
///////////////////////////////////////////////////////////////////////////////////////////
pub fn new_memory_store() -> MizeResult<MemoryItemstore> {
Ok(MemoryItemstore {
inner: HashMap::new(),
next_id: 100,
})
}
pub fn new_folder_store(path: PathBuf) -> MizeResult<FolderItemstore> {
if path.join("store_open").exists() {
let pid_str = fs::read_to_string(path.join("store_open"))
.mize_result_msg("Could not read the store_open file to check which process has the store opened")?;
let pid = pid_str.parse::<u32>()
.mize_result_msg("Could not Parse the content of the store_open file into a u32")?;
if Path::new("/proc").join(format!("{}", pid)).exists() {
return Err(MizeError::new().msg(
format!("the store at {} is already open by process with pid: {}", path.display(), pid)
));
} else {
warn!("An Instance was not closed properly. (store_open file contained a not running PID)");
std::fs::remove_file(path.join("store_open"))
.mize_result_msg("Could not remove the store_open file")?;
}
}
let mut store_open_file = File::create(path.join("store_open"))
.mize_result_msg("Could not create the store_open file")?;
store_open_file.write_all(format!("{}", std::process::id()).as_bytes())
.mize_result_msg("Could not write my pid to the store_open file")?;
// if no store_next_id exists write it with the default 100
let mut next_id: u64 = 100; // as a default
if !path.join("store_next_id").exists() {
fs::write(path.join("store_next_id"), format!("{}", next_id).as_bytes())
.mize_result_msg("Could not write the default next_id to store_next_id")?;
} else {
let next_id_string = fs::read_to_string(path.join("store_next_id"))
.mize_result_msg("Could not read store_next_id of this store")?;
next_id = next_id_string.parse()
.mize_result_msg("Could not parse contents of store_next_id into u64")?;
}
return Ok(FolderItemstore{ path, next_id});
}
///////////////////////////////////////////////////////////////////////////////////////////
impl FolderItemstore {
pub async fn write_next_id(&self) -> MizeResult<()> {
fs::write(self.path.join("store_next_id"), format!("{}", self.next_id).as_bytes())
.mize_result_msg(format!("Could not write the next_id: \"{}\" to \"{}\"", self.next_id, self.path.join("store_next_id").display()))?;
return Ok(());
}
}
///////////////////////////////////////////////////////////////////////////////////////////
impl MemoryItemstore {
// item funcs
async fn get_item(&self, id: MizeId) -> MizeResult<Item> {
match self.inner.get(&id) {
None => {
Err(MizeError::new().msg(format!("there was no item with id ({}) in this store", id)))
},
Some(item) => {
Ok(Item::Cbor(item.as_cbor()?))
},
}
}
async fn set_item(&mut self, item: Item) -> MizeResult<()> {
self.inner.insert(item.id()?, item)
.ok_or(MizeError::new().msg("Could not set an item in a MemoryStore"))?;
return Ok(());
}
async fn create_item(&mut self) -> MizeResult<MizeId> {
// advance the next_id
self.next_id += 1;
return Ok(MizeId::main(self.next_id))
}
//fn update_item(self) -> MizeResult<I>;
// other funcs
async fn close(&self) -> MizeResult<()> {
return Ok(());
}
async fn next_id(&self) -> u64 {
self.next_id
}
async fn has_item(&self, id: MizeId) -> MizeResult<bool> {
Ok(self.inner.contains_key(&id))
}
}
impl FolderItemstore {
// item funcs
async fn get_item(&self, id: MizeId) -> MizeResult<Item> {
let id_string = format!("{}", id.main);
let mut final_path = self.path.join("store");
if id_string.len() < 2 {
// use path 00 for ids under 10
final_path.push("00")
} else {
// will not panic, because of prior check if string is more than two long
final_path.push(id_string[..2].to_owned());
};
return Ok(Item::Descriptor(ItemDescriptor { path: final_path, id}));
}
async fn set_item(&self, item: Item) -> MizeResult<()> {
item.write_to_path(self.get_path_of_item(&item.id()?)?)
}
async fn create_item(&mut self) -> MizeResult<MizeId> {
// advance the next_id
self.next_id += 1;
match self.write_next_id().await {
// on error revert to old next_id
Ok(_) => {},
Err(err) => {
self.next_id -= 1;
return Err(err);
},
}
return Ok(MizeId::main(self.next_id));
}
//fn update_item(self) -> MizeResult<Item>;
// other funcs
async fn close(&self) -> MizeResult<()> {
if self.path.join("store_open").exists() {
std::fs::remove_file(self.path.join("store_open"))
.mize_result_msg("Could not remove the store_open file")?;
}
return Ok(());
}
async fn next_id(&self) -> u64 {
self.next_id
}
async fn has_item(&self, id: MizeId) -> MizeResult<bool> {
Ok(self.get_path_of_item(&id)?.exists())
}
fn get_path_of_item(&self, id: &MizeId) -> MizeResult<PathBuf> {
// get path of item
let id_string = format!("{}", id.main);
let mut final_path = self.path.join("store");
if id_string.len() < 2 {
// use path 00 for ids under 10
final_path.push("00")
} else {
// will not panic, because of prior check if string is more than two long
final_path.push(id_string[..2].to_owned());
};
return Ok(final_path);
}
}
impl Itemstore {
// item funcs
pub async fn get_item(&self, id: MizeId) -> MizeResult<Item> {
trace!("[ {} ] Itemstore::get_item()", "CALL".yellow());
trace!("[ {} ] id: {}", "ARG".yellow(), id);
match self {
Itemstore::Folder(val) => val.get_item(id).await,
Itemstore::Memory(val) => val.get_item(id).await,
}
}
pub async fn set_item(&mut self, item: Item) -> MizeResult<()> {
trace!("[ {} ] Itemstore::set_item()", "CALL".yellow());
trace!("[ {} ] item: {:?}", "ARG".yellow(), item);
match self {
Itemstore::Folder(val) => val.set_item(item).await,
Itemstore::Memory(val) => val.set_item(item).await,
}
}
pub async fn create_item(&mut self) -> MizeResult<MizeId> {
trace!("[ {} ] Itemstore::create_item()", "CALL".yellow());
match self {
Itemstore::Folder(val) => val.create_item().await,
Itemstore::Memory(val) => val.create_item().await,
}
}
//pub fn update_item(self) -> MizeResult<I>;
// other funcs
pub async fn close(&self) -> MizeResult<()> {
trace!("[ {} ] Itemstore::close()", "CALL".yellow());
match self {
Itemstore::Folder(val) => val.close().await,
Itemstore::Memory(val) => val.close().await,
}
}
pub async fn next_id(&self) -> u64 {
trace!("[ {} ] Itemstore::next_id()", "CALL".yellow());
match self {
Itemstore::Folder(val) => val.next_id().await,
Itemstore::Memory(val) => val.next_id().await,
}
}
pub async fn has_item(&self, id: MizeId) -> MizeResult<bool> {
trace!("[ {} ] Itemstore::has_item()", "CAL".yellow());
trace!("[ {} ] id: {}", "ARG".yellow(), id);
let ret = match self {
Itemstore::Folder(val) => val.has_item(id).await,
Itemstore::Memory(val) => val.has_item(id).await,
};
if let Ok(val) = ret {trace!("[ {} ] Itemstore::has_item(): {}", "RET".yellow(), val)};
return ret;
}
}

16
src/lib.rs Normal file
View File

@@ -0,0 +1,16 @@
#![ allow( warnings ) ]
static PROTO_VERSION: u8 = 1;
// pub mod types;
pub mod error;
mod instance;
mod proto;
mod item;
mod itemstore;

325
src/main.rs Normal file
View File

@@ -0,0 +1,325 @@
#![ allow( warnings ) ]
use std::path::PathBuf;
use clap::{ArgAction, ArgMatches};
use clap::{Arg, crate_version, Command};
use tokio::sync::{Mutex, mpsc};
use std::sync::Arc;
use log::Level;
use log::LevelFilter;
use env_logger::{Builder, Env};
use std::io::Write;
use colored::Colorize;
use std::env;
use log::{trace, debug, info, warn, error};
mod cli;
mod error;
// mod types;
mod instance;
mod item;
mod itemstore;
mod proto;
static APPNAME: &str = "mize";
static DEFAULT_LOG_LEVEL: LevelFilter = LevelFilter::Warn;
#[tokio::main]
async fn main() {
let cli_matches = cli_matches();
init_logger(&cli_matches);
// log a message of every level, to test loging
// test_logger();
// match command
match cli_matches.subcommand() {
// mi daemon
Some(("daemon", sub_matches)) => cli::daemon(sub_matches).await,
// mi serve
Some(("serve", sub_matches)) => cli::serve(sub_matches).await,
// mi mount
Some(("mount", sub_matches)) => cli::mount(sub_matches).await,
// mi get
Some(("get", sub_matches)) => cli::get(sub_matches).await,
// mi set
Some(("set", sub_matches)) => cli::set(sub_matches).await,
// mi print
Some(("print", sub_matches)) => cli::print(sub_matches).await,
// mi call
Some(("call", sub_matches)) => cli::call(sub_matches).await,
// some unknown command passed
Some((cmd, sub_matches)) => error!("The subcommand: {} is not known. use --help to list availavle commands", cmd),
None => error!("No subcommand was passed. use --help to list availavle comamnds."),
}
}
fn init_logger(cli_matches: &ArgMatches) {
// 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 = Builder::new();
builder.format_indent(Some(8));
// 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 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");
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::<&str>("log-level") {
let log_level_from_arg = match log_level_arg_string.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)));
}
// check --silent flag
if cli_matches.get_flag("silent") {
if log_level != DEFAULT_LOG_LEVEL {
log_messages.push((Level::Warn, format!(
"The loglevel was set by some method (it's not the DEFAULT_LOG_LEVEL) and is now overwritten by the --siltne to be OFF"
)))
}
log_level = LevelFilter::Off;
}
// actually set the log_level on the builder
builder.filter_level(log_level);
log_messages.push((Level::Info, format!("loglevel is now {}", log_level)));
// set format
builder.format(|buf, record| {
match record.level() {
Level::Trace => writeln!(buf, "[ {} ] {}", format!("{}", record.level()).yellow(), record.args()),
Level::Debug => writeln!(buf, "[ {} ] {}", format!("{}", record.level()).green(), record.args()),
Level::Info => writeln!(buf, "[ {} ] {}", format!("{}", record.level()).blue(), record.args()),
Level::Warn => writeln!(buf, "[ {} ] {}", format!("{}", record.level()).truecolor(245, 164, 66), record.args()),
Level::Error => writeln!(buf, "[ {} ] {}", format!("{}", record.level()).red(), record.args()),
}
});
builder.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 test_logger() {
error!("hi from error");
warn!("hi from warn");
info!("hi from info");
debug!("hi from debug");
trace!("hi from trace");
}
fn level_from_env(var_name: &str, log_level: LevelFilter) -> (Option<log::LevelFilter>, Vec<(Level, String)>) {
let mut log_messages: Vec<(Level, String)> = Vec::new();
let var_value = env::var(var_name);
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);
}
fn cli_matches() -> clap::ArgMatches {
let store_arg = Arg::new("store")
.short('s')
.long("store")
.help("The folder used as the Itemstore");
let main = Command::new(APPNAME)
.version(crate_version!())
.author("Sebastian Moser")
.about("The MiZe Command line tool")
.arg(Arg::new("verbose")
.long("verbose")
.short('v')
.action(ArgAction::Count)
)
.arg(Arg::new("log-level")
.long("log-level")
.value_name("LOGLEVEL")
.help("set the log-level to one of OFF, ERROR, WARN, INFO, DEBUG, TRACE")
)
.arg(Arg::new("silent")
.long("silent")
.action(ArgAction::SetTrue)
.help("set the log-level to OFF")
)
.subcommand(
Command::new("daemon")
.aliases(["da"])
.arg(&store_arg)
.about("Starts the mize daemon. Has subcommands to stop, etc")
.subcommand(Command::new("stop")
.about("Stops the mize daemon if one is running")
)
.subcommand(Command::new("kill"))
.about("Stops the mize daemon with a signal")
.alias("k")
.subcommand(Command::new("status")
.alias("st")
)
)
.subcommand(
Command::new("serve")
.aliases(["se"])
.arg(&store_arg)
)
.subcommand(
Command::new("mount")
.aliases(["m"])
.arg(&store_arg)
)
.subcommand(
Command::new("get")
.aliases(["g"])
.arg(&store_arg)
.arg(Arg::new("id").help("The id or path to get"))
)
.subcommand(
Command::new("set")
.aliases(["s"])
.arg(&store_arg)
.arg(Arg::new("id").help("The id or path to set"))
)
.subcommand(
Command::new("print")
.aliases(["p"])
.arg(&store_arg)
.arg(Arg::new("id").help("The id or path to print"))
)
.subcommand(
Command::new("call")
.aliases(["c"])
.arg(&store_arg)
)
.arg_required_else_help(true);
return main.get_matches();
}

7
src/proto.rs Normal file
View File

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

View File

@@ -0,0 +1,7 @@
{
"name": "instance",
"paths": [
[["daemon", "pid"], "int"],
[["daemon", "log", "blob"]]
]
}