#![warn(missing_docs)]
#![forbid(unsafe_code)]
use crossbeam_channel::Sender;
use log::LevelFilter;
use std::env;
use std::fmt::Display;
use std::path::Path;
use std::sync::Once;
#[macro_export]
macro_rules! watch {
( $( $x:expr ),* ) => {
{
$(
::log::trace!("{}: {}", stringify!($x), format!("{:#?}", $x).replace("\\n", "\n"));
)*
}
};
}
#[macro_export]
macro_rules! error {
($fmt:literal $(, $x:expr )* ) => {
{
::log::error!($fmt $(,$x)*);
}
};
( $channel:expr, $fmt:literal $(, $x:expr )* ) => {
{
(&$channel).send(Some(::xvc_logging::XvcOutputLine::Error(format!($fmt $(, $x)*)))).unwrap();
}
};
}
#[macro_export]
macro_rules! info {
($fmt:literal $(, $x:expr )* ) => {
{
::log::info!($fmt $(, $x)*);
}
};
( $channel:expr, $fmt:literal $(, $x:expr )* ) => {
{
(&$channel).send(Some(::xvc_logging::XvcOutputLine::Info(format!($fmt $(,$x)*)))).unwrap();
}
};
}
#[macro_export]
macro_rules! warn {
($fmt:literal $(, $x:expr )* ) => {
{
::log::warn!($fmt $(, $x)*);
}
};
( $channel:expr, $fmt:literal $(, $x:expr )* ) => {
{
(&$channel).send(
Some(
::xvc_logging::XvcOutputLine::Warn(
format!($fmt $(, $x)*)))).unwrap();
}
};
}
#[macro_export]
macro_rules! debug {
($fmt:literal $(, $x:expr )* ) => {
{
::log::debug!($fmt $(, $x)*);
}
};
( $channel:expr, $fmt:literal $(, $x:expr )* ) => {
{
(&$channel).send(Some(::xvc_logging::XvcOutputLine::Debug(format!($fmt $(, $x)*)))).unwrap();
}
};
}
#[macro_export]
macro_rules! trace {
($fmt:literal $(, $x:expr )* ) => {
{
::log::trace!($fmt $(, $x)*);
}
};
( $channel:expr, $fmt:literal $(, $x:expr ),* ) => {
{
(&$channel).send(Some(::xvc_logging::XvcOutputLine::Trace(format!("{} [{}::{}]", format!($fmt $(, $x)*), file!(), line!())))).unwrap();
}
};
}
#[macro_export]
macro_rules! output {
($fmt:literal $(, $x:expr )* ) => {
{
::std::println!($fmt $(, $x)*);
}
};
( $channel:expr, $fmt:literal $(, $x:expr )* ) => {
{
(&$channel).send(Some(::xvc_logging::XvcOutputLine::Output(format!($fmt $(, $x)*)))).unwrap();
}
};
}
#[macro_export]
macro_rules! panic {
($fmt:literal $(, $x:expr )* ) => {
{
watch!($fmt $(, $x)*);
::std::panic!($fmt $(, $x)*);
}
};
( $channel:expr, $fmt:literal $(, $x:expr )* ) => {
{
(&$channel).send(Some(::xvc_logging::XvcOutputLine::Panic(format!("{} [{}::{}]",
format!($fmt $(, $x)*), file!(), line!())))).unwrap();
::std::panic!($fmt $(, $x)*);
}
};
}
#[macro_export]
macro_rules! tick {
( $channel:ident, $n:literal) => {{
(&$channel)
.send(Some(::xvc_logging::XvcOutputLine::Tick($n)))
.unwrap();
}};
($n:literal) => {{
for _ in 0..$n {
::std::print!(".");
}
}};
}
#[macro_export]
macro_rules! uwr {
( $e:expr, $channel:expr ) => {{
match $e {
Ok(v) => v,
Err(e) => {
(&$channel)
.send(Some(::xvc_logging::XvcOutputLine::Panic(format!(
"{:?}, [{}::{}]",
e,
file!(),
line!()
))))
.unwrap();
::std::panic!("{:?}", e);
}
}
}};
}
#[macro_export]
macro_rules! uwo {
( $e:expr, $channel:expr ) => {{
match $e {
Some(v) => v,
None => {
watch!($e);
let msg = format!(
"None from the expression: {} [{}::{}]",
stringify!($e),
file!(),
line!()
);
(&$channel)
.send(Some(::xvc_logging::XvcOutputLine::Panic(msg.clone())))
.unwrap();
::std::panic!("{}", msg);
}
}
}};
}
static INIT: Once = Once::new();
pub fn setup_logging(term_level: Option<LevelFilter>, file_level: Option<LevelFilter>) {
INIT.call_once(|| init_logging(term_level, file_level));
}
fn init_logging(term_level: Option<LevelFilter>, file_level: Option<LevelFilter>) {
let logfilename = &format!("{}/xvc.log", env::temp_dir().to_string_lossy(),);
let logfile = Path::new(&logfilename);
let mut dispatch = fern::Dispatch::new().format(|out, message, record| {
out.finish(format_args!(
"[{}][{}::{}] {}",
record.level(),
record.file().get_or_insert("None"),
record.line().get_or_insert(0),
message
))
});
if let Some(level) = term_level {
dispatch = dispatch.level(level).chain(std::io::stderr());
}
if let Some(level) = file_level {
dispatch = dispatch
.level(level)
.chain(fern::log_file(logfilename).expect("Cannot set log filename"));
}
match dispatch.apply() {
Ok(_) => {
if let Some(level) = term_level {
debug!("Terminal logger enabled with level: {:?}", level);
};
if let Some(level) = file_level {
debug!(
"File logger enabled with level: {:?} to {:?}",
level, logfile
);
};
}
Err(err) => {
error!("Error enabling logger: {:?}", err);
}
};
}
#[derive(Clone, Debug)]
pub enum XvcOutputLine {
Output(String),
Info(String),
Debug(String),
Warn(String),
Error(String),
Panic(String),
Tick(usize),
}
pub type XvcOutputSender = Sender<Option<XvcOutputLine>>;
impl XvcOutputLine {
pub fn info(s: &str) -> Self {
Self::Info(s.to_string())
}
pub fn debug(s: &str) -> Self {
Self::Debug(s.to_string())
}
pub fn warn(s: &str) -> Self {
Self::Warn(s.to_string())
}
pub fn error(s: &str) -> Self {
Self::Error(s.to_string())
}
pub fn panic(s: &str) -> Self {
Self::Panic(s.to_string())
}
pub fn tick(n: usize) -> Self {
Self::Tick(n)
}
}
impl From<&str> for XvcOutputLine {
fn from(s: &str) -> Self {
Self::Output(s.to_string())
}
}
impl From<String> for XvcOutputLine {
fn from(s: String) -> Self {
Self::Output(s)
}
}
impl Display for XvcOutputLine {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self {
XvcOutputLine::Output(s) => writeln!(f, "{}", s),
XvcOutputLine::Info(s) => writeln!(f, "[INFO] {}", s),
XvcOutputLine::Debug(s) => writeln!(f, "[DEBUG] {}", s),
XvcOutputLine::Warn(s) => writeln!(f, "[WARN] {}", s),
XvcOutputLine::Error(s) => writeln!(f, "[ERROR] {}", s),
XvcOutputLine::Panic(s) => writeln!(f, "[PANIC] {}", s),
XvcOutputLine::Tick(n) => write!(f, "{}", ".".repeat(*n)),
}
}
}