towards the next try of mize...
This commit is contained in:
4
src/cli/call.rs
Normal file
4
src/cli/call.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
use clap::ArgMatches;
|
||||
|
||||
pub async fn call(sub_matches: &ArgMatches) {
|
||||
}
|
||||
63
src/cli/daemon.rs
Normal file
63
src/cli/daemon.rs
Normal 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
23
src/cli/get.rs
Normal 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
16
src/cli/mod.rs
Normal 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
4
src/cli/mount.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
use clap::ArgMatches;
|
||||
|
||||
pub async fn mount(sub_matches: &ArgMatches) {
|
||||
}
|
||||
4
src/cli/print.rs
Normal file
4
src/cli/print.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
use clap::ArgMatches;
|
||||
|
||||
pub async fn print(sub_matches: &ArgMatches) {
|
||||
}
|
||||
4
src/cli/serve.rs
Normal file
4
src/cli/serve.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
use clap::ArgMatches;
|
||||
|
||||
pub async fn serve(sub_matches: &ArgMatches) {
|
||||
}
|
||||
4
src/cli/set.rs
Normal file
4
src/cli/set.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
use clap::ArgMatches;
|
||||
|
||||
pub async fn set(sub_matches: &ArgMatches) {
|
||||
}
|
||||
271
src/error.rs
Normal file
271
src/error.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
74
src/instance/connection.rs
Normal file
74
src/instance/connection.rs
Normal 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
213
src/instance/mod.rs
Normal 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
1
src/instance/peer.rs
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
239
src/instance/web.rs
Normal file
239
src/instance/web.rs
Normal 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
234
src/item.rs
Normal 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
267
src/itemstore.rs
Normal 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
16
src/lib.rs
Normal 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
325
src/main.rs
Normal 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
7
src/proto.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
use serde_cbor::Value as CborValue;
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MizeMessage {
|
||||
value: CborValue,
|
||||
}
|
||||
Reference in New Issue
Block a user