use std::cmp;
use std::path::PathBuf;
use std::sync::Mutex;
use cidr::Ipv4Cidr;
use color_eyre::eyre::Result;
use directories::ProjectDirs;
use human_panic::metadata;
use lazy_static::lazy_static;
use std::net::Ipv4Addr;
use tracing::error;
use tracing_error::ErrorLayer;
use tracing_subscriber::{Layer, layer::SubscriberExt, util::SubscriberInitExt};
use crate::components::sniff::IPTraffic;
pub static GIT_COMMIT_HASH: &'static str = env!("_GIT_INFO");
lazy_static! {
pub static ref PROJECT_NAME: String = env!("CARGO_CRATE_NAME").to_uppercase().to_string();
pub static ref DATA_FOLDER: Option<PathBuf> =
std::env::var(format!("{}_DATA", PROJECT_NAME.clone()))
.ok()
.map(PathBuf::from);
pub static ref CONFIG_FOLDER: Option<PathBuf> =
std::env::var(format!("{}_CONFIG", PROJECT_NAME.clone()))
.ok()
.map(PathBuf::from);
pub static ref LOG_ENV: String = format!("{}_LOGLEVEL", PROJECT_NAME.clone());
pub static ref LOG_FILE: String = format!("{}.log", env!("CARGO_PKG_NAME"));
}
fn project_directory() -> Option<ProjectDirs> {
ProjectDirs::from("com", "kdheepak", env!("CARGO_PKG_NAME"))
}
pub fn get_ips4_from_cidr(cidr: Ipv4Cidr) -> Vec<Ipv4Addr> {
let mut ips = Vec::new();
for ip in cidr.iter() {
ips.push(ip.address());
}
ips
}
pub fn count_ipv4_net_length(net_length: u32) -> u32 {
2u32.pow(32 - net_length)
}
pub fn count_traffic_total(traffic: &[IPTraffic]) -> (f64, f64) {
let mut download = 0.0;
let mut upload = 0.0;
for ip in traffic.iter() {
download += ip.download;
upload += ip.upload;
}
(download, upload)
}
#[derive(Clone, Debug)]
pub struct MaxSizeVec<T> {
p_vec: Vec<T>,
max_len: usize,
}
impl<T> MaxSizeVec<T> {
pub fn new(max_len: usize) -> Self {
Self {
p_vec: Vec::with_capacity(max_len),
max_len,
}
}
pub fn push(&mut self, item: T) {
if self.p_vec.len() >= self.max_len {
self.p_vec.pop();
}
self.p_vec.insert(0, item);
}
pub fn get_vec(&self) -> &Vec<T> {
&self.p_vec
}
}
pub fn bytes_convert(num: f64) -> String {
let num = num.abs();
let units = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
if num < 1_f64 {
return format!("{}{}", num, "B");
}
let delimiter = 1000_f64;
let exponent = cmp::min(
(num.ln() / delimiter.ln()).floor() as i32,
(units.len() - 1) as i32,
);
let pretty_bytes = format!("{:.2}", num / delimiter.powi(exponent))
.parse::<f64>()
.unwrap()
* 1_f64;
let unit = units[exponent as usize];
format!("{}{}", pretty_bytes, unit)
}
pub fn initialize_panic_handler() -> Result<()> {
let (panic_hook, eyre_hook) = color_eyre::config::HookBuilder::default()
.panic_section(format!(
"This is a bug. Consider reporting it at {}",
env!("CARGO_PKG_REPOSITORY")
))
.capture_span_trace_by_default(false)
.display_location_section(false)
.display_env_section(false)
.into_hooks();
eyre_hook.install()?;
std::panic::set_hook(Box::new(move |panic_info| {
if let Ok(mut t) = crate::tui::Tui::new() {
if let Err(r) = t.exit() {
error!("Unable to exit Terminal: {:?}", r);
}
}
#[cfg(not(debug_assertions))]
{
use human_panic::{Metadata, handle_dump, print_msg};
let meta = metadata!()
.authors("Chleba <chlebik@gmail.com>")
.homepage("https://github.com/Chleba/netscanner")
.support("https://github.com/Chleba/netscanner/issues");
let file_path = handle_dump(&meta, panic_info);
print_msg(file_path, &meta)
.expect("human-panic: printing error message to console failed");
eprintln!("{}", panic_hook.panic_report(panic_info)); }
let msg = format!("{}", panic_hook.panic_report(panic_info));
log::error!("Error: {}", strip_ansi_escapes::strip_str(msg));
#[cfg(debug_assertions)]
{
better_panic::Settings::auto()
.most_recent_first(false)
.lineno_suffix(true)
.verbosity(better_panic::Verbosity::Full)
.create_panic_handler()(panic_info);
}
std::process::exit(libc::EXIT_FAILURE);
}));
Ok(())
}
pub fn get_data_dir() -> PathBuf {
let directory = if let Some(s) = DATA_FOLDER.clone() {
s
} else if let Some(proj_dirs) = project_directory() {
proj_dirs.data_local_dir().to_path_buf()
} else {
PathBuf::from(".").join(".data")
};
directory
}
pub fn get_config_dir() -> PathBuf {
let directory = if let Some(s) = CONFIG_FOLDER.clone() {
s
} else if let Some(proj_dirs) = project_directory() {
proj_dirs.config_local_dir().to_path_buf()
} else {
PathBuf::from(".").join(".config")
};
directory
}
pub fn initialize_logging() -> Result<()> {
let directory = get_data_dir();
std::fs::create_dir_all(&directory)?;
let log_path = directory.join(&*LOG_FILE);
let log_file = std::fs::File::create(log_path)?;
let env_filter_str = std::env::var("RUST_LOG")
.or_else(|_| std::env::var(&*LOG_ENV))
.unwrap_or_else(|_| format!("{}=info", env!("CARGO_CRATE_NAME")));
let env_filter = tracing_subscriber::filter::EnvFilter::new(env_filter_str);
let log_file = Mutex::new(log_file);
let file_subscriber = tracing_subscriber::fmt::layer()
.with_file(true)
.with_line_number(true)
.with_writer(log_file) .with_target(false)
.with_ansi(false)
.with_filter(env_filter);
let _ = tracing_subscriber::registry()
.with(file_subscriber)
.with(ErrorLayer::default())
.try_init();
Ok(())
}
#[macro_export]
macro_rules! trace_dbg {
(target: $target:expr, level: $level:expr, $ex:expr) => {{
match $ex {
value => {
tracing::event!(target: $target, $level, ?value, stringify!($ex));
value
}
}
}};
(level: $level:expr, $ex:expr) => {
trace_dbg!(target: module_path!(), level: $level, $ex)
};
(target: $target:expr, $ex:expr) => {
trace_dbg!(target: $target, level: tracing::Level::DEBUG, $ex)
};
($ex:expr) => {
trace_dbg!(level: tracing::Level::DEBUG, $ex)
};
}
pub fn version() -> String {
let author = clap::crate_authors!();
let commit_hash = GIT_COMMIT_HASH;
let config_dir_path = get_config_dir().display().to_string();
let data_dir_path = get_data_dir().display().to_string();
format!(
"\
{commit_hash}
Authors: {author}
Config directory: {config_dir_path}
Data directory: {data_dir_path}"
)
}