#![doc(html_favicon_url = "https://codemelted.com/favicon.png")]
#![doc(html_logo_url =
"https://codemelted.com/assets/favicon/apple-touch-icon.png")]
#![doc = include_str!("README.md")]
pub trait CProtocolHandler<T> {
fn id(&mut self) -> String;
fn get_message(&mut self, request: Option<&str>) -> Result<T, std::io::Error>;
fn is_running(&self) -> bool;
fn post_message(&mut self, data: T) -> Result<(), std::io::Error>;
fn terminate(&mut self);
}
pub type CTaskCB<T> = fn(Option<T>) -> Option<T>;
pub struct CTaskResult<T> {
handle: std::thread::JoinHandle<()>,
recv: std::sync::mpsc::Receiver<Option<T>>,
}
impl<T: std::marker::Send + 'static> CTaskResult<T> {
fn new(task: CTaskCB<T>, data: Option<T>, delay: u64) -> CTaskResult<T> {
let (tx, rx) = std::sync::mpsc::channel::<Option<T>>();
let handle = std::thread::spawn(move || {
async_sleep(delay);
let result = task(data);
let _ = tx.send(result);
});
CTaskResult { handle, recv: rx }
}
pub fn has_completed(&self) -> bool {
self.handle.is_finished()
}
pub fn value(&self) -> Option<T> {
let result = self.recv.recv();
match result {
Ok(v) => {
crate::async_sleep(100);
v
},
Err(_) => None,
}
}
}
pub type CTimerCB = fn();
pub struct CTimerResult {
handle: std::thread::JoinHandle<()>,
sender: std::sync::mpsc::Sender<bool>
}
impl CTimerResult {
fn new(task: CTimerCB, interval: u64) -> CTimerResult {
let (tx, rx) = std::sync::mpsc::channel::<bool>();
let handle = std::thread::spawn(move || {
loop {
async_sleep(interval);
task();
let result = rx.try_recv();
if result.is_ok() {
let stop_thread = result.ok().unwrap();
if stop_thread {
break;
}
}
}
});
CTimerResult { handle, sender: tx }
}
pub fn is_running(&self) -> bool {
!self.handle.is_finished()
}
pub fn stop(&self) {
self.sender.send(true).unwrap();
loop {
async_sleep(100);
if !self.is_running() {
break;
}
}
}
}
pub type CWorkerCB<T> = fn(T) -> T;
pub struct CWorkerProtocol<T> {
id: String,
handle: std::thread::JoinHandle<()>,
protocol_tx: Option<std::sync::mpsc::Sender<T>>,
protocol_rx: std::sync::mpsc::Receiver<T>,
}
impl<T: std::marker::Send + 'static> CWorkerProtocol<T> {
fn new(id: &str, task: CWorkerCB<T>) -> CWorkerProtocol<T> {
let (protocol_tx, thread_rx) = std::sync::mpsc::channel::<T>();
let (thread_tx, protocol_rx) = std::sync::mpsc::channel::<T>();
let handle = std::thread::spawn(move || {
loop {
match thread_rx.recv() {
Ok(v) => {
let result = task(v);
let _ = thread_tx.send(result);
},
Err(_) => break,
}
}
});
CWorkerProtocol::<T> {
id: id.to_string(),
handle,
protocol_tx: Some(protocol_tx),
protocol_rx,
}
}
}
impl<T> CProtocolHandler<Option<T>> for CWorkerProtocol<Option<T>> {
fn id(&mut self) -> String {
self.id.to_string()
}
fn get_message(
&mut self,
_request: Option<&str>
) -> Result<Option<T>, std::io::Error> {
match self.protocol_rx.try_recv() {
Ok(v) => Ok(v),
Err(why) => {
match why {
std::sync::mpsc::TryRecvError::Empty => Ok(None),
std::sync::mpsc::TryRecvError::Disconnected => {
Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
why.to_string()
))
},
}
},
}
}
fn is_running(&self) -> bool {
!self.handle.is_finished()
}
fn post_message(&mut self, data: Option<T>) -> Result<(), std::io::Error> {
match self.protocol_tx.as_ref().unwrap().send(data) {
Ok(_) => Ok(()),
Err(why) => {
Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
why.to_string()
))
},
}
}
fn terminate(&mut self) {
self.protocol_tx = None;
loop {
async_sleep(100);
if !self.is_running() {
break;
}
}
}
}
pub fn async_sleep(delay: u64) {
let delay = std::time::Duration::from_millis(delay);
std::thread::sleep(delay);
}
#[doc = simple_mermaid::mermaid!("models/codemelted_async.mmd")]
pub fn async_task<T: std::marker::Send + 'static>(
task: CTaskCB<T>,
data: Option<T>,
delay: u64
) -> CTaskResult<T> {
CTaskResult::new(task, data, delay)
}
#[doc = simple_mermaid::mermaid!("models/codemelted_async.mmd")]
pub fn async_timer(task: CTimerCB, interval: u64) -> CTimerResult {
CTimerResult::new(task, interval)
}
#[doc = simple_mermaid::mermaid!("models/codemelted_async.mmd")]
pub fn async_worker<T: std::marker::Send + 'static>(
id: &str,
task: CWorkerCB<T>,
) -> CWorkerProtocol<T> {
CWorkerProtocol::new(id, task)
}
fn console_read(prompt: &str) -> String {
let mut answer = String::new();
print!("{}", prompt);
let _ = std::io::Write::flush(&mut std::io::stdout());
let _ = std::io::stdin().read_line(&mut answer);
String::from(answer.trim())
}
fn console_write_stdout(message: &str, use_new_line: bool) {
if use_new_line {
println!("{}", message);
} else {
print!("{}", message);
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_console.mmd")]
pub fn console_alert(message: &str) {
let msg = match message {
"" => "[ENTER]: ",
_ => &format!("{} [ENTER]: ", message),
};
console_read(msg);
}
#[doc = simple_mermaid::mermaid!("models/codemelted_console.mmd")]
pub fn console_confirm(message: &str) -> bool {
let msg = match message {
"" => "CONFIRM [y/N]: ",
_ => &format!("{} CONFIRM [y/N]: ", message),
};
let answer: String = console_read(msg);
CObject::is_truthy(&answer)
}
#[doc = simple_mermaid::mermaid!("models/codemelted_console.mmd")]
pub fn console_choose(message: &str, choices: &[&str]) -> u32 {
let msg = match message {
"" => "CHOOSE",
_ => &format!("{}", message),
};
let options = choices;
let answer: u32;
loop {
println!("{}", "-".repeat(msg.chars().count()));
println!("{}", msg);
println!("{}", "-".repeat(msg.chars().count()));
let mut x = -1;
for option in options {
x += 1;
println!("{}. {}", x, option);
}
println!("");
let selection = console_read("Make a Selection: ");
match selection.trim().parse::<u32>() {
Ok(n) => {
if n < options.len().try_into().unwrap() {
answer = n;
break;
} else {
println!("");
println!("ERROR: Invalid selection, please try again.");
println!("");
}
},
Err(_e) => {
println!("");
println!("ERROR: Invalid selection, please try again.");
println!("");
}
}
}
answer
}
#[doc = simple_mermaid::mermaid!("models/codemelted_console.mmd")]
pub fn console_password(message: &str) -> String {
let msg = match message {
"" => "PASSWORD: ",
_ => &format!("{}: ", message),
};
let password = rpassword::prompt_password(msg).unwrap();
String::from(password)
}
#[doc = simple_mermaid::mermaid!("models/codemelted_console.mmd")]
pub fn console_prompt(message: &str) -> String {
let msg = match message {
"" => "PROMPT: ",
_ => &format!("{}: ", message),
};
let answer: String = console_read(msg);
String::from(answer)
}
#[doc = simple_mermaid::mermaid!("models/codemelted_console.mmd")]
pub fn console_write(message: &str) {
console_write_stdout(message, false);
}
#[doc = simple_mermaid::mermaid!("models/codemelted_console.mmd")]
pub fn console_writeln(message: &str) {
console_write_stdout(message, true);
}
#[doc = simple_mermaid::mermaid!("models/codemelted_db.mmd")]
pub fn db_exists(db_path: &str, should_panic: bool) -> bool {
match disk_exists(db_path, CDiskType::File) {
true => true,
false => {
if should_panic {
panic!("SyntaxError: {} database does not exist!", db_path);
}
false
},
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_db.mmd")]
pub fn db_manage(db_path: &str, create_db: bool, sql: Option<&str>) {
if !create_db {
db_exists(db_path, true);
}
match rusqlite::Connection::open(db_path) {
Ok(conn) => {
match sql {
Some(stmt) => {
match conn.execute(stmt, ()) {
Ok(_) => {},
Err(why) => panic!("SyntaxError: db_manage {}", why),
}
},
None => {},
}
},
Err(why) => panic!("SyntaxError: db_manage {}", why),
};
}
#[doc = simple_mermaid::mermaid!("models/codemelted_db.mmd")]
pub fn db_query<
P: rusqlite::Params,
T,
F: FnMut(&rusqlite::Row<'_>) -> rusqlite::Result<T>>(
db_path: &str,
sql: &str,
params: P,
f: F
) -> Vec<T> {
db_exists(db_path, true);
match rusqlite::Connection::open(db_path) {
Ok(conn) => {
match conn.prepare(sql) {
Ok(mut stmt) => {
match stmt.query_map(params, f) {
Ok(rows) => {
let mut results = Vec::<T>::new();
for row in rows {
match row {
Ok(v) => results.push(v),
Err(why) => panic!(
"SyntaxError: db_query {}",
why
),
}
}
results
},
Err(why) => panic!("SyntaxError: db_query {}", why),
}
},
Err(why) => panic!("SyntaxError: db_query {}", why),
}
},
Err(why) => panic!("SyntaxError: db_query {}", why),
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_db.mmd")]
pub fn db_update<P: rusqlite::Params>(
db_path: &str,
sql: &str,
params: P
) -> usize {
db_exists(db_path, true);
match rusqlite::Connection::open(db_path) {
Ok(conn) => {
match conn.prepare(sql) {
Ok(mut stmt) => {
match stmt.execute(params) {
Ok(v) => v,
Err(why) => panic!("SyntaxError: db_update {}", why),
}
},
Err(why) => panic!("SyntaxError: db_update {}", why),
}
},
Err(why) => panic!("SyntaxError: db_update {}", why),
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_db.mmd")]
pub fn db_version() -> String {
rusqlite::version().to_string()
}
pub enum CDiskType {
Either,
Directory,
File,
}
pub enum CFileContents {
Bytes(Vec<u8>),
String(String),
}
impl CFileContents {
pub fn as_bytes(&self) -> Option<Vec<u8>> {
match self {
CFileContents::Bytes(items) => Some(items.to_owned()),
CFileContents::String(_) => None,
}
}
pub fn as_string(&self) -> Option<String> {
match self {
CFileContents::Bytes(_) => None,
CFileContents::String(v) => Some(v.to_owned()),
}
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_disk.mmd")]
pub fn disk_cp(src: &str, dest: &str) -> Result<(), std::io::Error> {
let result = std::fs::copy(src, dest);
match result {
Ok(_) => Ok(()),
Err(why) => Err(why),
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_disk.mmd")]
pub fn disk_exists(src: &str, disk_type: CDiskType) -> bool {
match disk_type {
CDiskType::Either => std::path::Path::new(src).is_dir()
|| std::path::Path::new(src).is_file(),
CDiskType::Directory => std::path::Path::new(src).is_dir(),
CDiskType::File => std::path::Path::new(src).is_file(),
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_disk.mmd")]
pub fn disk_ls(src: &str) -> Result<std::fs::ReadDir, std::io::Error> {
std::fs::read_dir(src)
}
#[doc = simple_mermaid::mermaid!("models/codemelted_disk.mmd")]
pub fn disk_metadata(
src: &str
) -> Result<std::fs::Metadata, std::io::Error> {
std::path::Path::new(src).metadata()
}
#[doc = simple_mermaid::mermaid!("models/codemelted_disk.mmd")]
pub fn disk_mkdir(src: &str) -> Result<(), std::io::Error> {
std::fs::create_dir_all(src)
}
#[doc = simple_mermaid::mermaid!("models/codemelted_disk.mmd")]
pub fn disk_mv(src: &str, dest: &str) -> Result<(), std::io::Error> {
let result = std::fs::rename(src, dest);
match result {
Ok(_) => Ok(()),
Err(why) => Err(why),
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_disk.mmd")]
pub fn disk_read_file(
filename: &str,
is_string: bool,
) -> Result<CFileContents, std::io::Error> {
let file = std::fs::File::open(filename);
match file {
Ok(_) => {
let mut data = Vec::new();
let result = std::io::Read::read_to_end(&mut file?, &mut data);
match result {
Ok(_) => {
if !is_string {
Ok(CFileContents::Bytes(data.to_owned()))
} else {
match String::from_utf8(data) {
Ok(v) => Ok(CFileContents::String(v)),
Err(why) => Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
why.to_string()
)),
}
}
},
Err(why) => Err(why),
}
},
Err(why) => Err(why),
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_db.mmd")]
pub fn disk_rm(src: &str) -> Result<(), std::io::Error> {
let is_file = disk_exists(src, CDiskType::File);
if is_file {
let result = std::fs::remove_file(src);
match result {
Ok(_) => return Ok(()),
Err(why) => return Err(why),
}
} else {
let result = std::fs::remove_dir_all(src,);
match result {
Ok(_) => return Ok(()),
Err(why) => return Err(why),
}
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_disk.mmd")]
pub fn disk_write_file(
filename: &str,
data: CFileContents,
append: bool,
) -> Result<(), std::io::Error> {
let mut file = if append {
std::fs::OpenOptions::new()
.append(true)
.create(true)
.open(filename)?
} else {
std::fs::OpenOptions::new()
.write(true)
.create(true)
.open(filename)?
};
match data {
CFileContents::Bytes(data) => {
let _ = std::io::Write::write_all(&mut file, &data)?;
},
CFileContents::String(data) => {
let _ = std::io::Write::write_all(&mut file, data.as_bytes())?;
},
}
Ok(())
}
pub struct CBluetoothInfo {
peripheral: btleplug::platform::Peripheral,
}
impl CBluetoothInfo {
fn new(peripheral: btleplug::platform::Peripheral) -> CBluetoothInfo {
CBluetoothInfo { peripheral }
}
pub fn address(&self) -> String {
btleplug::api::Peripheral::address(&self.peripheral).to_string()
}
pub fn id(&self) -> String {
btleplug::api::Peripheral::id(&self.peripheral).to_string()
}
}
#[derive(Debug)]
pub enum CSerialPortData {
BaudRate(Option<u32>),
DataBits(Option<serialport::DataBits>),
FlowControl(Option<serialport::FlowControl>),
Parity(Option<serialport::Parity>),
StopBits(Option<serialport::StopBits>),
Timeout(Option<std::time::Duration>),
Break(Option<bool>),
ClearBuffer(Option<serialport::ClearBuffer>),
CarrierDetect(Option<bool>),
ClearToSend(Option<bool>),
DataSetReady(Option<bool>),
DataTerminalReady(Option<bool>),
RequestToSend(Option<bool>),
RingIndicator(Option<bool>),
DataBytes(Option<Vec<u8>>),
}
impl CSerialPortData {
pub fn as_bool(&self) -> Option<bool> {
match self {
CSerialPortData::Break(v) => *v,
CSerialPortData::CarrierDetect(v) => *v,
CSerialPortData::ClearToSend(v) => *v,
CSerialPortData::DataSetReady(v) => *v,
CSerialPortData::DataTerminalReady(v) => *v,
CSerialPortData::RequestToSend(v) => *v,
CSerialPortData::RingIndicator(v) => *v,
_ => None
}
}
pub fn as_bytes(&self) -> Option<Vec<u8>> {
match self {
CSerialPortData::DataBytes(items) => items.clone(),
_ => None,
}
}
pub fn as_data_bits(&self) -> Option<serialport::DataBits> {
match self {
CSerialPortData::DataBits(data_bits) => *data_bits,
_ => None,
}
}
pub fn as_flow_control(&self) -> Option<serialport::FlowControl> {
match self {
CSerialPortData::FlowControl(flow_control) => *flow_control,
_ => None,
}
}
pub fn as_parity(&self) -> Option<serialport::Parity> {
match self {
CSerialPortData::Parity(parity) => *parity,
_ => None,
}
}
pub fn as_stop_bits(&self) -> Option<serialport::StopBits> {
match self {
CSerialPortData::StopBits(stop_bits) => *stop_bits,
_ => None,
}
}
pub fn as_timeout(&self) -> Option<std::time::Duration> {
match self {
CSerialPortData::Timeout(v) => *v,
_ => None,
}
}
pub fn as_u32(&self) -> Option<u32> {
match self {
CSerialPortData::BaudRate(v) => *v,
_ => None,
}
}
pub fn get_message_request(&self) -> Option<&str> {
match self {
CSerialPortData::BaudRate(_) => Some("baud_rate"),
CSerialPortData::DataBits(_) => Some("data_bits"),
CSerialPortData::FlowControl(_) => Some("flow_control"),
CSerialPortData::Parity(_) => Some("parity"),
CSerialPortData::StopBits(_) => Some("stop_bits"),
CSerialPortData::Timeout(_) => Some("duration"),
CSerialPortData::CarrierDetect(_) => Some("carrier_detect"),
CSerialPortData::ClearToSend(_) => Some("clear_to_send"),
CSerialPortData::DataSetReady(_) => Some("data_set_ready"),
CSerialPortData::RingIndicator(_) => Some("ring_indicator"),
CSerialPortData::DataBytes(_) => Some("data_bytes"),
_ => None,
}
}
}
pub struct CSerialPortProtocol {
port: Option<Box<dyn serialport::SerialPort>>,
}
impl CSerialPortProtocol {
fn new(port_info: &serialport::SerialPortInfo) -> CSerialPortProtocol {
let port = serialport::new(
port_info.port_name.to_string(),
9600
).open().expect("CSerialPortProtocol: Failed to open!");
CSerialPortProtocol { port: Some(port) }
}
fn port_ref(&mut self) -> &mut Box<dyn serialport::SerialPort + 'static> {
self.port.as_mut().unwrap()
}
}
impl CProtocolHandler<CSerialPortData> for CSerialPortProtocol {
fn id(&mut self) -> String {
match self.port_ref().name() {
Some(v) => v,
None => String::from(""),
}
}
fn get_message(
&mut self,
request: Option<&str>
) -> Result<CSerialPortData, std::io::Error> {
if request.unwrap() == "baud_rate" {
match self.port_ref().baud_rate() {
Ok(v) => Ok(CSerialPortData::BaudRate(Some(v))),
Err(why) => Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
why.to_string()
)),
}
} else if request.unwrap() == "data_bits" {
match self.port_ref().baud_rate() {
Ok(v) => Ok(CSerialPortData::BaudRate(Some(v))),
Err(why) => Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
why.to_string()
)),
}
} else if request.unwrap() == "flow_control" {
match self.port_ref().flow_control() {
Ok(v) => Ok(CSerialPortData::FlowControl(Some(v))),
Err(why) => Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
why.to_string()
)),
}
} else if request.unwrap() == "parity" {
match self.port_ref().parity() {
Ok(v) => Ok(CSerialPortData::Parity(Some(v))),
Err(why) => Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
why.to_string()
)),
}
} else if request.unwrap() == "stop_bits" {
match self.port_ref().stop_bits() {
Ok(v) => Ok(CSerialPortData::StopBits(Some(v))),
Err(why) => Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
why.to_string()
)),
}
} else if request.unwrap() == "timeout" {
Ok(CSerialPortData::Timeout(Some(self.port_ref().timeout())))
} else if request.unwrap() == "carrier_detect" {
match self.port_ref().read_carrier_detect() {
Ok(v) => Ok(CSerialPortData::CarrierDetect(Some(v))),
Err(why) => Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
why.to_string()
)),
}
} else if request.unwrap() == "clear_to_send" {
match self.port_ref().read_clear_to_send() {
Ok(v) => Ok(CSerialPortData::ClearToSend(Some(v))),
Err(why) => Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
why.to_string()
)),
}
} else if request.unwrap() == "data_set_ready" {
match self.port_ref().read_data_set_ready() {
Ok(v) => Ok(CSerialPortData::DataSetReady(Some(v))),
Err(why) => Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
why.to_string()
)),
}
} else if request.unwrap() == "ring_indicator" {
match self.port_ref().read_ring_indicator() {
Ok(v) => Ok(CSerialPortData::RingIndicator(Some(v))),
Err(why) => Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
why.to_string()
)),
}
} else if request.unwrap() == "data_bytes" {
match self.port_ref().bytes_to_read() {
Ok(buffer_size) => {
let mut buffer = vec![0; buffer_size as usize];
match self.port_ref().read_exact(&mut buffer) {
Ok(_) => Ok(CSerialPortData::DataBytes(Some(buffer))),
Err(why) => Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
why.to_string()
)),
}
},
Err(why) => Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
why.to_string()
)),
}
} else {
panic!(
"CSerialPortProtocol::get_message() received an invalid request!"
);
}
}
fn is_running(&self) -> bool {
self.port.is_some()
}
fn post_message(
&mut self,
data: CSerialPortData
) -> Result<(), std::io::Error> {
let result = match data {
CSerialPortData::BaudRate(v) => {
self.port_ref().set_baud_rate(v.unwrap())
},
CSerialPortData::DataBits(data_bits) => {
self.port_ref().set_data_bits(data_bits.unwrap())
},
CSerialPortData::FlowControl(flow_control) => {
self.port_ref().set_flow_control(flow_control.unwrap())
},
CSerialPortData::Parity(parity) => {
self.port_ref().set_parity(parity.unwrap())
},
CSerialPortData::StopBits(stop_bits) => {
self.port_ref().set_stop_bits(stop_bits.unwrap())
},
CSerialPortData::Timeout(duration) => {
self.port_ref().set_timeout(duration.unwrap())
},
CSerialPortData::DataTerminalReady(v) => {
self.port_ref().write_data_terminal_ready(v.unwrap())
},
CSerialPortData::RequestToSend(v) => {
self.port_ref().write_request_to_send(v.unwrap())
},
CSerialPortData::DataBytes(data) => {
match self.port_ref().write_all(&data.unwrap()) {
Ok(_) => {
match self.port_ref().flush() {
Ok(_) => Ok(()),
Err(why) => {
Err(serialport::Error::new(
serialport::ErrorKind::Io(why.kind()),
why.to_string())
)
}
}
},
Err(why) => {
Err(serialport::Error::new(
serialport::ErrorKind::Io(why.kind()),
why.to_string())
)
}
}
},
CSerialPortData::Break(v) =>{
if v.unwrap() {
self.port_ref().set_break()
} else {
self.port_ref().clear_break()
}
},
CSerialPortData::ClearBuffer(v) => {
self.port_ref().clear(v.unwrap())
},
_ => {
panic!(
"CSerialPortProtocol::post_message(): received invalid data!"
);
}
};
match result {
Ok(_) => Ok(()),
Err(why) => Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
why.to_string()
))
}
}
fn terminate(&mut self) {
self.port = None;
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_hw.mmd")]
pub fn hw_available_bluetooth_devices(scan_time_milliseconds: u64)
-> Result<Vec<CBluetoothInfo>, btleplug::Error> {
let rt = tokio::runtime::Runtime::new().unwrap();
let devices: Result<Vec<CBluetoothInfo>, btleplug::Error> =
rt.block_on(async {
let manager = btleplug::platform::Manager::new().await?;
let adapters = btleplug::api::Manager::adapters(&manager).await?;
let mut devices = Vec::<CBluetoothInfo>::new();
let central = adapters.into_iter().nth(0);
if central.is_none() {
return Ok(devices);
}
let adapter = central.unwrap();
btleplug::api::Central::start_scan(
&adapter,
btleplug::api::ScanFilter::default()
).await?;
async_sleep(scan_time_milliseconds);
let peripherals = btleplug::api::Central::peripherals(&adapter).await?;
for p in peripherals {
devices.push(CBluetoothInfo::new(p));
}
btleplug::api::Central::stop_scan(&adapter).await?;
Ok(devices)
});
devices
}
#[doc = simple_mermaid::mermaid!("models/codemelted_hw.mmd")]
pub fn hw_available_serial_ports() -> Result<
Vec<serialport::SerialPortInfo>, serialport::Error> {
serialport::available_ports()
}
pub fn hw_open_bluetooth_device() {
unimplemented!("FUTURE IMPLEMENTATION!");
}
#[doc = simple_mermaid::mermaid!("models/codemelted_hw.mmd")]
pub fn hw_open_serial_port(
port_info: &serialport::SerialPortInfo
) -> CSerialPortProtocol {
CSerialPortProtocol::new(port_info)
}
pub enum CDataType {
Array,
Boolean,
Empty,
Null,
Number,
Object,
String,
}
pub trait CTruthyString {
fn as_truthy(&self) -> bool;
fn is_truthy(data: &str) -> bool;
}
pub type CObject = json::JsonValue;
impl CTruthyString for CObject {
fn as_truthy(&self) -> bool {
CObject::is_truthy(self.as_str().unwrap())
}
fn is_truthy(data: &str) -> bool {
let true_strings: [&'static str; 9] = [
"true",
"1",
"t",
"y",
"yes",
"yeah",
"yup",
"certainly",
"uh-huh",
];
let data_check = String::from(data.to_lowercase());
for el in true_strings {
if el.contains(&data_check) {
return true;
}
}
false
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_json.mmd")]
pub fn json_as_bool(data: &CObject) -> bool {
let answer = data.as_bool();
match answer {
Some(v) => v,
_ => false,
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_json.mmd")]
pub fn json_as_number(data: &CObject) -> Option<json::number::Number>{
data.as_number()
}
#[doc = simple_mermaid::mermaid!("models/codemelted_json.mmd")]
pub fn json_as_string(data: &CObject) -> Option<String> {
let answer = data.as_str();
match answer {
Some(v) => Some(String::from(v)),
_ => None,
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_json.mmd")]
pub fn json_check_type(
data_type: CDataType,
data: &CObject,
should_panic: bool,
) -> bool {
let answer = match data_type {
CDataType::Array => data.is_array(),
CDataType::Boolean => data.is_boolean(),
CDataType::Empty => data.is_empty(),
CDataType::Null => data.is_null(),
CDataType::Number => data.is_number(),
CDataType::Object => data.is_object(),
CDataType::String => data.is_string(),
};
if should_panic && !answer {
panic!("ERROR: json_check_type() failed.");
}
answer
}
#[doc = simple_mermaid::mermaid!("models/codemelted_json.mmd")]
pub fn json_create_array() -> CObject {
CObject::new_array()
}
#[doc = simple_mermaid::mermaid!("models/codemelted_json.mmd")]
pub fn json_create_object() -> CObject {
CObject::new_object()
}
#[doc = simple_mermaid::mermaid!("models/codemelted_json.mmd")]
pub fn json_has_key(
data: &CObject,
key: &str,
should_panic: bool,
) -> bool {
let answer = data.has_key(key);
if should_panic && !answer {
panic!("ERROR: json_has_key() failed.");
}
answer
}
#[doc = simple_mermaid::mermaid!("models/codemelted_json.mmd")]
pub fn json_parse(data: &str) -> Option<CObject> {
let answer = json::parse(&data);
match answer {
Ok(v) => Some(v),
_ => None,
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_json.mmd")]
pub fn json_stringify(data: CObject) -> String {
json::stringify(data)
}
#[doc = simple_mermaid::mermaid!("models/codemelted_json.mmd")]
pub fn json_valid_url(
data: &str,
should_panic: bool,
) -> bool {
let answer = !(url::Url::parse(data).is_err());
if should_panic && !answer {
panic!("ERROR: json_valid_url() failed.");
}
answer
}
static LOG_LEVEL: std::sync::Mutex<CLogLevel> = std::sync::Mutex::new(
CLogLevel::Warning
);
static LOG_HANDLER: std::sync::Mutex<Option<CLoggedEventHandler>> =
std::sync::Mutex::new(None);
#[derive(Clone, PartialEq, Debug)]
pub enum CLogLevel {
Debug,
Info,
Warning,
Error,
Off,
}
impl CLogLevel {
pub fn as_string(&self) -> String {
match self {
CLogLevel::Debug => String::from("DEBUG"),
CLogLevel::Info => String::from("INFO"),
CLogLevel::Warning => String::from("WARNING"),
CLogLevel::Error => String::from("ERROR"),
CLogLevel::Off => String::from("OFF"),
}
}
pub fn as_int(&self) -> u8 {
match self {
CLogLevel::Debug => 0,
CLogLevel::Info => 1,
CLogLevel::Warning => 2,
CLogLevel::Error => 3,
CLogLevel::Off => 4,
}
}
}
pub struct CLogRecord {
time_stamp: chrono::DateTime<chrono::Utc>,
log_level: CLogLevel,
data: String,
}
impl CLogRecord {
fn new(log_level: CLogLevel, data: &str) -> CLogRecord {
CLogRecord {
time_stamp: chrono::Utc::now(),
log_level,
data: String::from(data),
}
}
pub fn get_time_stamp(&self) -> &chrono::DateTime<chrono::Utc> {
&self.time_stamp
}
pub fn get_log_level(&self) -> &CLogLevel {
&self.log_level
}
pub fn get_data(&self) -> &str {
&self.data
}
pub fn as_string(&self) -> String {
format!(
"{} [{}]: {}",
self.get_time_stamp().format("%Y-%b-%d %H:%M:%S.%3f"),
self.get_log_level().as_string(),
self.get_data()
)
}
}
pub type CLoggedEventHandler = fn(CLogRecord);
#[doc = simple_mermaid::mermaid!("models/codemelted_logger.mmd")]
pub fn logger_get_log_level() -> CLogLevel {
let data = LOG_LEVEL.lock().unwrap();
data.clone()
}
#[doc = simple_mermaid::mermaid!("models/codemelted_logger.mmd")]
pub fn logger_set_log_level(log_level: CLogLevel) {
let mut data = LOG_LEVEL.lock().unwrap();
*data = log_level;
}
#[doc = simple_mermaid::mermaid!("models/codemelted_logger.mmd")]
pub fn logger_get_log_handler() -> Option<CLoggedEventHandler> {
let data = LOG_HANDLER.lock().unwrap();
*data
}
#[doc = simple_mermaid::mermaid!("models/codemelted_logger.mmd")]
pub fn logger_set_log_handler(handler: Option<CLoggedEventHandler>) {
let mut data = LOG_HANDLER.lock().unwrap();
*data = handler;
}
#[doc = simple_mermaid::mermaid!("models/codemelted_logger.mmd")]
pub fn logger_log(level: CLogLevel, data: &str) {
let logger_level = logger_get_log_level();
if logger_level == CLogLevel::Off {
return
}
let record = CLogRecord::new(level, data);
if record.get_log_level().as_int() >= logger_level.as_int() {
println!("{}", record.as_string())
}
let log_handler = logger_get_log_handler();
if let Some(v) = log_handler {
v(record);
}
}
pub trait CCsvFormat {
fn system_id() -> String {
format!(
"{} / {} / {}",
runtime_os_name(),
runtime_os_version(),
runtime_kernel_version(),
)
}
fn csv_header(&self) -> String;
fn as_csv(&self) -> String;
}
pub struct CComponentMonitor {
components: sysinfo::Components
}
impl CComponentMonitor {
fn new() -> CComponentMonitor {
let mut components = sysinfo::Components::new_with_refreshed_list();
components.refresh(true);
CComponentMonitor { components }
}
pub fn refresh(&mut self) {
self.components.refresh(true);
}
pub fn len(&self) -> usize {
self.components.len()
}
pub fn label(&self, index: usize) -> String {
match self.components.get(index) {
Some(v) => {
v.label().to_string()
},
None => panic!("SyntaxError: invalid index specified."),
}
}
pub fn temp_current_c(&self, index: usize) -> f32 {
match self.components.get(index) {
Some(v) => {
match v.temperature() {
Some(v) => v,
None => f32::NAN,
}
},
None => panic!("SyntaxError: invalid index specified."),
}
}
pub fn temp_max_c(&self, index: usize) -> f32 {
match self.components.get(index) {
Some(v) => {
match v.max() {
Some(v) => v,
None => f32::NAN,
}
},
None => panic!("SyntaxError: invalid index specified."),
}
}
pub fn temp_critical_c(&self, index: usize) -> f32 {
match self.components.get(index) {
Some(v) => {
match v.critical() {
Some(v) => v,
None => f32::NAN,
}
},
None => panic!("SyntaxError: invalid index specified."),
}
}
}
impl CCsvFormat for CComponentMonitor {
fn csv_header(&self) -> String {
format!(
"{},{},{},{},{}",
String::from("system_id"),
String::from("label"),
String::from("temp_current_c"),
String::from("temp_max_c"),
String::from("temp_critical_c"),
)
}
fn as_csv(&self) -> String {
let mut csv_data = String::new();
let mut x = 0;
while x < self.len() {
let data = format!(
"{},{},{},{},{}",
Self::system_id(),
self.label(x),
self.temp_current_c(x),
self.temp_max_c(x),
self.temp_critical_c(x),
);
csv_data.push_str(&data);
csv_data.push('\n');
x += 1
}
csv_data
}
}
pub struct CDiskMonitor {
disks: sysinfo::Disks
}
impl CDiskMonitor {
fn new() -> CDiskMonitor {
let mut disks = sysinfo::Disks::new_with_refreshed_list();
disks.refresh(true);
CDiskMonitor { disks }
}
pub fn len(&self) -> usize {
self.disks.len()
}
pub fn refresh(&mut self) {
self.disks.refresh(true);
}
pub fn name(&self, index: usize) -> String {
match self.disks.get(index) {
Some(v) => {
match v.name().to_str() {
Some(v) => String::from(v),
None => String::from("UNDETERMINED"),
}
},
None => panic!("SyntaxError: index out of range."),
}
}
pub fn disk_available_bytes(&self, index: usize) -> u64 {
match self.disks.get(index) {
Some(v) => v.available_space(),
None => panic!("SyntaxError: index out of range."),
}
}
pub fn disk_used_bytes(&self, index: usize) -> u64 {
self.disk_total_bytes(index) - self.disk_available_bytes(index)
}
pub fn disk_total_bytes(&self, index: usize) -> u64 {
match self.disks.get(index) {
Some(v) => v.total_space(),
None => panic!("SyntaxError: index out of range."),
}
}
pub fn disk_load(&self, index: usize) -> f32 {
let used_space = self.disk_used_bytes(index) as f32;
let total_space = self.disk_total_bytes(index) as f32;
(used_space / total_space) * 100.0
}
pub fn file_system(&self, index: usize) -> String {
match self.disks.get(index) {
Some(v) => {
match v.file_system().to_str() {
Some(v) => String::from(v),
None => String::from("UNDETERMINED"),
}
},
None => panic!("SyntaxError: index out of range."),
}
}
pub fn is_read_only(&self, index: usize) -> bool {
match self.disks.get(index) {
Some(v) => v.is_read_only(),
None => panic!("SyntaxError: index out of range."),
}
}
pub fn is_removable(&self, index: usize) -> bool {
match self.disks.get(index) {
Some(v) => v.is_removable(),
None => panic!("SyntaxError: index out of range."),
}
}
pub fn kind(&self, index: usize) -> String {
match self.disks.get(index) {
Some(v) => v.kind().to_string(),
None => panic!("SyntaxError: index out of range."),
}
}
pub fn mount_point(&self, index: usize) -> String {
match self.disks.get(index) {
Some(v) => {
match v.mount_point().to_str() {
Some(v) => String::from(v),
None => String::from("UNDETERMINED"),
}
},
None => panic!("SyntaxError: index out of range."),
}
}
}
impl CCsvFormat for CDiskMonitor {
fn csv_header(&self) -> String {
format!(
"{},{},{},{},{},{},{},{},{},{},{}",
String::from("system_id"),
String::from("name"),
String::from("disk_available_bytes"),
String::from("disk_used_bytes"),
String::from("disk_total_bytes"),
String::from("disk_load"),
String::from("file_system"),
String::from("is_readonly"),
String::from("is_removable"),
String::from("kind"),
String::from("mount_point"),
)
}
fn as_csv(&self) -> String {
let mut csv_data = String::new();
let mut x = 0;
while x < self.len() {
let data = format!(
"{},{},{},{},{},{},{},{},{},{},{}",
Self::system_id(),
self.name(x),
self.disk_available_bytes(x),
self.disk_used_bytes(x),
self.disk_total_bytes(x),
self.disk_load(x),
self.file_system(x),
self.is_read_only(x),
self.is_removable(x),
self.kind(x),
self.mount_point(x)
);
csv_data.push_str(&data);
csv_data.push('\n');
x += 1
}
csv_data
}
}
pub struct CNetworkMonitor {
networks: sysinfo::Networks
}
impl CNetworkMonitor {
fn new() -> CNetworkMonitor {
let networks = sysinfo::Networks::new_with_refreshed_list();
CNetworkMonitor { networks }
}
pub fn refresh(&mut self) {
self.networks.refresh(true);
}
pub fn names(&self) -> Vec<String> {
let mut names = Vec::<String>::new();
for (name, _network) in &self.networks {
names.push(name.to_string());
}
names
}
pub fn mac_address(&self, name: &str) -> String{
match self.networks.get(name) {
Some(v) => {
v.mac_address().to_string()
},
None => panic!("SyntaxError: Unknown name specified."),
}
}
pub fn mtu(&self, name: &str) -> u64{
match self.networks.get(name) {
Some(v) => {
v.mtu()
},
None => panic!("SyntaxError: Unknown name specified."),
}
}
pub fn network_total_rx_bytes(&self, name: &str) -> u64 {
match self.networks.get(name) {
Some(v) => {
v.total_received()
},
None => panic!("SyntaxError: Unknown name specified."),
}
}
pub fn network_total_rx_errors(&self, name: &str) -> u64 {
match self.networks.get(name) {
Some(v) => {
v.total_errors_on_received()
},
None => panic!("SyntaxError: Unknown name specified."),
}
}
pub fn network_total_rx_packets(&self, name: &str) -> u64 {
match self.networks.get(name) {
Some(v) => {
v.total_packets_received()
},
None => panic!("SyntaxError: Unknown name specified."),
}
}
pub fn network_total_tx_bytes(&self, name: &str) -> u64 {
match self.networks.get(name) {
Some(v) => {
v.total_transmitted()
},
None => panic!("SyntaxError: Unknown name specified."),
}
}
pub fn network_total_tx_errors(&self, name: &str) -> u64 {
match self.networks.get(name) {
Some(v) => {
v.total_errors_on_received()
},
None => panic!("SyntaxError: Unknown name specified."),
}
}
pub fn network_total_tx_packets(&self, name: &str) -> u64 {
match self.networks.get(name) {
Some(v) => {
v.total_packets_received()
},
None => panic!("SyntaxError: Unknown name specified."),
}
}
}
impl CCsvFormat for CNetworkMonitor {
fn csv_header(&self) -> String {
format!(
"{},{},{},{},{},{},{},{},{},{}",
String::from("system_id"),
String::from("name"),
String::from("mac_address"),
String::from("mtu"),
String::from("network_total_rx_bytes"),
String::from("network_total_rx_errors"),
String::from("network_total_rx_packets"),
String::from("network_total_tx_bytes"),
String::from("network_total_tx_errors"),
String::from("network_total_tx_packets"),
)
}
fn as_csv(&self) -> String {
let mut csv_data = String::new();
let names = self.names();
for name in names {
let data = format!(
"{},{},{},{},{},{},{},{},{},{}",
Self::system_id(),
name,
self.mac_address(&name),
self.mtu(&name),
self.network_total_rx_bytes(&name),
self.network_total_rx_errors(&name),
self.network_total_rx_packets(&name),
self.network_total_tx_bytes(&name),
self.network_total_tx_errors(&name),
self.network_total_tx_packets(&name),
);
csv_data.push_str(&data);
csv_data.push('\n');
}
csv_data
}
}
pub struct CPerformanceMonitor {
sys: sysinfo::System
}
impl CPerformanceMonitor {
fn new() -> CPerformanceMonitor {
let sys = sysinfo::System::new_all();
CPerformanceMonitor { sys }
}
pub fn refresh(&mut self) {
self.sys.refresh_memory();
self.sys.refresh_cpu_usage();
}
pub fn cpu_load(&self) -> f32 {
let mut count: f32 = 0.0;
let mut total: f32 = 0.0;
for cpu in self.sys.cpus() {
total += cpu.cpu_usage();
count += 1.0;
}
total / count
}
pub fn memory_available_bytes(&self) -> u64 {
self.sys.available_memory()
}
pub fn memory_free_bytes(&self) -> u64 {
self.sys.free_memory()
}
pub fn memory_used_bytes(&self) -> u64 {
self.sys.used_memory()
}
pub fn memory_total_bytes(&self) -> u64 {
self.sys.total_memory()
}
pub fn memory_load(&self) -> f32 {
let used_bytes = self.memory_used_bytes() as f32;
let total_bytes = self.memory_total_bytes() as f32;
(used_bytes / total_bytes) * 100.0
}
pub fn swap_free_bytes(&self) -> u64 {
self.sys.free_swap()
}
pub fn swap_used_bytes(&self) -> u64 {
self.sys.used_swap()
}
pub fn swap_total_bytes(&self) -> u64 {
self.sys.total_swap()
}
pub fn swap_load(&self) -> f32 {
let used_bytes = self.swap_used_bytes() as f32;
let total_bytes = self.swap_total_bytes() as f32;
(used_bytes / total_bytes) * 100.0
}
}
impl CCsvFormat for CPerformanceMonitor {
fn csv_header(&self) -> String {
format!(
"{},{},{},{},{},{},{},{},{},{},{},{},{}",
String::from("system_id"),
String::from("cpu_arch"),
String::from("cpu_count"),
String::from("cpu_load"),
String::from("memory_available_bytes"),
String::from("memory_free_bytes"),
String::from("memory_used_bytes"),
String::from("memory_total_bytes"),
String::from("memory_load"),
String::from("swap_free_bytes"),
String::from("swap_used_bytes"),
String::from("swap_total_bytes"),
String::from("swap_load")
)
}
fn as_csv(&self) -> String {
format!(
"{},{},{},{},{},{},{},{},{},{},{},{},{}",
Self::system_id(),
runtime_cpu_arch(),
runtime_cpu_count(),
self.cpu_load(),
self.memory_available_bytes(),
self.memory_free_bytes(),
self.memory_used_bytes(),
self.memory_total_bytes(),
self.memory_load(),
self.swap_free_bytes(),
self.swap_used_bytes(),
self.swap_total_bytes(),
self.swap_load()
)
}
}
pub struct CProcessMonitor {
sys: sysinfo::System
}
impl CProcessMonitor {
fn new() -> CProcessMonitor {
let sys = sysinfo::System::new_all();
CProcessMonitor { sys }
}
pub fn refresh(&mut self) {
self.sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
}
pub fn pids(&self) -> Vec<u32> {
let mut pids = Vec::<u32>::new();
for (pid, _process) in self.sys.processes() {
pids.push(pid.as_u32());
}
pids
}
pub fn cpu_usage(&self, pid: u32) -> f32 {
match self.sys.process(sysinfo::Pid::from_u32(pid)) {
Some(v) => {
v.cpu_usage() / runtime_cpu_count() as f32
},
None => panic!("SyntaxError: Unknown pid specified."),
}
}
pub fn cwd(&self, pid: u32) -> String {
match self.sys.process(sysinfo::Pid::from_u32(pid)) {
Some(v) => {
match v.cwd() {
Some(v) => {
match v.to_str() {
Some(v) => String::from(v),
None => String::from("UNDETERMINED"),
}
},
None => String::from("UNDETERMINED"),
}
},
None => panic!("SyntaxError: Unknown pid specified."),
}
}
pub fn disk_total_read_bytes(&self, pid: u32) -> u64 {
match self.sys.process(sysinfo::Pid::from_u32(pid)) {
Some(v) => v.disk_usage().total_read_bytes,
None => panic!("SyntaxError: Unknown pid specified."),
}
}
pub fn disk_total_written_bytes(&self, pid: u32) -> u64 {
match self.sys.process(sysinfo::Pid::from_u32(pid)) {
Some(v) => v.disk_usage().total_written_bytes,
None => panic!("SyntaxError: Unknown pid specified."),
}
}
pub fn exe(&self, pid: u32) -> String {
match self.sys.process(sysinfo::Pid::from_u32(pid)) {
Some(v) => {
match v.exe() {
Some(v) => {
match v.to_str() {
Some(v) => String::from(v),
None => String::from("UNDETERMINED"),
}
},
None => String::from("UNDETERMINED"),
}
},
None => panic!("SyntaxError: Unknown pid specified."),
}
}
pub fn group_id(&self, pid: u32) -> String {
match self.sys.process(sysinfo::Pid::from_u32(pid)) {
Some(v) => {
match v.group_id() {
Some(v) => v.to_string(),
None => String::from("UNDETERMINED"),
}
},
None => panic!("SyntaxError: Unknown pid specified."),
}
}
pub fn memory_usage_bytes(&self, pid: u32) -> u64 {
match self.sys.process(sysinfo::Pid::from_u32(pid)) {
Some(v) => v.memory(),
None => panic!("SyntaxError: Unknown pid specified."),
}
}
pub fn memory_virtual_bytes(&self, pid: u32) -> u64 {
match self.sys.process(sysinfo::Pid::from_u32(pid)) {
Some(v) => v.virtual_memory(),
None => panic!("SyntaxError: Unknown pid specified."),
}
}
pub fn name(&self, pid: u32) -> String {
match self.sys.process(sysinfo::Pid::from_u32(pid)) {
Some(v) => {
match v.name().to_str() {
Some(v) => String::from(v),
None => String::from("UNDETERMINED"),
}
},
None => panic!("SyntaxError: Unknown pid specified."),
}
}
pub fn open_files(&self, pid: u32) -> u32 {
match self.sys.process(sysinfo::Pid::from_u32(pid)) {
Some(v) => {
match v.open_files() {
Some(v) => v,
None => 0,
}
},
None => panic!("SyntaxError: Unknown pid specified."),
}
}
pub fn parent_pid(&self, pid: u32) -> u32 {
match self.sys.process(sysinfo::Pid::from_u32(pid)) {
Some(v) => {
match v.parent() {
Some(v) => v.as_u32(),
None => 0,
}
},
None => panic!("SyntaxError: Unknown pid specified."),
}
}
pub fn root(&self, pid: u32) -> String {
match self.sys.process(sysinfo::Pid::from_u32(pid)) {
Some(v) => {
match v.root() {
Some(v) => {
match v.to_str() {
Some(v) => String::from(v),
None => String::from("UNDETERMINED"),
}
},
None => String::from("UNDETERMINED"),
}
},
None => panic!("SyntaxError: Unknown pid specified."),
}
}
pub fn session_id(&self, pid: u32) -> u32 {
match self.sys.process(sysinfo::Pid::from_u32(pid)) {
Some(v) => {
match v.session_id() {
Some(v) => v.as_u32(),
None => 0,
}
},
None => panic!("SyntaxError: Unknown pid specified."),
}
}
pub fn status(&self, pid: u32) -> String {
match self.sys.process(sysinfo::Pid::from_u32(pid)) {
Some(v) => v.status().to_string(),
None => panic!("SyntaxError: Unknown pid specified."),
}
}
pub fn time_started_seconds(&self, pid: u32) -> u64 {
match self.sys.process(sysinfo::Pid::from_u32(pid)) {
Some(v) => v.start_time(),
None => panic!("SyntaxError: Unknown pid specified."),
}
}
pub fn time_running_seconds(&self, pid: u32) -> u64 {
match self.sys.process(sysinfo::Pid::from_u32(pid)) {
Some(v) => v.run_time(),
None => panic!("SyntaxError: Unknown pid specified."),
}
}
pub fn user_id(&self, pid: u32) -> String {
match self.sys.process(sysinfo::Pid::from_u32(pid)) {
Some(v) => {
match v.user_id() {
Some(v) => v.to_string(),
None => String::from("UNDETERMINED"),
}
},
None => panic!("SyntaxError: Unknown pid specified."),
}
}
pub fn kill(&self, pid: u32) -> bool {
match self.sys.process(sysinfo::Pid::from_u32(pid)) {
Some(v) => v.kill(),
None => panic!("SyntaxError: Unknown pid specified."),
}
}
pub fn wait(&self, pid: u32) -> i32 {
match self.sys.process(sysinfo::Pid::from_u32(pid)) {
Some(v) => {
match v.wait() {
Some(v) => {
match v.code() {
Some(v) => v,
None => -1 as i32,
}
},
None => -1 as i32,
}
},
None => panic!("SyntaxError: Unknown pid specified."),
}
}
}
impl CCsvFormat for CProcessMonitor {
fn csv_header(&self) -> String {
format!(
"{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}",
String::from("system_id"),
String::from("pid"),
String::from("cpu_usage"),
String::from("cwd"),
String::from("disk_total_read_bytes"),
String::from("disk_total_written_bytes"),
String::from("exe"),
String::from("group_id"),
String::from("memory_usage_bytes"),
String::from("memory_virtual_bytes"),
String::from("name"),
String::from("open_files"),
String::from("parent_pid"),
String::from("root"),
String::from("session_id"),
String::from("status"),
String::from("time_started_seconds"),
String::from("time_running_seconds"),
String::from("user_id"),
)
}
fn as_csv(&self) -> String {
let mut csv_data = String::new();
let pids = self.pids();
for pid in pids {
let data = format!(
"{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}",
Self::system_id(),
pid,
self.cpu_usage(pid),
self.cwd(pid),
self.disk_total_read_bytes(pid),
self.disk_total_written_bytes(pid),
self.exe(pid),
self.group_id(pid),
self.memory_usage_bytes(pid),
self.memory_virtual_bytes(pid),
self.name(pid),
self.open_files(pid),
self.parent_pid(pid),
self.root(pid),
self.session_id(pid),
self.status(pid),
self.time_started_seconds(pid),
self.time_running_seconds(pid),
self.user_id(pid),
);
csv_data.push_str(&data);
csv_data.push('\n');
}
csv_data
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_monitor.mmd")]
pub fn monitor_components() -> CComponentMonitor {
CComponentMonitor::new()
}
#[doc = simple_mermaid::mermaid!("models/codemelted_monitor.mmd")]
pub fn monitor_disk() -> CDiskMonitor {
CDiskMonitor::new()
}
#[doc = simple_mermaid::mermaid!("models/codemelted_monitor.mmd")]
pub fn monitor_network() -> CNetworkMonitor {
CNetworkMonitor::new()
}
#[doc = simple_mermaid::mermaid!("models/codemelted_monitor.mmd")]
pub fn monitor_performance() -> CPerformanceMonitor {
CPerformanceMonitor::new()
}
#[doc = simple_mermaid::mermaid!("models/codemelted_monitor.mmd")]
pub fn monitor_processes() -> CProcessMonitor {
CProcessMonitor::new()
}
pub enum CFetchAction {
Delete,
Get,
Post,
Put,
}
pub struct CFetchRequest {
client: reqwest::RequestBuilder
}
impl CFetchRequest {
pub fn new(action: CFetchAction, url: &str) -> CFetchRequest {
let client = match action {
CFetchAction::Delete => reqwest::Client::new().delete(url),
CFetchAction::Get => reqwest::Client::new().get(url),
CFetchAction::Post => reqwest::Client::new().post(url),
CFetchAction::Put => reqwest::Client::new().put(url),
};
CFetchRequest { client }
}
pub fn basic_auth(self, username: &str, password: Option<&str>) {
let _ = self.client.basic_auth(username, password);
}
pub fn bearer_auth(self, token: &str) {
let _ = self.client.bearer_auth(token);
}
pub fn body(self, data: CObject) {
let serialized = match json_as_string(&data) {
Some(v) => v,
None => panic!("CFetchRequest::body(): data was not valid CObject!"),
};
let _ = self.client.body(serialized);
}
pub fn form(self, params: std::collections::HashMap::<String, String>) {
let _ = self.client.form(¶ms);
}
pub fn header(self, key: &str, value: &str) {
let _ = self.client.header(key, value);
}
async fn send(self) -> CFetchResponse {
match self.client.send().await {
Ok(resp) => {
let status = resp.status().as_u16();
if status >= 200 || status <= 299 {
println!("We got a {}", status);
println!("Headers are {:?}", resp.headers());
let content_type = match resp.headers().get("Content-Type") {
Some(v) => {
match v.to_str() {
Ok(v) => v.to_string(),
Err(_) => String::from(""),
}
},
None => String::from(""),
};
if content_type.to_lowercase().contains("application/octet-stream") ||
content_type.to_lowercase().contains("image/") {
let data_as_bytes = match resp.bytes().await {
Ok(v) => Some(v.to_vec()),
Err(_) => None,
};
CFetchResponse::new(status, "OK", data_as_bytes, None, None)
} else if content_type.to_lowercase().contains("application/json") {
let data_as_json = match resp.text().await {
Ok(data_as_string) => {
match json_parse(&data_as_string) {
Some(v) => Some(v),
None => None,
}
}
Err(_) => None,
};
CFetchResponse::new(status, "OK", None, data_as_json, None)
} else {
let data_as_string = match resp.text().await {
Ok(v) => Some(v),
Err(_) => None,
};
CFetchResponse::new(status, "OK", None, None, data_as_string)
}
} else {
CFetchResponse::new(status, "UNDETERMINED", None, None, None)
}
},
Err(why) => {
let status = match why.status() {
Some(v) => v.as_u16(),
None => 418,
};
let status_text = why.without_url().to_string();
CFetchResponse::new(status, &status_text, None, None, None)
},
}
}
}
pub struct CFetchResponse {
status: u16,
status_text: String,
data_as_bytes: Option<Vec<u8>>,
data_as_json: Option<CObject>,
data_as_string: Option<String>,
}
impl CFetchResponse {
fn new(
status: u16,
status_text: &str,
data_as_bytes: Option<Vec<u8>>,
data_as_json: Option<CObject>,
data_as_string: Option<String>
) -> CFetchResponse {
CFetchResponse {
status,
status_text:
status_text.to_string(),
data_as_bytes,
data_as_json,
data_as_string,
}
}
pub fn status(&self) -> u16 {
self.status
}
pub fn status_text(&self) -> String {
self.status_text.to_string()
}
pub fn data_as_bytes(&self) -> Option<Vec<u8>> {
self.data_as_bytes.clone()
}
pub fn data_as_json(&self) -> Option<CObject> {
self.data_as_json.clone()
}
pub fn data_as_string(&self) -> Option<String> {
self.data_as_string.clone()
}
}
pub type CServerRequest = rouille::Request;
pub type CServerResponse = rouille::Response;
pub enum CWebSocketData {
NoData,
Bytes(Vec<u8>),
String(String),
}
impl CWebSocketData {
pub fn data_as_bytes(&self) -> Option<Vec<u8>> {
match self {
CWebSocketData::Bytes(items) => Some(items.to_owned()),
_ => None,
}
}
pub fn data_as_string(&self) -> Option<String> {
match self {
CWebSocketData::String(v) => Some(v.to_owned()),
_ => None,
}
}
}
pub struct CWebSocketProtocol {
id: String,
socket: Option<rouille::websocket::Websocket>,
}
impl CWebSocketProtocol {
fn new(
id: &str,
socket: rouille::websocket::Websocket
) -> CWebSocketProtocol {
CWebSocketProtocol {
id: id.to_string(),
socket: Some(socket),
}
}
}
impl CProtocolHandler<CWebSocketData> for CWebSocketProtocol {
fn id(&mut self) -> String {
self.id.to_string()
}
fn get_message(
&mut self,
_request: Option<&str>
) -> Result<CWebSocketData, std::io::Error> {
if self.socket.is_none() {
return Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"CWebSocketProtocol has been closed."
));
}
let socket = self.socket.as_mut().unwrap();
if socket.by_ref().peekable().peek().is_some() {
match socket.by_ref().next() {
Some(data) => {
match data {
rouille::websocket::Message::Text(v) =>
Ok(CWebSocketData::String(v.to_owned())
),
rouille::websocket::Message::Binary(
items
) => Ok(CWebSocketData::Bytes(
items.to_owned()
)),
}
},
None => Ok(CWebSocketData::NoData),
}
} else {
Ok(CWebSocketData::NoData)
}
}
fn is_running(&self) -> bool {
if self.socket.is_none() {
return false;
}
self.socket.as_ref().unwrap().is_closed()
}
fn post_message(
&mut self,
data: CWebSocketData
) -> Result<(), std::io::Error> {
if self.socket.is_none() {
return Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"CWebSocketProtocol has been closed."
));
}
let socket = self.socket.as_mut().unwrap();
let result = match data {
CWebSocketData::Bytes(data) => {
socket.send_binary(&data)
},
CWebSocketData::String(data) =>{
socket.send_text(&data)
},
_ => {
panic!(
"CWebSocketProtocol::post_message(): Invalid data received"
);
}
};
match result {
Ok(_) => Ok(()),
Err(_) => Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"Unable to transmit data"
)),
}
}
fn terminate(&mut self) {
self.socket = None;
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_network.mmd")]
pub fn network_fetch(request: CFetchRequest) -> CFetchResponse {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
request.send().await
})
}
#[doc = simple_mermaid::mermaid!("models/codemelted_network.mmd")]
pub fn network_serve<F>(bind_addr: &str, handler: F)
where
F: Send + Sync + 'static + Fn(&CServerRequest) -> CServerResponse,
{
rouille::start_server(bind_addr, move |request| {
handler(request)
});
}
#[doc = simple_mermaid::mermaid!("models/codemelted_network.mmd")]
pub fn network_upgrade_web_socket(
id: &str,
request: &CServerRequest,
subprotocol: Option<&'static str>
) -> Result<CWebSocketProtocol, rouille::websocket::WebsocketError> {
match rouille::websocket::start(request, subprotocol) {
Ok(v) => {
let socket = v.1.recv().unwrap();
Ok(CWebSocketProtocol::new(id, socket))
},
Err(why) => Err(why),
}
}
pub fn network_web_rtc() {
unimplemented!("FUTURE IMPLEMENTATION!");
}
pub enum CMathFormula {
GeodeticDistance,
GeodeticHeading,
GeodeticSpeed,
TemperatureCelsiusToFahrenheit,
TemperatureCelsiusToKelvin,
TemperatureFahrenheitToCelsius,
TemperatureFahrenheitToKelvin,
TemperatureKelvinToCelsius,
TemperatureKelvinToFahrenheit
}
impl CMathFormula {
const PI: f64 = std::f64::consts::PI;
fn fmod(a: f64, b: f64) -> f64 {
let mut answer: f64 = if a < 0.0 { -a } else { a };
let use_of_b: f64 = if b < 0.0 { -b } else { b };
while answer >= use_of_b {
answer -= b;
}
if a < 0.0 {
answer = -answer
}
answer
}
fn geodetic_distance(start_latitude: f64, start_longitude: f64,
end_latitude: f64, end_longitude: f64) -> f64 {
let lat1 = start_latitude * CMathFormula::PI / 180.0;
let lon1 = start_longitude * CMathFormula::PI / 180.0;
let lat2 = end_latitude * CMathFormula::PI / 180.0;
let lon2 = end_longitude * CMathFormula::PI / 180.0;
let r = 6378100.0;
let rho1 = r * lat1.cos();
let z1 = r * lat1.sin();
let x1 = rho1 * lon1.cos();
let y1 = rho1 * lon1.sin();
let rho2 = r * lat2.cos();
let z2 = r * lat2.sin();
let x2 = rho2 * lon2.cos();
let y2 = rho2 * lon2.sin();
let dot = x1 * x2 + y1 * y2 + z1 * z2;
let cos_theta = dot / (r * r);
let theta = cos_theta.acos();
r * theta
}
fn geodetic_heading(start_latitude: f64, start_longitude: f64,
end_latitude: f64, end_longitude: f64) -> f64 {
let lat1 = start_latitude * (CMathFormula::PI / 180.0);
let lon1 = start_longitude * (CMathFormula::PI / 180.0);
let lat2 = end_latitude * (CMathFormula::PI / 180.0);
let lon2 = end_longitude * (CMathFormula::PI / 180.0);
let y = (lon2 - lon1).sin() * lat2.cos();
let x = (lat1.cos() * lat2.sin()) -
(lat1.sin() * lat2.cos() * (lon2 - lon1).cos());
let rtnval = y.atan2(x) * (180.0 / CMathFormula::PI);
CMathFormula::fmod(rtnval + 360.0, 360.0)
}
fn geodetic_speed(start_milliseconds: f64, start_latitude: f64,
start_longitude: f64, end_milliseconds: f64, end_latitude: f64,
end_longitude: f64) -> f64 {
let dist_meters = CMathFormula::geodetic_distance(
start_latitude, start_longitude,
end_latitude, end_longitude
);
let time_s = (end_milliseconds - start_milliseconds) / 1000.0;
return dist_meters / time_s;
}
fn math(&self, args: &[f64]) -> f64 {
use std::panic;
let result = panic::catch_unwind(|| {
match self {
CMathFormula::GeodeticDistance => CMathFormula::geodetic_distance(
args[0],
args[1],
args[2],
args[3]
),
CMathFormula::GeodeticHeading => CMathFormula::geodetic_heading(
args[0],
args[1],
args[2],
args[3]
),
CMathFormula::GeodeticSpeed => CMathFormula::geodetic_speed(
args[0],
args[1],
args[2],
args[3],
args[4],
args[5]
),
CMathFormula::TemperatureCelsiusToFahrenheit =>
(args[0] * 9.0 / 5.0) + 32.0,
CMathFormula::TemperatureCelsiusToKelvin => args[0] + 273.15,
CMathFormula::TemperatureFahrenheitToCelsius =>
(args[0] - 32.0) * (5.0 / 9.0),
CMathFormula::TemperatureFahrenheitToKelvin =>
(args[0] - 32.0) * (5.0 / 9.0) + 273.15,
CMathFormula::TemperatureKelvinToCelsius => args[0] - 273.15,
CMathFormula::TemperatureKelvinToFahrenheit =>
(args[0] - 273.15) * (9.0 / 5.0) + 32.0,
}
});
Result::expect(
result,
"SyntaxError: args did not match the formula selected."
)
}
}
pub fn npu_compute() {
unimplemented!("FUTURE IMPLEMENTATION!");
}
#[doc = simple_mermaid::mermaid!("models/codemelted_npu.mmd")]
pub fn npu_math(formula: CMathFormula, args: &[f64]) -> f64 {
formula.math(args)
}
pub struct CProcessProtocol {
protocol_stderr_rx: std::sync::mpsc::Receiver<u8>,
protocol_stdout_rx: std::sync::mpsc::Receiver<u8>,
protocol_rx_thread: std::thread::JoinHandle<()>,
protocol_shutdown_tx: std::sync::mpsc::Sender<bool>,
process: std::process::Child,
protocol_stdin: std::process::ChildStdin,
}
impl CProcessProtocol {
fn new(command: &str, args: &str) -> CProcessProtocol {
let cmd = format!("{} {}", command, args);
let mut process = if cfg!(target_os = "windows") {
std::process::Command::new("cmd").args(["/c", &cmd])
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn().unwrap()
} else {
std::process::Command::new("sh").args(["-c", &cmd])
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn().unwrap()
};
let mut stdout = process.stdout.take().unwrap();
let mut stderr = process.stderr.take().unwrap();
let (
thread_stdout_tx,
protocol_stdout_rx
) = std::sync::mpsc::channel::<u8>();
let (
thread_stderr_tx,
protocol_stderr_rx
) = std::sync::mpsc::channel::<u8>();
let (
protocol_shutdown_tx,
thread_shutdown_rx
) = std::sync::mpsc::channel::<bool>();
let protocol_rx_thread = std::thread::spawn(move || {
let mut backoff_counter = 0;
let mut buf = [0x00];
loop {
match std::io::Read::read(&mut stdout, &mut buf) {
Ok(v) => {
if v != 0 {
backoff_counter = 0;
match thread_stdout_tx.send(buf[0]) {
Ok(_) => continue,
Err(_) => break, }
} else {
backoff_counter += 1;
}
},
Err(_) => continue,
}
match std::io::Read::read(&mut stderr, &mut buf) {
Ok(v) => {
if v != 0 {
backoff_counter = 0;
match thread_stderr_tx.send(buf[0]) {
Ok(_) => continue,
Err(_) => break, }
} else {
backoff_counter += 1;
}
},
Err(_) => continue,
}
match thread_shutdown_rx.try_recv() {
Ok(_) => break, Err(_) => {
if backoff_counter >= 10 {
async_sleep(1000);
}
continue
},
}
}
});
let protocol_stdin = process.stdin.take().unwrap();
CProcessProtocol {
protocol_stderr_rx,
protocol_stdout_rx,
protocol_rx_thread,
protocol_shutdown_tx,
process,
protocol_stdin,
}
}
}
impl CProtocolHandler<String> for CProcessProtocol {
fn id(&mut self) -> String {
self.process.id().to_string()
}
fn get_message(
&mut self,
request: Option<&str>
) -> Result<String, std::io::Error> {
let mut rx_buf = Vec::<u8>::new();
if request == Some("error") {
loop {
match self.protocol_stderr_rx.try_recv() {
Ok(v) => rx_buf.push(v),
Err(_) => break,
};
}
match String::from_utf8(rx_buf) {
Ok(v) => Ok(v),
Err(why) => Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
why.to_string()
)),
}
} else {
loop {
match self.protocol_stdout_rx.try_recv() {
Ok(v) => rx_buf.push(v),
Err(_) => break,
};
}
match String::from_utf8(rx_buf) {
Ok(v) => Ok(v),
Err(why) => Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
why.to_string()
)),
}
}
}
fn is_running(&self) -> bool {
!self.protocol_rx_thread.is_finished()
}
fn post_message(
&mut self,
data: String
) -> Result<(), std::io::Error> {
let result = std::io::Write::write_all(
&mut self.protocol_stdin,
data.as_bytes()
);
match result {
Ok(_) => {
match std::io::Write::flush(&mut self.protocol_stdin) {
Ok(_) => Ok(()),
Err(why) => Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
why.to_string()
)),
}
},
Err(why) => Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
why.to_string()
)),
}
}
fn terminate(&mut self) {
self.process.kill().expect("Process should have terminated!");
let _ = self.protocol_shutdown_tx.send(true);
loop {
if !self.is_running() {
break;
}
async_sleep(100);
}
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_process.mmd")]
pub fn process_exists(command: &str) -> bool {
let mut proc = if cfg!(target_os = "windows") {
std::process::Command::new("cmd")
.args(["/c", "where", command])
.spawn()
.expect("Expected process to execute.")
} else {
std::process::Command::new("sh")
.args(["-c", "which", command])
.spawn()
.expect("Expected process to execute.")
};
let rc = proc.wait().expect("Expected process to wait.");
rc.success()
}
#[doc = simple_mermaid::mermaid!("models/codemelted_process.mmd")]
pub fn process_run(command: &str, args: &str) -> String {
let cmd = format!("{} {}", command, args);
let proc = if cfg!(target_os = "windows") {
std::process::Command::new("cmd")
.args(["/c", &cmd])
.output()
.expect("Expected process to execute.")
} else {
std::process::Command::new("sh")
.args(["-c", &cmd])
.output()
.expect("Expected process to execute.")
};
String::from_utf8(proc.stdout).expect("Should vec<u8> to String")
}
#[doc = simple_mermaid::mermaid!("models/codemelted_process.mmd")]
pub fn process_spawn(command: &str, args: &str) -> CProcessProtocol {
CProcessProtocol::new(command, args)
}
#[doc = simple_mermaid::mermaid!("models/codemelted_runtime.mmd")]
pub fn runtime_cpu_arch() -> String {
sysinfo::System::cpu_arch()
}
#[doc = simple_mermaid::mermaid!("models/codemelted_runtime.mmd")]
pub fn runtime_cpu_count() -> usize {
match sysinfo::System::physical_core_count() {
Some(v) => v,
None => 1,
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_runtime.mmd")]
pub fn runtime_environment(key: &str) -> Option<String> {
match std::env::var(key) {
Ok(val) => Some(val),
Err(_) => None,
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_runtime.mmd")]
pub fn runtime_home_path() -> String {
let home_path = if cfg!(target_os = "windows") {
runtime_environment("USERPROFILE")
} else {
runtime_environment("HOME")
};
match home_path {
Some(v) => v,
None => {
panic!("SyntaxError: disk_home_path() unable to query.")
},
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_runtime.mmd")]
pub fn runtime_hostname() -> String {
match sysinfo::System::host_name() {
Some(v) => v,
None => String::from("UNDETERMINED"),
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_runtime.mmd")]
pub fn runtime_kernel_version() -> String {
match sysinfo::System::kernel_version() {
Some(v) => v,
None => String::from("UNDETERMINED"),
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_runtime.mmd")]
pub fn runtime_newline() -> String {
if cfg!(target_os = "windows") {
String::from("\r\n")
} else {
String::from("\n")
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_runtime.mmd")]
pub fn runtime_online(timeout: Option<u64>) -> bool {
online::check(timeout).is_ok()
}
#[doc = simple_mermaid::mermaid!("models/codemelted_runtime.mmd")]
pub fn runtime_os_name() -> String {
match sysinfo::System::name() {
Some(v) => v,
None => String::from("UNDETERMINED"),
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_runtime.mmd")]
pub fn runtime_os_version() -> String {
match sysinfo::System::os_version() {
Some(v) => v,
None => String::from("UNDETERMINED"),
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_runtime.mmd")]
pub fn runtime_path_separator() -> String {
if cfg!(target_os = "windows") {
String::from("\\")
} else {
String::from("/")
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_runtime.mmd")]
pub fn runtime_temp_path() -> String {
match std::env::temp_dir().to_str() {
Some(v) => String::from(v),
None => {
panic!("SyntaxError: disk_temp_path() unable to query.")
}
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_runtime.mmd")]
pub fn runtime_user() -> String {
let user = if cfg!(target_os = "windows") {
runtime_environment("USERNAME")
} else {
runtime_environment("USER")
};
match user {
Some(v) => v,
None => String::from("UNDETERMINED"),
}
}
static STORAGE: std::sync::Mutex<Option<CObject>> = std::sync::Mutex::new(
None
);
#[doc = simple_mermaid::mermaid!("models/codemelted_storage.mmd")]
fn storage_save_file(data: &str) {
let home_path = runtime_home_path().to_string();
let filename = format!("{}/{}",
home_path,
".codemelted_storage"
);
if disk_exists(&filename, CDiskType::File) {
let _ = disk_rm(&filename);
}
let result = disk_write_file(
&filename,
CFileContents::String(data.to_owned()),
false
);
if result.is_err() {
panic!("SyntaxError: storage_save_file: {}", result.err().unwrap());
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_storage.mmd")]
pub fn storage_init() {
let mut storage_mutex = STORAGE.lock().unwrap();
if storage_mutex.is_none() {
let home_path = runtime_home_path();
let filename = format!("{}/{}",
home_path,
".codemelted_storage"
);
let data = disk_read_file(&filename, true);
let storage_obj = match data {
Ok(v) => {
let contents = v.as_string().unwrap();
if contents.len() == 0 {
CObject::new_object()
} else {
match json_parse(&contents) {
Some(v) => v,
None => {
panic!("SyntaxError: .codemelted_storage file is corrupt.")
},
}
}
},
Err(_) => CObject::new_object(),
};
*storage_mutex = Some(storage_obj);
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_storage.mmd")]
pub fn storage_clear() {
let mut storage_mutex = STORAGE.lock().unwrap();
if let Some(storage_obj) = std::ops::DerefMut::deref_mut(
&mut storage_mutex
).as_mut() {
storage_obj.clear();
storage_save_file("");
} else {
panic!("SyntaxError: storage_init() not yet called!");
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_storage.mmd")]
pub fn storage_get(key: &str) -> Option<String> {
let mut storage_mutex = STORAGE.lock().unwrap();
if let Some(storage_obj) = std::ops::DerefMut::deref_mut(
&mut storage_mutex
).as_mut() {
match storage_obj.has_key(key) {
true => {
Some(storage_obj[key].to_string())
},
false => None
}
} else {
panic!("SyntaxError: storage_init() not yet called!");
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_storage.mmd")]
pub fn storage_length() -> usize {
let mut storage_mutex = STORAGE.lock().unwrap();
if let Some(storage_obj) = std::ops::DerefMut::deref_mut(
&mut storage_mutex
).as_mut() {
storage_obj.len()
} else {
panic!("SyntaxError: storage_init() not yet called!");
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_storage.mmd")]
pub fn storage_remove(key: &str) {
let mut storage_mutex = STORAGE.lock().unwrap();
if let Some(storage_obj) = std::ops::DerefMut::deref_mut(
&mut storage_mutex
).as_mut() {
storage_obj.remove(key);
let data = storage_obj.dump();
storage_save_file(&data);
} else {
panic!("SyntaxError: storage_init() not yet called!");
}
}
#[doc = simple_mermaid::mermaid!("models/codemelted_storage.mmd")]
pub fn storage_set(key: &str, value: &str) {
let mut storage_mutex = STORAGE.lock().unwrap();
if let Some(storage_obj) = std::ops::DerefMut::deref_mut(
&mut storage_mutex
).as_mut() {
let _ = storage_obj.insert(key, value);
let data = storage_obj.dump();
storage_save_file(&data);
} else {
panic!("SyntaxError: storage_init() not yet called!");
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_is_nan() {
assert_eq!(true, f64::is_nan((-1.0 as f64).sqrt()));
}
}